Lorsque des changements sont faits sur la base, tels que l'ajout de champs à une table, il est nécessaire de le faire prudemment, pour éviter que certains nœuds ne soient bloqués lors de la création de certaines tables.
Si vous opérez les changements à travers Slony-I™ via SLONIK EXECUTE SCRIPT(7) (slonik) / schemadocddlscript_complete( integer, text, integer ) (procédure stockée), cela vous garantie que les changements prendront effet au même niveau dans les flux de transactions sur tous les nœuds. Cela n'a pas la même importance si vous pouvez arrêter votre système pour appliquer vos modifications de schéma mais si vous voulez faire vos mises à jour alors que vos transactions continuent leur vie, cela devient nécessaire.
Il est essentiel d'utiliser EXECUTE SCRIPT si vous modifiez des tables pour changer leurs structures. Si vous ne le faites pas, vous pouvez rencontrer des problèmes identiques à ceux décrits ici où les triggers des tables modifiées ne tiennent pas compte des changements de schéma réalisés. Cela peut conduire à une corruption des données sur le nœud abonné.
Il est utile de faire quelques « recommandations » sur SLONIK EXECUTE SCRIPT(7) :
Le script ne doit pas contenir d'instructions BEGIN ou END car il s'exécute déjà dans une transaction. Avec la version 8 de PostgreSQL™, l'introduction des transactions imbriquées change cela quelque peu, mais vous devez rester attentif au fait que les actions du script sont exécutées comme une transaction simple dans laquelle on ne maîtrise pas le BEGIN et le END.
S'il y a une erreur quelconque dans le script ou un problème quelconque sur la manière dont il s'exécute sur un nœud particulier, cela conduira le démon slon(1) de ce nœud à paniquer et à tomber. Vous pouvez consulter plusieurs types de messages attendus lors d'un déroulement normal ou anormal à Section 26.7.2, « Traces à propose des scripts DDL ». Si vous redémarrez le démon slon(1), il tentera, de manière très probable, de rejouer le script DDL, ce qui conduira certainement au même échec qu'à sa première tentative. J'ai eu un tel scénario, m'obligeant à supprimer du nœud « maître » l'évènement de la table sl_event pour arrêter de le voir tomber en échec.
La conséquence est qu'il est vital que les modifications ne soient pas faites de manière hasardeuse sur un nœud ou un autre. Les schémas doivent toujours rester synchronisés.
Pour slon(1) aussi, à ce niveau, « panic » est probablement la réponse correcte car il permet au DBA de sortir la base du nœud défecteux, de corriger manuellement le problème en supprimant l'évènement incorrect et de redémarrer slon(1). Vous pouvez être certain que les mises à jour faites après le changement DDL sur le nœud maître sont enregistrées dans une file d'attente, avant d'être traitées par l'abonné. Vous ne courrez ainsi pas le risque qu'il y ait des mises à jour demandées alors qu'elles dépendaient des modifications DDL.
Lorsque vous exécutez SLONIK EXECUTE SCRIPT(7), cela conduit slonik(1) à poser, pour chaque table faisant partie du jeu de réplication, un verrou exclusif sur la table.
Cela commence par la pose du verrou, poursuit avec l'altération de la table pour supprimer les triggers Slony-I™, et se termine avec la restauration de tous les triggers qui ont été cachés.
BEGIN; LOCK TABLE table_name; SELECT _oxrsorg.altertablerestore(tab_id); --tab_id is _slony_schema.sl_table.tab_id
Après l'exécution du script, chaque table est « restaurée » dans le but d'ajouter en fin soit le trigger qui collecte les mises à jour sur le nœud origine soit celui qui rejette les mises à jour sur l'abonné.
SELECT _oxrsorg.altertableforreplication(tab_id); --tab_id is _slony_schema.sl_table.tab_id COMMIT;
Notez que c'est ce qui permet à Slony-I™ de prendre connaissance de l'altération de tables : avant ce SYNC, Slony-I™ réplique des lignes suivant le vieux schéma ; après le script DDL_SCRIPT, les lignes sont répliquées suivant le nouveau schéma.
Sur un système réalisant de nombreuses mises à jour, il peut être difficile « d'obtenir » tous les verrous nécessaires. Les verrous peuvent conduire à des situations de blocages. Vous pouvez régler le problème de deux manières différentes :
Vous devez envisager de définir des ensembles de réplication représentant un plus petit nombre de tables de manière à ce que moins de verrous soient posés et permettent au script DDL d'être joué.
Si un script DDL affecte une seule table, il ne devrait pas être nécessaire de verrouiller toutes les tables de l'application.
Véritablement, à partir de la version 1.1.5 ou plus, ce n'est plus vrai. Le danger que quelqu'un opère des changements DDL interférant avec l'ensemble de réplication est suffisamment évident pour que slon(1) verrouille toutes les tables répliquées, qu'elles soient ou pas dans l'ensemble de réplication.
Vous pouvez avoir besoin de faire un arrêt de votre système pour vous assurer que vos applications ne demandent pas des verrous qui rentreraient en conflit avec ceux dont vous auriez besoin pour faire les changements de schéma de base.
Dans les versions 1.0 à 1.1.5 de Slony-I™, le script s'exécute comme une simple requête, ce qui peut poser problème si vous réalisez des modifications complexes. À partir de la version 1.2, le script est découpé en traitements SQL individuels, et chaque requête est soumise séparemment, ce qui est préférable.
Le problème avec une requête traitant une « requête imbriquée » est que l'analyseur SQL réalise son plan d'exécution pour le jeu entier de requête avant même l'exécution de la requête.
Cela ne pose pas de problème particulier si les requêtes sont indépendantes les unes des autres, telles que deux requêtes d'ajout de deux colonnes à une table.
ALTER TABLE t1 ADD COLUMN c1 integer; ALTER TABLE t1 ADD COLUMN c2 integer;
Par contre, les problèmes se rencontrent si vous devez référencer une requête précédente. Considérez la requête DDL suivante...
ALTER TABLE t1 ADD COLUMN c1 integer; CREATE SEQUENCE s1; UPDATE t1 SET c1=nextval('s1'); ALTER TABLE t1 ALTER COLUMN c1 SET NOT NULL; ALTER TABLE t1 ADD PRIMARY KEY(c1);
Jusqu'à la version 1.2 de Slony-I™, cette requête aurait échouée lors de l'atteinte de la condition UPDATE, indiquant l'inexistence de la colonne c1. Cela est dû au fait que toutes ces requêtes sont découpées et analysées avant même l'exécution de l'une d'entre elles. Ainsi, l'UPDATE est évalué sur une table avant que la colonne n'ait été ajoutée. Oops.
Si vous avez des versions antérieures, vous pouvez contourner cela en invoquant une requête séparée SLONIK EXECUTE SCRIPT(7) dans un script distinct, en générant un nouveau script pour chaque requête qui référence un objet créé dans une requête précédente.
Dans la version 1.2 de Slony-I™, il y a un mécanisme qui sépare le script DDL en requêtes individuelles. Chaque requête est soumise par une requête PQexec() séparée, ce qui ne pose plus de problème.
Malheureusement, cela conduit à une vulnérabilité du script DDL, suivant la manière dont les changements DDL sont écrits. Si vos applications n'ont pas de schémas SQL suffisamment stables, l'utilisation de Slony-I™ pour le mécanisme de réplication peut ne pas être adaptée. Voir la section sur les questions de verrous pour plus de discussion sur le sujet.
Voici un article sur l'administration des changements de schémas avec Slony-I™ : Varlena General Bits.
Alors qu'il est vital d'utiliser EXECUTE SCRIPT pour propager des modifications DDL sur des tables répliquées, il y a plusieurs autres modifications que vous pouvez vouloir réaliser d'une autre façon :
Il y a plusieurs types d'objets n'ayant pas de trigger que Slony-I™ ne peut pas répliquer, tels que les procédures stockées. Et il est assez probable que cela vous posera problème de propager ces mises à jour en association avec un ensemble de réplication où EXECUTE SCRIPT va verrouiller tout un jeu de tables qui n'ont pas réellement besoin de l'être.
Si vous propagez une procédure stockée qui n'est pas utilisée tout le temps (de telle sorte que vous acceptiez une petite désynchronisation entre les nœuds), alors vous pouvez simplement la soumettre à chaque nœud par l'intermédiare d'une commande psql, ne faisant pas d'utilisation particulière de Slony-I™.
Si vous avez besoin d'une parfaite synchronisation sur l'ensemble des nœuds, vous devez utiliser EXECUTE SCRIPT pour verrouiller vos travaux.
Vous pouvez avoir besoin d'index supplémentaires sur quelques nœuds répliqués pour accroître les performances.
Par exemple, une table consistant en des transactions peut seulement avoir besoin des index relatifs à l'intégrité référentielle sur le nœud « origine ». Maximiser les performances dicte l'impératif de n'avoir que les index nécessaires. Mais rien ne vous empêche d'ajouter des index supplémentaires pour améliorer les performances des rapports qui s'exécutent via les nœuds répliqués.
Il serait imprudent d'ajouter des indicateurs qui contraindraient les évènements sur les nœuds répliqués. En cas de problème, cela conduirait la réplication à s'arrêter car le ou les nœuds abonnés ne pourraient appliquer les changements provenant du nœud origine violant ainsi la contrainte.
Cela ne pose pas de difficultés d'ajouter quelques mesures d'améliorations de performances. Il ne faut pratiquement jamais utiliser EXECUTE SCRIPT dans ce cadre ; cela conduit certains jeux de réplication à verrouiller/déverrouiller des tables et potentiellement échouer à l'application de l'événement à cause de verrous en cours sur les objets, puis devoir ré-essayer un certain nombre de fois avant de pouvoir effectuer le changement. À la place, si vous appliquez l'index « directement » au travers de psql, vous pouvez déterminer le moment auquel le verrou s'exerce sur la table. Ajouter un index à une table nécessite un verrou exlusif pour la durée de création de cet index : cela va implicitement stopper la réplication pendant la durée de construction de l'index, mais sans causer de problemes particuliers. Si vous ajoutez un index sur une table et que sa construction dure 20 minutes, la réplication sera stoppée pendant 20 minutes, mais repartira juste après la fin de la création.
Slony-I™ stocke le nom de l'« index primaire » dans sl_table, et utilise ce nom pour contrôler les colonnes considérées comme les « colonnes clés » lorsque le declencheur de table est présent. Il pourrait être envisageable de supprimer cet index et de le remplacer par une autre clé primaire potentielle, mais un changement de nom de cette clé primaire casserait certainement certaines choses.
Une méthode de test des changements DDL a été validée comme « bonne pratique ».
Vous devez tester les scripts DDL de façon non destructrice.
Si les nœuds sont, pour une raison ou une autre, totalement désynchonisés, la replication va très certainement échouer, et cela a lieu la plupart du temps au plus mauvais moment : celui où vous vouliez que cela fonctionne.
Vous pouvez vérifier si vos scripts DDL fonctionnent bien ou pas en les exécutant manuellement, en ajoutant BEGIN; au début et ROLLBACK; à la fin. De cette façon, les changements effectuent un retour arrière.
Si le script s'effectue correctement sur tous les nœuds, cela semble dire qu'il devrait s'exécuter également correctement via slonik(1). Si des problèmes apparaissent sur certains nœuds, cela devrait vous permettre de remettre ces machines à niveau, de façon à ce que le script s'exécute sans erreur.
Attention, si le script contient un COMMIT; n'importe où avant le ROLLBACK;, cela va effectuer des changements auxquels vous ne vous attendiez pas.