Il est courant pour les administrateurs de vouloir exploiter des systèmes en utilisant un ensemble de « bonnes pratiques » disponibles et documentées. Documenter ce genre de choses est essentiel pour l'ISO 9000, l'ISO 9001 et d'autres type de certifications d'organisation.
Il est intéressant d'introduire toute discussion sur des « bonnes pratiques » en mentionnant que chaque organisation utilisant Slony-I™ est unique et qu'il est nécessaire qu'une politique locale reflète les caractéristiques administratives locales. C'est pour cette raison que Slony-I™ n'impose pas ses propres règles pour des points tels que les bascules d'urgence ; elles devront être déterminées en fonction de l'architecture globale de votre réseau, de votre ensemble de serveurs de base de données et de votre manière d'utiliser ces serveurs.
Il y a, toutefois, un certain nombre de découvertes des pionniers de Slony-I™ qui peuvent au moins aider à concevoir le genre de règles d'administration que vous pourriez mettre en place.
Slony-I™ est un système multi-clients et multi-serveurs complexe, ce qui implique qu'il existe un ensemble presque infini d'endroits où des problèmes peuvent surgir.
C'est donc naturellement que la maintenance d'un environnement propre, cohérent est réellement décisif car tout type de « cafouillage » dans l'environnement peut provoquer des problèmes ou masquer le problème réel.
De nombreux utilisateurs ont rapporté des problèmes provenants d'incompatibilités entre les versions de Slony-I™, les bibliothèques locales et les bibliothèques de PostgreSQL™. Chaque détail compte : vous devez identifier clairement sur quels hôtes sont hébergées quelles versions de quels logiciels.
Normalement il s'agit juste d'être discipliné lors du déploiement de vos logiciels, et ce défi est une conséquence naturelle lorsqu'on met en place un système distribué constitué par un grand nombre de composants qui doivent correspondre.
Si un script slonik ne fonctionne pas comme prévu à la première tentative, il serait téméraire de tenter de l'exécuter à nouveau tant que le problème n'a pas été trouvé et résolu.
Il y a très peu de commandes slonik tel que SLONIK STORE PATH(7) qui se comporte de manière déterministe ; si vous exécutez SLONIK STORE PATH(7) plusieurs fois, cela mettra plusieurs fois la même valeur dans la table sl_path.
Au contraire, SLONIK SUBSCRIBE SET(7) se comporte de deux manières très différentes selon que l'abonnement a déjà été activé ou pas ; si l'initialisation de l'abonnement n'a pas fonctionné à la toute première tentative, soumettre à nouveau cette requête n'arrangera pas la situation.
Principe : utilisez un fuseau horaire stable et non-ambigüe tel que UTC ou GMT.
Les utilisateurs ont rencontrés des problèmes pour faire fonctionner slon(1) correctement lorsque leur système utilisait un fuseau horaire que PostgreSQL™ ne pouvait pas reconnaître tel que CUT0 ou WST. Il est nécessaire que vous utilisiez un fuseau horaire que PostgreSQL™ reconnaît correctement. De plus, il est préférable d'utiliser un fuseau qui n'est pas soumis au basculement entre heure d'été et heure d'hiver.
Le choix « géographiquement neutre » semble être TZ=UTC ou TZ=GMT. Par ailleurs, assurez-vous que vos serveurs sont « synchronisés » grâce à l'utilisation de NTP sur l'ensemble de votre environnement.
Voir aussi Section 3.4, « Synchronisation horloge ».
Principe : les transactions longues, c'est mal.
La FAQ a un chapitre sur la croissance de pg_listener qui explique tout cela en détails ; pour simplifier, les transactions longues ont de nombreux effets secondaires. Elles sont problématiques en particulier sur un nœud « origine » s'accaparant les verrous, rendant inefficace les VACUUM, et ainsi de suite.
Dans la version 1.2, certains comportements « désagréables » devraient être diminués car :
Les événements dans pg_listener sont seulement générés lorsque les mise à jours de réplication sont relativement rare, ce qui devrait impliquer que les systèmes chargés ne vont pas générer beaucoup de lignes mortes dans cette table.
Le système va périodiquement faire un troncage (en utilisant TRUNCATE pour nettoyer l'ancienne table) entre les deux tables de logs, sl_log_1 et sl_log_2, évitant une croissance illimitée de l'espace « mort » à cet endroit.
Les règles de bascule en urgence devraient être planifiées à l'avance.
Plus précisément, tout nœud qui a pour but d'être une cible dans le cadre d'une bascule doit avoir ces abonnements configurés avec l'option FORWARD = YES. Autrement, ce nœud ne peut pas être un candidat pour devenir nœud origine.
Cela peut simplement se résumer à réfléchir à une liste de priorités indiquant qui devrait basculer vers quoi, plutôt que d'essayer d'automatiser la bascule. Quoiqu'il en soit, savoir au préalable ce qu'il faut faire réduit le nombre d'erreurs commises.
Chez Afilias, une grande variété de guides internes, tel que [Le guide de l'administrateur réveillé à 3 heures du matin...], ont été rédigé pour fournir les procédures à suivre en cas d'événements « malheureux ». Ce genre de matériel est hautement spécifique à l'environnement et à l'ensemble d'applications présentes, donc vous devriez rédiger vos propres documents. Ceci est un des composants vitaux de tout procédure de reprise après crash.
SLONIK MOVE SET(7) doit être utlisé pour la maintenance préventive afin d'éviter l'apparition des problèmes nécessitant une bascule après panne (« failover »).
La politique de VACUUM doit être définie avec soin.
Comme indiqué précédemment, « les transactions longues sont maléfiques ». La commande VACUUM n'est pas une exception à cette règle. Un VACUUM sur une grande table ouvrira une transaction longue qui aura tous les effets négatifs déjà cités.
Si vous utilisez le processus autovacuum avec une version récente de PostgreSQL™, vous pouvez éviter de le faire pour les tables Slony-I™ car Slony-I™ est un peu plus intelligent que le démon autovaccuum lorsqu'il s'agit des VACUUM dans des conditions de réplication (par exemple : la purge des anciennes données).
Reportez-vous au chapitre Section 6.1, « Interaction avec l'autovacuum de PostgreSQL » pour plus de détails.
Il a été prouvé qu'il est préférable d'exécuter tous les démons slon(1) sur un serveur central pour chaque sous-réseau.
Chaque slon(1) doit être hébergé par hôte sur le même réseau local que le nœud qu'il opère car il envoie beaucoup de communications avec sa base de donnée et que la connexion doit être aussi fiable que possible.
En théorie, les « meilleures » performances sont prévues lorsque lslon(1) fonctionne sur le même serveur que la base qu'il opère.
En pratique, déployer les processus slon(1) et leur configuration à travers un réseau d'une douzaine de serveurs se révèle être difficilement gérable.
Les processus slon(1) doivent évoluer dans le même « contexte réseau » que le nœud d'origine, afin que la liaison entre eux soit une connexion « locale ». N'établissez pas ce genre de liaison à travers un réseau WAN. Donc, si vous avez des nœuds à Londres et d'autres à New York, les processus slon(1) gérant les nœuds de Londres devraient être exécutés à Londres, et les processus slon(1) gérant les nœuds de New York devraient être exécutés à New York.
Une coupure de lien WAN (ou un lien peu fiable en général) peut provoquer des connexions « zombies », et le comportement typique du TCP/IP consiste à laisser ces connexions persister, empêchant le démon slon de redémarrer pendant environ deux heures.
Il n'est pas difficile de remédier à cela. Vous devez simplement tuer les processus liés aux connexions persistantes avec la commande kill SIGINT. Cependant en exécutant les slon(1) localement, vous êtes généralement à l'abri de ce genre de problèmes.
Si vous rencontrez ce genre de problèmes, avant de vous exciter, essayez de tuer et redémarrer tous les processus slon(1). Historiquement, cette méthode a souvent résolu ce genre de « petit tracas. ».
À quelques rares exceptions, il n'est pas très grave de tuer et relancer les processus slon(1). Chaque slon(1) se connecte à la base de données qu'il gère, puis ouvre les connexions nécessaires à la propagation des événements. Si vous tuez un slon(1), vous provoquez simplement l'interruption de ces connexions. Si un évènement SYNC ou un autre événement est en cours de propagation, il n'y a pas de problème : la transaction sera annulée (via un ROLLBACK) et lorsque le slon(1) sera relancé, l'évènement sera retraité à partir de zéro.
Le scénario d'exception qui rend un redémarrage de slon(1) indésirable est le cas où une commande COPY_SET est en cours d'exécution sur un grand ensemble de réplication. Dans ce genre de cas, arrêter un slon(1) peut annuler plusieurs heures de travail.
Dans les premières versions de Slony-I™, il était fréquent que des connexions soient un peu « dérangées », ce qu'on pouvait réparer en redémarrant slon(1). Ceci est devenu nettement plus rare mais il est parfois utile de redémarrer slon(1). En cas de « panne réseau », cela peut résoudre le problème des connexions défuntes.
La section sur les changements de modèle de données détaille quelques bonnes pratiques qui sont utiles lorsqu'on souhaite modifier le schéma de la base de données.
Gestion des clefs primaires.
Comme précisé dans la section Ensembles de réplication, il est idéal que chaque table répliquée dispose d'une vraie contrainte de clef primaire ; il est acceptable d'utiliser une « clef primaire candidate ».
Il n'est pas recommandé d'utiliser une clef définie par Slony-I™ (créée par SLONIK TABLE ADD KEY(7)) pour proposer une clef primaire candidate car cela introduit la possibilité d'échecs de certaines mises à jour sur la table à cause de l'index unique de cette clef. Ceci entraînerait potentiellement des bogues dans votre application à cause de Slony-I™.
Dans la version 2 de Slony-I™, SLONIK TABLE ADD KEY(7) n'est plus supportée. Vous devez absolument avoir soit une vraie clef primaire, soit une clef primaire candidate.
La section Définir les ensembles de réplication vous suggère des stratégies pour déterminer comment regrouper les tables et les séquences en ensembles de réplication.
Il est évident que les actions qui peuvent supprimer beaucoup de données doivent être effectuées avec le plus grand soin. La section Supprimer des éléments de la réplication évoque les différentes sortes de « suppression » que Slony-I™ supporte.
Certaines opérations Slony-I™, notament set add table, move set, lock set et execute script nécessitent l'acquisition de verrous exclusifs sur les tables répliquées.
En fonction de type d'activité de votre base de données, cela peut (ou pas) provoquer des coupures de services (heureusement assez brèves).
Que faire des ordres DDL ?
Slony-I™ fonctionne en détectant les mises à jour sur les tables grâce à des triggers qui sont placés sur ces tables. Cela implique que les mises à jours que se font au moyen de méthodes qui ne déclenchent pas les triggers ne seront pas propagées. Les commandes ALTER TABLE, CREATE OR REPLACE FUNCTION, CREATE TABLE sont toutes des requêtes SQL que Slony-I™ ne peut pas détecter.
Pour ce genre de situation, la philosophie de Slony-I™ consiste à considérer que les développeurs compétents n'écrivent jamais de code auto-modifiant et en particulier du code modifiant les modèles de données à la volée. Slony-I™ ne cherche pas à faciliter ce genre de modification du schéma de données.
Cependant, il existe des cas où cela est nécessaire, ainsi execute script est fournie pour appliquer des changements DDL au même point dans le flux de transactions sur tous les serveurs.
Malheureusement, cela provoque de nombreux verrous sur les objets de la base de données. Modifier les tables nécessite l'acquisition d'un verrou exclusif sur ces objets ; ainsi le script d'exécution des modifications entraîne un verrou exclusif sur toutes les tables répliquées. Cela peut s'avérer très problématique si les applications fonctionnent alors que des instructions DDL sont en cours d'exécution ; Slony-I™ demande pour elles des verrous exclusifs sur les tables alors que, simultanément, certaines connexions renoncent graduellement à des verrous et que d'autres récupèrent les verrous Slony-I™.
Une position particulièrement dogmatique défendue par certains consiste à dire qu'il faut toujours propager les changements de modèles de données avec un script d'exécution. Cela garantit que les nœuds restent cohérents. Cependant, le coût des verrous et des inter-blocages est trop élevé pour certains utilisateurs.
Chez Afilias, notre approche est moins dogmatique ; il y a certains changements qui doivent être appliqués avec un script d'exécution mais nous appliquons certaines modifications de manière indépendante.
Liste de changements qui doivent être effectuée dans un script d'exécution :
Toutes les commandes ALTER TABLE.
Listes des changement qui ne nécessitent pas un script d'exécution :
CREATE INDEX
CREATE TABLE
Les tables qui ne sont pas répliquées n'ont pas besoin de ces mécanismes.
CREATE OR REPLACE FUNCTION
Typiquement, le chargement de nouvelles version de fonctions peut être effectuée sans que Slony-I™ en soit « averti ». À l'exception évidente des cas où la nouvelle fonction est déployée suite à la modification d'une table. Dans ce cas-là, la nouvelle version doit être déployée de manière synchronisée avec le script d'exécution qui modifie la table.
De la même manière, les ordres CREATE TYPE, CREATE AGGREGATE, etc. n'ont pas besoin d'être forcément insérés de manière « parfaitement synchronisés » sur l'ensemble des nœuds.
Les ordres de gestion des autorisations, tels que CREATE USER, CREATE ROLE , GRANT, etc. sont inutiles pour Slony-I™ car ce dernier est exécuté avec les droits de « super-utilisateur ».
Dans la pratique, il est utile d'avoir différentes politiques de sécurité sur les nœuds. L'accès au nœud « maître » doit être limité aux applications qui en ont réellement besoin. Les utilisateurs effectuant de l'extraction de données (« reporting ») ont généralement des droits plus limités sur le nœud maître que sur les nœuds abonnés.
Noms d'utilisateur spécifiques à Slony-I™.
Il s'est avéré utile de définir un utilisateur slony employé uniquement par Slony-I™, un utilisateur distinct de l'utilisateur générique postgres ou pgsql.
Lorsque toutes sortes d'activités de « maintenance » automatiques, telles que les VACUUM et les sauvegardes, sont effectuées avec l'« identité » de l'utilisateur PostgreSQL™, cela peut assez facilement provoquer des problèmes d'inter-blocages (« deadlocks »).
Par exemple, une série de VACUUM qui sont lancés de manière inattendue sur une base de donnée avec un gros événement SUBSCRIBE_SET en cours d'exécution peut entraîner des inter-blocages qui peuvent annuler plusieurs heures de travail de copie de données.
À l'inverse, si les différentes opérations de maintenance sont exécutées par différents utilisateurs, vous pourrez assurer la réussite d'une opération vitale telle que SUBSCRIBE_SET, en bloquant les autres utilisateurs au niveau du fichier pg_hba.conf, et en autorisant uniquement l'utilisateur slony, ce qui réduit considérablement les risques lors d'une souscription.
Configuration des chemins
La section sur les voies de communications évoque les problèmes liés aux connexions réseau nécessaires au bon fonctionnement de Slony-I™.
Réduction des super-pouvoirs
Traditionellement, on considère que « Slony-I™ a besoin de connexions en mode super-utilisateur ». Cela n'est pas totalement vrai et si l'usage excessif de comptes super-utilisateurs pose problème, il est possible de réduire nettement cet usage.
Il est vrai que chaque slon(1) doit avoir une connexion super-utilisateur afin de gérer le nœud qu'il opère. Il doit pouvoir modifier le catalogue système afin de mettre en place les abonnements et procéder aux modifications (par exemple pour exécuter SLONIK EXECUTE SCRIPT(7) et d'autres évènement qui peuvent modifier le rôle d'une tables répliqué sur un nœud local).
Toutefois, les connexions que les processus slon(1) ouvrent vers d'autres nœuds afin d'accéder aux événements et aux souscriptions n'ont pas besoin d'avoir d'autant de droits. Ainsi, on peut mettre en place un « utilisateur faible » assigné à toutes les requêtes SLONIK STORE PATH(7). Les droits minimaux de cet utilisateur, appellons-le weakuser, sont les suivantes :
Il doit avoir accès en lecture sur le schéma spécifique de Slony-I™ ;
Il doit avoir accès en lecture sur toutes les tables et les séquences de ce schéma ;
Il doit avoir accès en écriture sur la table sl_nodelock et la séquence sl_nodelock_nl_conncnt_seq ;
Lors de la souscrition, il doit avoir accès en lecture sur toutes les tables répliquées.
Après la souscription, il n'est plus nécessaire d'accéder aux tables répliquées.
Il est parfois nécessaire de lire les tables dans pg_catalog sans qu'on ait pu vérifier le niveau d'accès convenable.
Dans la version 1.3, les tests du Section 25, « Banc d'essai Slony-I » permettent l'utilisation d'un utilisateur faible (WEAKUSER) afin de tester si les droits définis sont suffisants pour supporter la réplication.
La section sur les voies d'écoute parle des problèmes entourant la table sl_listen.
À partir de la version Slony-I™ 1.1, le contenu de cette table était calculé automatiquement en se basant sur les informations dont disposait Slony-I™, ce qui devrait supprimer les problèmes rencontrés. Beaucoup de problèmes de communication inexplicables, avec des nœuds qui n'arrivent pas à se parler alors que c'est techniquement possible, étaient du à une configuration incorrecte des voies d'écoutes.
Exécutez Section 5.1, « test_slony_state » fréquemment pour découvrir les problèmes de configuration aussi rapidement que possible.
Il s'agit d'un script Perl qui se connecte à un nœud Slony-I™ puis parcourt la configuration Slony-I™ à la recherche de différentes conditions qui indiquent la présence de problèmes, en particulier :
le gonflement de certaines tables de congfiguration ;
l'analyse des voies d'écoute ;
l'analyse de la propagation et de la confirmation des événements.
Si, de manière mystérieuse, la réplication « ne marche pas », cet outil peut vérifier beaucoup de problèmes potentiels pour vous.
Il remarquera un ensemble de situations où quelque chose a cassé. Il doit être utilisé quand des problèmes ont été remarqués, mais il devrait aussi être utilisé fréquemment (c'est-à-dire environ toutes les heures) comme un outil de vérification de la « santé » pour chaque cluster Slony-I™.
À partir de la version 1.1, la configuration de slon(1) peut être définie par ligne de commande ou dans des fichiers de configuration. De « bonnes » pratiques sont conseillées dans les deux cas :
Configuration via les options en ligne de commande
Cette approche a le mérite de rendre visible dans l'environnement système toutes les options actives. Ainsi, s'il y en a beaucoup, cela peut devenir pénible à lire.
Malheureusement, si vous invoquez slon(1) à partir d'une ligne de commande, vous pouvez oublier d'inclure la configuration de log shipping et ainsi détruire les séquences de logs pour les nœuds de log shipping.
À la différence des options en ligne de commande, les options actives ne sont pas visibles. Elles peuvent seulement être positionnées à partir du nom et du contenu du fichier de configuration de slon(1) et ne refléteront pas les changements apparus dans le fichier de configuration.
En plaçant les options dans un fichier, vous n'en oublierez aucune, ce qui est plus sûr pour log shipping.
Taches à faire lorsqu'on abonne un nœud
Lorsqu'un nouveau nœud exécute l'événement COPY_SET sur un grand ensemble de réplication (par exemple un ensemble qui nécessite un abonnement de plusieurs heures), il est prouvé qu'il est souhaitable de verrouiller l'accès au nœud pour tous les utilisateurs autres que slony car :
Une bonne idée est de modifier le paramètre slon_conf_sync_interval de slon(1) sur le nœud origine pour réduire le nombre d'événements SYNC générés. Si l'abonnement prend huit heures, il n'y a aucunintérêt à avoir 28800 SYNC en attente d'être appliqué. Exécuter un SYNC chaque minute rendre plus facile le rattrapage du retard.
Les applications s'exécutent sur des données partiellement copiées qui ne seront pas cohérentes.
Il est possible que certaines applications (et certains scripts de maintenance) soumettent une combinaison de requêtes qui placeront le système dans une situation d'inter-blocage (« deadlock »), ce qui annulera l'événement COPY_SET et impliquera le ré-abonnement complet du nœud.
Il peut être intéressant de désactiver la fonctionnalité fsync de PostgreSQL™ pendant la copie des données, car cela améliorera les performances, et en cas d'échec lors de l'évènement COPY_SET, vous pourrez redémarrer avec une copie entière de l'ensemble de réplication.
Gestion de slonik
Les notes sur l'utilisation de Slonik décrivent certaines leçons apprises en gérant un grand nombre de scripts slonik(1).
Voici les principes importants dégagés lors de la création de ces scripts :
Utiliser des fichiers « préambule » est hautement recommandé car cela implique que vous utilisiez et réutilisiez des préambules vérifiés maintes fois.
Toute opportunité de générer automatiquement la configuration soit en la récupérant dans une base de données, soit en utilisant un script de génération vous aidera à éviter les erreurs humaines.
Gérer un très grand ensemble de réplication.
Certains utilisateurs ont bâti des réplications sur des ensembles de plusieurs dizaines, voire plusieurs centaines de gigaoctets, ce qui ajoute des « contraintes » sur le système, en particulier lorsqu'il faut plusieurs jours pour effectuer un évènement COPY_SET. Voici quelques principes qui ont été définis pour gérer ce genre de situations.
Supprimer tous les index autres que les index primaires lorsque l'évènement en COPY_SET est exécuté.
Lorsque les données sont copiées dans une table qui dispose d'index, PostgreSQL™ construit les index de manière incrémentale, à la volée. Ceci est beaucoup plus lent que simplement copier les donnés dans une table puis de recréer chaque index « ex nihilo » car cette dernière opération profite de l'avantage de la mémoire de tri.
Dans Slony-I™ version 1.1.5 et dans les versions ultérieures, les index sont supprimés et recréés automatiquement, ce qui rend caduque ce conseil.
Si beaucoup de mises à jour ont lieu lorsque de grands ensembles sont copiés, ceci peut mener à un nombre énorme d'événements SYNC sur le nœud qui s'abonne.
Si un SYNC est généré une fois par seconde, ceci conduit à un « retard » de plus de 90 000 SYNC par jour, pendant probablement plusieurs jours.
Parallèlement, on constate une croissance énorme des tables sl_log_1, sl_log_2 et sl_seqlog. Malheureusement, une fois que COPY_SET est terminé, on constate que les requêtes sur ces tables se font via des parcours séquentiels. Même si le SYNC ne traite qu'une petite partie de ces 90 000 événements SYNC, la table sera lue dans son ensemble. Dans de tels cas, il est possible que le nœud abonné ne rattrape jamais le nœud origine.
Plusieurs taches peuvent résoudre ce problème, notamment en réglant avec soin les paramètres slon(1) :
Assurez-vous qu'il existe sur le nœud « maître » un index sur sl_log_1(log_xid). S'il n'y en a pas, comme avec les versions de Slony-I™ inférieures à la version 1.1.1, regardez dans le fichier slony1_base.sql pour la configuration correcte de cet index.
En 1.2, il y a un processus qui ajoute automatiquement les index partiels par numéro de nœud d'origine, ce qui est la forme optimale pour ces index.
Sur un nœud abonné, ajoutez le nombre d'évènements SYNC traités ensemble, en réglant le paramètre slon_conf_sync_group_maxsize à une valeur lui permettant une portion significative de la masse d'événements SYNC.
Sur le nœud abonné, réglez le paramètre desired_sync_time à 0 car le système de regroupement adaptatif des SYNC fonctionne avec de petits groupes qui, sous certaines circonstances, donne de mauvaises performances.
Augmenter le slon_conf_sync_interval sur le nœud origine slon(1) afin que les événements SYNC soient générés moins fréquemment. Si un SYNC est simplement généré une fois par minute plutôt qu'une fois par seconde, cela divisera par soixante le nombre d'évènements.
Il faudra probablement utiliser le slon_conf_vac_frequency pour désactiver les VACUUM lancés par slon(1) afin d'utiliser vos propres scripts de VACUUM car il y aura une masse de données non purgeables pendant que les données seront copiées et que l'abonné commencera à rattraper le fournisseur.