Réplication synchrone et autres fonctionnalités de réplication *************************************************************** Il y a un certain nombre de nouvelles fonctionnalités autour de la réplication en 9.1: En 9.0, l'utilisateur servant à la réplication devait être superutilisateur. Ce n'est plus le cas, il y a un nouvel attribut appelé 'replication'. .. code-block:: sql CREATE ROLE replication_role REPLICATION LOGIN PASSWORD 'pwd_replication'. Ce rôle peut alors être ajouté au pg_hba.conf, et être utilisé pour la streaming replication. C'est évidemment préférable, d'un point de vue sécurité, que d'avoir un rôle superutilisateur dédié à cela. Maintenant que nous avons une instance créée, ainsi qu'un utilisateur de réplication, nous pouvons mettre en place la streaming replication. Il ne s'agit que d'ajouter la permission de se connecter à la base virtuelle 'replication' dans "pg_hba.conf", positionner wal_level, l'archivage (archive_mode et archive_command) et max_wal_senders, ce qui est déjà traité dans le billet sur les nouveautés de la 9.0. Quand l'instance est prête pour le streaming, nous pouvons montrer la seconde nouveauté. :: pg_basebackup. Ce nouvel outil permet de cloner une base, ou en faire une sauvegarde, en n'utilisant que le protocole réseau PostgreSQL. Il n'y a pas besoin d'appeler "pg_start_backup()", puis réaliser une copie manuelle et enfin appeler "pg_stop_backup()". pg_basebackup effectue tout ce travail en une seule commande. Pour la démonstration, nous allons cloner l'instance en cours de fonctionnement vers /tmp/newcluster. :: > pg_basebackup -D /tmp/newcluster -U replication -v Password: NOTICE: pg_stop_backup complete, all required WAL segments have been archived pg_basebackup: base backup completed Cette nouvelle instance est prête à démarrer: ajoutez simplement un fichier "recovery.conf" avec une "restore_command" pour récupérer les fichiers archivés, et démarrez la nouvelle instance. pg_basebackup peut aussi fabriquer un tar, ou inclure tous les fichiers xlog requis (pour avoir une sauvegarde totalement autonome). Comme nous allons maintenant montrer la réplication synchrone, préparons un "recovery.conf" pour se connecter à la base maître et récupérer les enregistrements au fil de l'eau. Le fichier va ressembler à ceci :: restore_command = 'cp /tmp/%f %p' standby_mode = on primary_conninfo = 'host=localhost port=59121 user=replication password=replication application_name=newcluster' trigger_file = '/tmp/trig_f_newcluster' Puis nous démarrons la nouvelle instance: :: pg_ctl -D /tmp/newcluster start LOG: database system was interrupted; last known up at 2011-05-22 17:15:45 CEST LOG: entering standby mode LOG: restored log file "00000001000000010000002F" from archive LOG: redo starts at 1/2F000020 LOG: consistent recovery state reached at 1/30000000 LOG: database system is ready to accept read only connections cp: cannot stat « /tmp/000000010000000100000030 »: No such file or directory LOG: streaming replication successfully connected to primary Nous avons notre esclave, et il récupère les données provenant du maître par le mode «streaming», mais nous sommes toujours en asynchrone. Notez que nous avons positionné un paramètre "application_name" dans la chaîne de connexion du "recovery.conf". * Réplication synchrone Pour que la réplication devienne synchrone, c'est très simple, il suffit de positionner ceci dans le postgresql.conf du maître: :: synchronous_standby_names = 'newcluster' C'est bien sûr l'"application_name" provenant du "primary_conninfo" de l'esclave. Un «pg_ctl_reload», et le nouveau paramètre est pris en compte. Maintenant, tout «COMMIT» sur le maître ne sera considéré comme terminé que quand l'esclave l'aura écrit sur son propre journal, et l'aura notifié au maître. Un petit avertissement: les transactions sont considérées comme validées quand elles sont écrites dans le journal de l'esclave, pas quand elles sont visibles sur l'esclave. Cela veut dire qu'il y a toujours un délai entre le moment où une transaction est validée sur le maître, et le moment où elle est visible sur l'esclave. La réplication est tout de même synchrone: vous ne perdrez pas de données dans le cas du crash d'un maître. La réplication synchrone peut être réglée assez finement: elle est contrôlable par session. Le paramètre "synchronous_commit" peut être désactivé (il est évidemment actif par défaut) par session, si celle-ci n'a pas besoin de cette garantie de réplication synchrone. Si, dans votre transaction, vous n'avez pas besoin de la réplication synchrone, faites simplement :: SET synchronous_commit TO off et vous ne paierez pas la pénalité due à l'attente de l'esclave. Il y a quelques autres nouveautés à mentionner pour la réplication: * Les esclaves peuvent maintenant demander au maître de ne pas nettoyer par VACUUM les enregistrements dont ils pourraient encore avoir besoin. C'était une des principales difficultés du paramétrage de la réplication en 9.0, si on souhaitait utiliser l'esclave: un VACUUM pouvait détruire des enregistrements qui étaient encore nécessaires à l'exécution des requêtes de l'esclave, engendrant des conflits de réplication. L'esclave avait alors à faire un choix: soit tuer la requête en cours d'exécution, soit accepter de retarder l'application des modifications générées par le VACUUM (et toutes celles qui le suivent bien sûr), et donc prendre du retard. On pouvait contourner le problème, en positionnant "vacuum_defer_cleanup_age" à une valeur non nulle, mais c'était difficile de trouver une bonne valeur. La nouvelle fonctionnalité est activée en positionnant "hot_standby_feedback", sur les bases de standby. Bien sûr, cela entraîne que la base de standby va pouvoir empêcher VACUUM de faire son travail de maintenance sur le maître, s'il y a des requêtes très longues qui s'exécutent sur l'esclave. * pg_stat_replication est une nouvelle vue système. Elle affiche, sur le maître, l'état de tous les esclaves: combien de WAL ils ont reçu, s'ils sont connectés, synchrones, où ils en sont de l'application des modifications: .. code-block:: sql SELECT * from pg_stat_replication ; +---------+----------+-------------+------------------+-------------+-----------------+-------------+ |procpid | usesysid | usename | application_name | client_addr | client_hostname | client_port | +---------+----------+-------------+------------------+-------------+-----------------+-------------+ | 17135 | 16671 | replication | newcluster | 127.0.0.1 | | 43745 | +---------+----------+-------------+------------------+-------------+-----------------+-------------+ +------------------------------+-----------+---------------+----------------+----------------+-----------------+---------------+-----------+ | backend_start | state | sent_location | write_location | flush_location | replay_location | sync_priority | sync_state| +------------------------------+-----------+---------------+----------------+----------------+-----------------+---------------+-----------+ | 2011-05-22 18:13:04.19283+02 | streaming | 1/30008750 | 1/30008750 | 1/30008750 | 1/30008750 | 1 | sync | +------------------------------+-----------+---------------+----------------+----------------+-----------------+---------------+-----------+ Il n'est dont plus nécessaire d'exécuter des requêtes sur les esclaves pour connaître leur état par rapport au maître. * pg_stat_database_conflicts est une autre vue système. Celle ci est sur la base de standby, et montre combien de requêtes ont été annulées, et pour quelles raisons: .. code-block:: sql SELECT * from pg_stat_database_conflicts ; +-------+-----------+------------------+------------+----------------+-----------------+----------------+ | datid | datname | confl_tablespace | confl_lock | confl_snapshot | confl_bufferpin | confl_deadlock | +-------+-----------+------------------+------------+----------------+-----------------+----------------+ | 1 | template1 | 0 | 0 | 0 | 0 | 0 | +-------+-----------+------------------+------------+----------------+-----------------+----------------+ | 11979 | template0 | 0 | 0 | 0 | 0 | 0 | +-------+-----------+------------------+------------+----------------+-----------------+----------------+ | 11987 | postgres | 0 | 0 | 0 | 0 | 0 | +-------+-----------+------------------+------------+----------------+-----------------+----------------+ | 16384 | marc | 0 | 0 | 1 | 0 | 0 | +-------+-----------+------------------+------------+----------------+-----------------+----------------+ * la réplication peut maintenant être mise en pause sur un esclave. Appelez tout simplement pg_xlog_replay_pause() pour mettre en pause, et pg_xlog_replay_resume() pour reprendre. Cela gèlera la base, ce qui en fait un excellent outil pour réaliser des sauvegardes cohérentes. pg_is_xlog_replay_paused() permet de connaître l'état actuel. On peut aussi demander à PostgreSQL de mettre l'application des journaux en pause à la fin de la récupération d'instance, sans passer la base en production, pour permettre à l'administrateur d'exécuter des requêtes sur la base. L'administrateur peut alors vérifier si le point de récupération atteint est correct, avant de mettre fin à la réplication. Ce nouveau paramètre est "pause_at_recovery_target", et se positionne dans le recovery.conf. * On peut créer des points de récupération (Restore Points) Ce ne sont rien de plus que des points nommés dans le journal de transactions. Il peuvent être utilisés en spécifiant un "recovery_target_name" à la place d'un "recovery_target_time" ou un "recovery_target_xid" dans le fichier recovery.conf. Ils sont créés en appelant "pg_create_restore_point()".