• Aucun résultat trouvé

1.2 Systèmes logiciels complexes

1.2.5 Le problème posé par les systèmes multi-couches

Nous venons de présenter très rapidement certains des aspects architecturaux utili- sés pour assurer découplage et interopérabilité dans les logiciels d’aujourd’hui : systèmes d’exploitation, paradigme orienté objet, intergiciels. Cette présentation, inévitablement in- complète et parcellaire, fait cependant ressortir l’importance de la structuration en couches des systèmes abordés. Chaque couche, en masquant (partiellement ou complètement) la couche précédente par de nouvelles abstractions, introduit une sorte de pare-feu sémantique qui assure un découplage vertical des différents éléments du système et permet de rendre le service réalisé (perçu par l’utilisateur de la couche en question) indépendant du substrat matériel et logiciel qui le supporte.

Cette forme de structuration présente de nombreux avantages. Elle permet par exemple de construire et de valider les différents éléments d’un système de manière modulaire. En se basant — parfois implicitement — sur un système de conditions / garanties, les fournisseurs de chaque couche peuvent se concentrer sur le niveau dont ils ont la responsabilité (compila- teur, OS, bibliothèque), en assurant que celui-ci se comporte correctement sous l’hypothèse que les niveaux sous-jacents sur lesquels il s’appuie sont eux aussi corrects [Meyer 1997]. Cette structuration permet aussi de développer des environnements de développement plus intuitifs, plus riches, plus faciles à mettre en œuvre et à déployer. En se basant sur des interfaces standardisées (IA32, POSIX, CORBA, Java, RMI, ...), elle rend les intégrateurs de système indépendants de leurs fournisseurs, assure la pérennité des développements en permettant l’évolution séparée des implémentations sous-jacentes.

Du point de vue de la tolérance aux fautes, cependant, cette structuration stratifiée induit un certain nombre de problèmes que nous allons tenter d’esquisser ici. Comme nous l’avons mentionné, l’abstraction fournie par chaque nouvelle couche s’opère au prix d’une opacifica- tion de l’implémentation sous-jacente. Or l’introduction d’une couche augmente les risques de fautes logicielles (fautes de conception à l’intérieur de la couche, fautes d’utilisation de la couche, etc.) tout en restreignant les capacités de contrôle et d’observation par ses utilisateurs. Les erreurs créées par les fautes traversent hélas les frontières d’abstraction mises en place par chaque couche, si bien qu’un développeur qui souhaite implémenter des mécanismes de tolérance aux fautes au-dessus d’une architecture complexe doit lutter en aveugle contre des dangers qui lui sont rendus « transparents », avec des moyens limités.

La tolérance aux fautes a bien sûr en partie été prise en compte dans les différents paradigmes que nous avons évoqués : confinement des espaces d’adressage virtuel au ni- veau micro–processeur, codes de retour d’erreur pour les appels système, exceptions dans les langages orientés objet, et dans les intergiciels. Ces moyens sont cependant souvent rudimentaires, et ne peuvent pas être modifiés par les développeurs « clients » d’une solution. La réalisation de la tolérance aux fautes dans des systèmes complexes en couches ne peut

souvent pas être abordée d’un point de vue uniquement parcellaire, en ne considérant qu’une seule couche indépendamment des autres éléments avec lesquels elle interagit. La tolérance aux fautes est un concept orthogonal aux couches d’un système, et donc impose une connaissance, qui peut être plus ou moins étendue, de ce qui s’y déroule. Pour réaliser la détection d’erreur, des informations doivent parfois être collectées à différents niveaux de l’architecture. Pour réaliser une capture d’état de manière « intelligente », des informa- tions de niveaux d’abstraction différents sont souvent nécessaires pour une implémentation portable et efficace.

À ceci s’ajoute le souci, général pour les systèmes informatiques complexes, de décou- plage et de composabilité : les mécanismes de tolérance aux fautes doivent pouvoir être développés séparément du reste du système. Plusieurs raisons motivent cette préoccupa- tion :

– l’imbrication dans le même code de préoccupations directement fonctionnelles et

d’aspects transversaux comme la tolérance aux fautes rend malaisé le développement des programmes, en superposant deux logiques distinctes et souvent orthogonales ;

– la tolérance aux fautes, parce qu’elle fait intervenir plusieurs niveaux d’abstraction,

introduit des dépendances supplémentaires entre les couches, et affaiblit le principe de découplage vertical ;

– le développement de mécanismes de tolérance aux fautes requiert souvent un savoir

d’expert, indépendant des connaissances métier nécessaires au développement des aspects applicatifs du système.

Pour faire face aux changements, la tolérance aux fautes doit pouvoir être modifiée aisément, parfois même à l’exécution, sans arrêt du système. Des algorithmes développés indépendamment du reste du système, sous la forme de composants sur étagère, doivent pouvoir y être connectés facilement, de manière orthogonale, sans impacter les parties applicatives du système.

L’on peut donc résumer la problématique de la tolérance aux fautes dans les architectures complexes par les deux points suivants :

– Comment construire la tolérance aux fautes dans les différentes couches du système,

en préservant la structuration en strates, tout en se libérant de l’opacité des interfaces entre couches ?

– Comment assurer la modularité, l’adaptabilité de la tolérance aux fautes, sa séparation

des aspects directement fonctionnels du système ?

Ce constat induit finalement une nécessité d’inventer un nouveau modèle à composants dont l’encapsulation n’est pas remise en cause pour leur utilisation fonctionnelle, mais qui offrent de nouvelles propriétés pour la mise en œuvre de mécanismes non-fonctionnels. Cette affirmation est le fond de la thèse défendue dans ce mémoire.