<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Replication on Carnet de découvertes</title><link>https://fljd.in/tags/replication/</link><description>Recent content in Replication on Carnet de découvertes</description><generator>Hugo -- gohugo.io</generator><language>fr</language><managingEditor>Florent Jardin</managingEditor><lastBuildDate>Thu, 19 Dec 2019 17:00:00 +0200</lastBuildDate><atom:link href="https://fljd.in/tags/replication/index.xml" rel="self" type="application/rss+xml"/><item><title>Le jour où tout bascule</title><link>https://fljd.in/2019/12/19/le-jour-ou-tout-bascule/</link><pubDate>Thu, 19 Dec 2019 17:00:00 +0200</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2019/12/19/le-jour-ou-tout-bascule/</guid><description>&lt;p&gt;Lorsque l&amp;rsquo;on exploite une plateforme PostgreSQL avec de la réplication, il est
exceptionnel de devoir déclencher le plan de bascule, rédigé par un ancien
collègue ou un prestataire oublié. Ce genre de décision se prend lorsque
l&amp;rsquo;ensemble des experts ont individuellement déterminé qu&amp;rsquo;aucune autre solution
n&amp;rsquo;était envisageable.&lt;/p&gt;
&lt;p&gt;Quels moyens a-t-on avec une architecture PostgreSQL dans son plus simple appareil
pour réaliser une bascule des rôles et raccrocher les instances secondaires au
nouveau primaire fraîchement élu ?&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="mise-en-place"&gt;Mise en place&lt;/h2&gt;
&lt;p&gt;Partons du postulat que l&amp;rsquo;architecture la plus simple en terme de haute-disponibilité
avec PostgreSQL est constituée de deux services avec une réplication physique
d&amp;rsquo;une instance primaire vers une instance secondaire.&lt;/p&gt;
&lt;p&gt;Pour des raisons de coûts, aucun serveur supplémentaire n&amp;rsquo;est alloué pour accueillir
les archives de journaux de transactions, et ces derniers seront déplacés sur le
serveur secondaire avec la commande &lt;code&gt;rsync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Et puisque la version 12 apporte des nouveautés notables sur la gestion d&amp;rsquo;une
réplication, nous l&amp;rsquo;employerons dans cet article pour configurer nos instances
sans le fichier &lt;code&gt;recovery.conf&lt;/code&gt;. Pour la facilité de lecture, je baptiserai
volontairement les deux serveurs : &lt;code&gt;batman&lt;/code&gt; et &lt;code&gt;robin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2019-12-19-architecture-batman-robin.png" alt="Architecture simplifiée"&gt;&lt;/p&gt;
&lt;p&gt;L&amp;rsquo;instance &lt;code&gt;batman&lt;/code&gt; dispose de la configuration suivante :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# postgresql.auto.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;5432&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;archive_mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;wal_log_hints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;archive_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;rsync -a %p robin:/opt/batcave/%f&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;restore_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;cp /opt/batcave/%f %p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;archive_cleanup_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;pg_archivecleanup /opt/batcave %r&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;primary_conninfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;host=batman user=streamer&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le fichier &lt;code&gt;postgresql.auto.conf&lt;/code&gt; permet de surcharger les valeurs présentes dans
le fichier standard &lt;code&gt;postgresql.conf&lt;/code&gt; et, avantage certain sur une distribution
Debian &amp;amp; co, il est accessible dans le répertoire de données &lt;code&gt;PGDATA&lt;/code&gt; et pourra
être copié en l&amp;rsquo;état vers toutes les instances secondaires au moment de leur
création.&lt;/p&gt;
&lt;p&gt;Cette étape par ailleurs repose sur un outil simple et fourni avec toutes les
versions PostgreSQL : &lt;code&gt;pg_basebackup&lt;/code&gt;. L&amp;rsquo;unique prérequis est de disposer d&amp;rsquo;un
compte de réplication sur l&amp;rsquo;instance à répliquer et que le serveur distant puisse
réaliser une authentification valide (fichier &lt;code&gt;pg_hba.conf&lt;/code&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_basebackup --host&lt;span class="o"&gt;=&lt;/span&gt;batman --user&lt;span class="o"&gt;=&lt;/span&gt;streamer &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --pgdata&lt;span class="o"&gt;=&lt;/span&gt;robin --wal-method&lt;span class="o"&gt;=&lt;/span&gt;stream --checkpoint&lt;span class="o"&gt;=&lt;/span&gt;fast
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;touch robin/standby.signal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl start -D robin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Un autre outil existe pour contrôler l&amp;rsquo;état d&amp;rsquo;une instance en scannant le contenu
du fichier &lt;code&gt;PGDATA/global/pg_control&lt;/code&gt;, notamment pour savoir si notre serveur est
primaire ou secondaire et s&amp;rsquo;il réplique les journaux de transactions :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_controldata -D robin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# pg_control version number: 1201&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Catalog version number: 201909212&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Database system identifier: 6771436906102136159&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Database cluster state: in archive recovery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Latest checkpoint location: 0/A000098&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Latest checkpoint&amp;#39;s REDO location: 0/A000060&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Latest checkpoint&amp;#39;s REDO WAL file: 00000001000000000000000A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# wal_level setting: replica&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# wal_log_hints setting: on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# WAL block size: 8192&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Data page checksum version: 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="la-bascule"&gt;La bascule&lt;/h2&gt;
&lt;p&gt;À présent, mettons de la forme à notre incident.&lt;/p&gt;
&lt;p&gt;Vous revenez de pause déjeûner aux alentours de 13:30 et le service support est
alerté depuis midi de la perte d&amp;rsquo;un composant réseau sur votre &lt;em&gt;datacenter&lt;/em&gt;
principal. Toute la charge a basculé et les services web sont redirigées
correctement… Pas de bol, les frontaux remontent des erreurs et la navigation
est en mode dégradé : l&amp;rsquo;instance secondaire est en lecture seule, et l&amp;rsquo;on vous
attendait pour corriger le tir !&lt;/p&gt;
&lt;p&gt;Ni une ni deux, un accès au serveur &lt;code&gt;robin&lt;/code&gt; et une commande solutionnent le
problème :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl promote -D robin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# FATAL: terminating walreceiver process due to administrator command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: invalid record length at 0/A000148: wanted 24, got 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: redo done at 0/A000110&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: selected new timeline ID: 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: archive recovery complete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: database system is ready to accept connections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;L&amp;rsquo;instance &lt;code&gt;robin&lt;/code&gt; est donc promue, elle acceptera toutes les demandes d&amp;rsquo;écriture
en contrepartie d&amp;rsquo;une nouvelle ligne de temps (&lt;em&gt;timeline&lt;/em&gt;) dédiée aux futures
transactions.&lt;/p&gt;
&lt;p&gt;Je passe la scène des grandes accolades et chaleureux compliments qui n&amp;rsquo;auront
jamais lieu car les équipes ont déjà d&amp;rsquo;autres chats à fouetter ; après tout,
soyez réaliste, vous n&amp;rsquo;avez exécuté qu&amp;rsquo;une seule commande ! Autant dire, vous
prenez votre pause, et alors que coule votre café, vous apprenez par
l&amp;rsquo;intermédiaire du &lt;em&gt;delivery manager&lt;/em&gt; que le client rencontre des dégradations
de performance sur son &lt;em&gt;backoffice&lt;/em&gt; depuis la perte du nœud &lt;code&gt;batman&lt;/code&gt;. Mais, de
quoi parle-t-il ?&lt;/p&gt;
&lt;p&gt;Et l&amp;rsquo;architecture globale vous revient en mémoire. Une goûte perle votre front :
l&amp;rsquo;instance secondaire est utilisée pour répartir la charge de lecture entre chaque
nœud à l&amp;rsquo;aide de l&amp;rsquo;attribut de préférence &lt;code&gt;target_session_attrs&lt;/code&gt; (&lt;a href="https://www.postgresql.org/docs/12/libpq-connect.html#id-1.7.3.8.3.6" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt;)
et aucun mécanisme d&amp;rsquo;éviction en cas de &lt;em&gt;split-brain&lt;/em&gt; ou de VIP flottante n&amp;rsquo;ont
été déployés sur vos serveurs…&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2019-12-19-split-brain.png" alt="Split brain applicatif"&gt;&lt;/p&gt;
&lt;h2 id="synchronisation"&gt;Synchronisation&lt;/h2&gt;
&lt;p&gt;L&amp;rsquo;urgence impose d&amp;rsquo;intervenir sur les chaînes de connexion pour réduire le risque
de modification sur la mauvaise &lt;em&gt;timeline&lt;/em&gt;. Vous recommandez à l&amp;rsquo;équipe support
de retirer l&amp;rsquo;IP &lt;code&gt;batman&lt;/code&gt; de tout ce qui s&amp;rsquo;apparente à un fichier &lt;code&gt;settings_db.xml&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;postgresql://app@batman,robin/gotham?target_session_attrs&lt;span class="o"&gt;=&lt;/span&gt;read-write
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# devient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;postgresql://app@robin/gotham?target_session_attrs&lt;span class="o"&gt;=&lt;/span&gt;read-write
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Les performances ne sont bien évidemment pas meilleures, mais tout risque de perte
de données lié au &lt;em&gt;split-brain&lt;/em&gt; est écarté. Le timing est parfait, car au même
moment, l&amp;rsquo;équipe système vous informe que l&amp;rsquo;intervention au &lt;em&gt;datacenter&lt;/em&gt; a permis
la remise en réseau des serveurs, toujours actifs.&lt;/p&gt;
&lt;p&gt;Votre rôle consiste donc à rétablir la synchronisation entre &lt;code&gt;batman&lt;/code&gt; et &lt;code&gt;robin&lt;/code&gt;
pour accomplir leur mission de répartition de charge. La première méthode disponible
réside dans le duo gagnant &lt;code&gt;pg_start/stop_backup()&lt;/code&gt; et &lt;code&gt;rsync&lt;/code&gt; pour réaliser à
la main une sauvegarde physique différentielle.&lt;/p&gt;
&lt;h3 id="première-méthode"&gt;Première méthode&lt;/h3&gt;
&lt;p&gt;Puisque la sauvegarde exclusive est annoncée obsolète depuis la version 9.6,
nous déclarerons le début d&amp;rsquo;une sauvegarde concurrente sur l&amp;rsquo;instance primaire
&lt;code&gt;robin&lt;/code&gt; à l&amp;rsquo;aide de la commande suivante et nous maintiendrons la connexion :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_start_backup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rsync the batman&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- pg_start_backup
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- -----------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 0/15000028
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sur le nœud &lt;code&gt;batman&lt;/code&gt;, on peut alors transférer les données avec l&amp;rsquo;option
&lt;code&gt;--whole-file&lt;/code&gt; de la commande rsync pour réduire le risque de corruption des
fichiers de données :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl stop -D batman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsync robin:/opt/robin/ batman --archive --checksum --whole-file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;À l&amp;rsquo;issue de cette copie victorieuse, n&amp;rsquo;oubliez pas de lancer la commande
&lt;code&gt;pg_stop_backup()&lt;/code&gt; sur l&amp;rsquo;instance primaire pour finaliser la sauvegarde et la
rendre valide.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;labelfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stop_backup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;gx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- -[ RECORD 1 ]-------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- labelfile | START WAL LOCATION: 0/15000028 (file 000000020000000000000015)+
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- | CHECKPOINT LOCATION: 0/15000060 +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- | BACKUP METHOD: streamed +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- | BACKUP FROM: master +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- | START TIME: 2019-12-17 16:21:21 CET +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- | LABEL: rsync the batman +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- | START TIMELINE: 2 +
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La dernière étape devient bordélique, mais n&amp;rsquo;ayez crainte, ça ne dure pas
longtemps. Les commandes sont à exécuter sur l&amp;rsquo;instance secondaire &lt;code&gt;batman&lt;/code&gt; et
s&amp;rsquo;assurent notamment que l&amp;rsquo;instance redémarre avec les bons paramètres de
réplication, dont &lt;code&gt;primary_conninfo&lt;/code&gt; et les fichiers &lt;code&gt;standby.signal&lt;/code&gt; et
&lt;code&gt;backup_label&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sed -i -e &lt;span class="s1"&gt;&amp;#39;s/batman/robin/g&amp;#39;&lt;/span&gt; batman/postgresql.auto.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsync robin:/opt/robin/pg_wal/ batman/pg_wal &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --archive --checksum --whole-file
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# récupération des instructions backup_label présentes dans &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# le fichier .backup généré par la commande pg_stop_backup()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -iv ^stop batman/pg_wal/000000020000000000000015.00000028.backup &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt; batman/backup_label
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm batman/postmaster.pid
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;touch batman/standby.signal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl start -D batman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: entering standby mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000002.history&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;000000020000000000000015&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: redo starts at 0/15000028&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: consistent recovery state reached at 0/15000138&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: database system is ready to accept read only connections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: invalid record length at 0/16000148: wanted 24, got 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: started streaming WAL from primary at 0/16000000 on timeline 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;L&amp;rsquo;état intermédiaire de notre cluster peut être visualisé comme suit. On constate
que la zone d&amp;rsquo;archivage (&lt;code&gt;batcave&lt;/code&gt;) réside donc sur le même serveur que l&amp;rsquo;instance
nouvellement primaire &lt;code&gt;robin&lt;/code&gt; et pourrait être un risque en cas de surincident.
De manière générale, il est recommandé d&amp;rsquo;externaliser systématiquement les archives
et les sauvegardes !&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2019-12-19-architecture-apres-rsync.png" alt="Situation après la synchronisation"&gt;&lt;/p&gt;
&lt;p&gt;Vous y conviendrez, cette étape était particulièrement coton pour l&amp;rsquo;envisager
dans une situation passablement stressante. Voyons ensemble l&amp;rsquo;autre solution plus
adaptée à notre scénario : la commande &lt;code&gt;pg_rewind&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="deuxième-méthode"&gt;Deuxième méthode&lt;/h3&gt;
&lt;p&gt;La situation reste inchangée, &lt;em&gt;i.e.&lt;/em&gt; l&amp;rsquo;instance &lt;code&gt;batman&lt;/code&gt; est primaire sur une
ancienne &lt;em&gt;timeline&lt;/em&gt; et doit être resynchronisée avec &lt;code&gt;robin&lt;/code&gt; pour obtenir toutes
les modifications réalisées depuis sa promotion.&lt;/p&gt;
&lt;p&gt;Comme suggéré dans la &lt;a href="https://www.postgresql.org/docs/12/app-pgrewind.html#id-1.9.5.9.8" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt;, le compte de réplication &lt;code&gt;streamer&lt;/code&gt;
doit disposer des droits d&amp;rsquo;exécution sur certaines fontions internes pour
utiliser l&amp;rsquo;outil &lt;code&gt;pg_rewind&lt;/code&gt; correctement :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_ls_dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;streamer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;streamer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_read_binary_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;streamer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_read_binary_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;streamer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sur le serveur &lt;code&gt;batman&lt;/code&gt;, l&amp;rsquo;instance doit être arrêtée avant de lancer la
synchronisation et le contenu de la zone d&amp;rsquo;archivage &lt;code&gt;batcave&lt;/code&gt; doit être copié
manuellement vers le répertoire de récupération &lt;code&gt;pg_wal&lt;/code&gt; ; il s&amp;rsquo;agit de reproduire
l&amp;rsquo;instruction &lt;code&gt;restore_command&lt;/code&gt; que ne peut pas exécuter l&amp;rsquo;instance lorsqu&amp;rsquo;elle
est éteinte.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl stop -D batman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsync robin:/opt/batcave/ batman/pg_wal &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --archive --checksum --whole-file
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_rewind -D batman &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --source-server&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;host=robin user=streamer dbname=postgres&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# pg_rewind: servers diverged at WAL location 0/8000060 on timeline 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# pg_rewind: rewinding from last common checkpoint at 0/7000060 on timeline 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# pg_rewind: Done!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Le résultat de la commande &lt;code&gt;pg_rewind&lt;/code&gt; nous informe que &lt;code&gt;batman&lt;/code&gt; est revenu à
la position de sa &lt;em&gt;timeline&lt;/em&gt; au moment de la promotion de &lt;code&gt;robin&lt;/code&gt;. Cette opération
repose sur un format étendu des journaux de transactions, désactivé par défaut.
Les plus attentifs auront constaté le paramètre &lt;code&gt;wal_log_hints=on&lt;/code&gt;(&lt;a href="https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LOG-HINTS" target="_blank" rel="noopener"&gt;documentation&lt;/a&gt;)
dans le fichier &lt;code&gt;postgresql.auto.conf&lt;/code&gt; en début d&amp;rsquo;article, qui est l&amp;rsquo;un des
prérequis de l&amp;rsquo;outil &lt;code&gt;pg_rewind&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;pg_rewind requires that the target server either has the wal_log_hints option
enabled in postgresql.conf or data checksums enabled when the cluster was
initialized with initdb. Neither of these are currently on by default.
full_page_writes must also be set to on, but is enabled by default.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="message"&gt;Pour certains au fond de la salle, cette opération correspond à l&amp;rsquo;instruction
&lt;code&gt;FLASHBACK DATABASE&lt;/code&gt; sur un moteur Oracle Database lorsque l&amp;rsquo;on souhaite
reconstruire une instance Dataguard.&lt;/div&gt;
&lt;p&gt;La dernière étape consiste à modifier la chaîne &lt;code&gt;primary_conninfo&lt;/code&gt; et ajouter le
fichier &lt;code&gt;standby.signal&lt;/code&gt; avant de démarrer l&amp;rsquo;instance &lt;code&gt;batman&lt;/code&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sed -i -e &lt;span class="s1"&gt;&amp;#39;s/batman/robin/g&amp;#39;&lt;/span&gt; batman/postgresql.auto.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;touch batman/standby.signal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl start -D batman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: entering standby mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000002.history&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;000000010000000000000007&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: redo starts at 0/7000028&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;000000020000000000000008&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: started streaming WAL from primary at 0/9000000 on timeline 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: consistent recovery state reached at 0/904B5C8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="inversion-des-rôles"&gt;Inversion des rôles&lt;/h2&gt;
&lt;p&gt;Cette opération permet la remise en place des rôles à leur état nominal. Ainsi,
&lt;code&gt;batman&lt;/code&gt; reprendra le contrôle et &lt;code&gt;robin&lt;/code&gt; deviendra son second. Les étapes sont
relativement simples :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Arrêter proprement l&amp;rsquo;instance &lt;code&gt;robin&lt;/code&gt; pour écrire les caches sur disque,
notamment les dernières transactions dans les journaux mais également, pour
envoyer toutes les modifications à travers le flux de réplication vers &lt;code&gt;batman&lt;/code&gt; ;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl stop -D robin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Contrôler optionnellement que les positions sont identiques entre les deux
instances ;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_controldata -D robin &lt;span class="p"&gt;|&lt;/span&gt; grep -iE &lt;span class="s2"&gt;&amp;#34;(cluster state|checkpoint location)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Database cluster state: shut down&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Latest checkpoint location: 0/A000028&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_controldata -D batman &lt;span class="p"&gt;|&lt;/span&gt; grep -iE &lt;span class="s2"&gt;&amp;#34;(cluster state|checkpoint location)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Database cluster state: in archive recovery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Latest checkpoint location: 0/A000028&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Promouvoir la nouvelle instance primaire &lt;code&gt;batman&lt;/code&gt; ;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl promote -D batman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: received promote request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: redo done at 0/A000028&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: selected new timeline ID: 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: archive recovery complete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000002.history&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: database system is ready to accept connections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Configurer &lt;code&gt;robin&lt;/code&gt; en instance secondaire et la démarrer.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;touch robin/standby.signal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pg_ctl start -D robin
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000003.history&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: entering standby mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000003.history&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000003000000000000000A&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: restored log file &amp;#34;00000002.history&amp;#34; from archive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: consistent recovery state reached at 0/A0000A0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: redo starts at 0/A0000A0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: database system is ready to accept read only connections&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# LOG: started streaming WAL from primary at 0/B000000 on timeline 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Depuis la version 12, le paramètre &lt;code&gt;recovery_target_timeline&lt;/code&gt; est défini sur la
valeur &lt;code&gt;latest&lt;/code&gt; par défaut, ce qui permet à l&amp;rsquo;instance secondaire de détecter le
saut de &lt;em&gt;timeline&lt;/em&gt; provoqué par une promotion et de raccrocher correctement les
transactions à répliquer avant de se connecter en &lt;em&gt;streaming&lt;/em&gt; à l&amp;rsquo;instance primaire.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;L&amp;rsquo;architecture proposée répond à plusieurs problématiques assez fréquentes mais
présente un certain nombre d&amp;rsquo;inconvénients. À travers cet article, nous avons
parcouru l&amp;rsquo;ensemble des outils disponibles nativement avec PostgreSQL.&lt;/p&gt;
&lt;p&gt;Comme rappelé précédemment, il est fortement conseillé de décentraliser la zone
d&amp;rsquo;archivage sur un système de fichiers redondé pour se prévenir de l&amp;rsquo;absence
d&amp;rsquo;un nœud. Le diagramme suivant présenterait alors le moins de risque possible
tout en assurant un niveau de service acceptable, avec un minimum d&amp;rsquo;actions en
cas de bascule :&lt;/p&gt;
&lt;p&gt;&lt;img src="https://fljd.in/img/fr/2019-12-19-architecture-complete.png" alt="Architecture complète"&gt;&lt;/p&gt;
&lt;p&gt;La détection de panne et la bascule automatique sont des thématiques récurrentes
lorsque l&amp;rsquo;on exprime un besoin de haute-disponibilité. Durant l&amp;rsquo;année 2019, une
série d&amp;rsquo;outils tiers ont assis leur réputation avec notamment &lt;a href="https://patroni.readthedocs.io/en/latest/" target="_blank" rel="noopener"&gt;patroni&lt;/a&gt; de
Zalando qui propose intelligemment l&amp;rsquo;usage de &lt;code&gt;pg_rewind&lt;/code&gt; dans son fonctionnement.&lt;/p&gt;</description></item><item><title>Construire une instance standby avec pgBackRest</title><link>https://fljd.in/2019/11/07/construire-une-instance-standby-avec-pgbackrest/</link><pubDate>Thu, 07 Nov 2019 16:00:00 +0200</pubDate><author>Florent Jardin</author><guid>https://fljd.in/2019/11/07/construire-une-instance-standby-avec-pgbackrest/</guid><description>&lt;p&gt;Pour cette démonstration, j&amp;rsquo;utilise le système Debian et dispose de deux instances
en version 12, d&amp;rsquo;un serveur de sauvegarde et du paquet &lt;code&gt;pgbackrest&lt;/code&gt; pour mettre
en place une réplication et observer les nouveautés concernant la &lt;a href="https://paquier.xyz/postgresql-2/postgres-12-recovery-change/" target="_blank" rel="noopener"&gt;disparition&lt;/a&gt;
du fichier &lt;code&gt;recovery.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Pour faciliter le transfert des WAL et des sauvegardes PITR, j&amp;rsquo;ajoute un compte
utilisateur &lt;code&gt;pgbr&lt;/code&gt; sur le serveur de sauvegarde, accessible à partir des deux
instances avec un partage de clé SSH. &lt;em&gt;(Conseil : toujours externaliser les
sauvegardes de bases de données sur un système et un stockage indépendant de
l&amp;rsquo;infrastructure de production PostgreSQL…)&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo useradd -s /bin/bash -md /etc/pgbackrest pgbr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mv /etc/pgbackrest.conf /etc/pgbackrest/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown pgbr: /etc/pgbackrest/pgbackrest.conf &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /var/log/pgbackrest /var/lib/pgbackrest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La réplication par flux (&lt;em&gt;streaming&lt;/em&gt;) est assurée par un compte dédié nommé
&lt;code&gt;streamer&lt;/code&gt;, autorisé à se connecter sur les instances PostgreSQL du sous-réseau
grâce à l&amp;rsquo;ajout d&amp;rsquo;une entrée dans le fichier &lt;code&gt;pg_hba.conf&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u postgres createuser --replication --pwprompt streamer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# pg_hba.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Allow replication connections from trusted subnet, by a user with the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replication privilege.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;host replication streamer 10.1.0.0/28 md5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Le fichier &lt;code&gt;.pgpass&lt;/code&gt; est déposé sur chaque serveur pour le compte &lt;code&gt;postgres&lt;/code&gt;,
afin d&amp;rsquo;assurer la connexion du compte &lt;code&gt;streamer&lt;/code&gt; sans saisie de mot de passe.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Dans ce scénario, les archives générées par l&amp;rsquo;instance primaire seront déplacées
sur le serveur de sauvegarde et l&amp;rsquo;instance standby les consultera au besoin. La
planification des sauvegardes sera sous la responsabilité de l&amp;rsquo;utilisateur &lt;code&gt;pgbr&lt;/code&gt;
avec une administration distante. La configuration fine se découpe dans les
fichiers suivants.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /etc/pgbackrest/bkp1.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[lab]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg1-host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;lab1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg2-host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;lab2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg1-host-user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg2-host-user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg1-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/postgresql/12/lab1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg2-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/postgresql/12/lab2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg1-host-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/pgbackrest/lab1.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg2-host-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/pgbackrest/lab2.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[global]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;start-fast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;log-level-console&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;repo1-retention-full&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;repo1-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/pgbackrest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /etc/pgbackrest/lab1.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[lab]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg1-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/postgresql/12/lab1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[global]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;repo1-host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;bkp1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;repo1-host-user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgbr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /etc/pgbackrest/lab2.conf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[lab]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;pg1-path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/var/lib/postgresql/12/lab2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;recovery-option&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;recovery_target_timeline=latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;recovery-option&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;primary_conninfo=host=lab1 port=5432 user=streamer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[global]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;repo1-host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;bkp1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;repo1-host-user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;pgbr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Avant de pouvoir créer la stanza, il est nécessaire de démarrer l&amp;rsquo;instance secondaire
en mode &lt;code&gt;standby&lt;/code&gt; ; il suffit de positionner un fichier &lt;code&gt;standby.signal&lt;/code&gt; dans
le répertoire de données et de redémarrer l&amp;rsquo;instance :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_ctlcluster stop &lt;span class="m"&gt;12&lt;/span&gt; lab2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u postgres touch /var/lib/postgresql/12/lab1/standby.signal
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_ctlcluster start &lt;span class="m"&gt;12&lt;/span&gt; lab2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;À partir de ce moment, et même si les deux instances ne partagent pas les mêmes
données, je peux créer la stanza avec l&amp;rsquo;outil &lt;code&gt;pgbackrest&lt;/code&gt; sur mon serveur de
sauvegarde avec l&amp;rsquo;utilisateur dédié.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u pgbr pgbackrest stanza-create &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --stanza&lt;span class="o"&gt;=&lt;/span&gt;lab --config&lt;span class="o"&gt;=&lt;/span&gt;/etc/pgbackrest/bkp1.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Dès ce moment, mon instance, consciente de l&amp;rsquo;espace de stockage distant, peut
envoyer ses archives de WAL &lt;em&gt;via&lt;/em&gt; la commande &lt;code&gt;archive-push&lt;/code&gt; de &lt;code&gt;pgbackrest&lt;/code&gt;.
Un redémarrage est requis pour activer le mode archive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SYSTEM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;archive_mode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SYSTEM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;archive_command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pgbackrest archive-push %p --stanza=lab --config=/etc/pgbackrest/lab1.conf&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et pour finir, une première sauvegarde complète peut être lancée sur le serveur
de sauvegarde pour assurer la construction de l&amp;rsquo;instance standby.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u pgbr pgbackrest backup --stanza&lt;span class="o"&gt;=&lt;/span&gt;lab --config&lt;span class="o"&gt;=&lt;/span&gt;/etc/pgbackrest/bkp1.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u pgbr pgbackrest info
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# stanza: lab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# status: ok&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cipher: none&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# db (current)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# wal archive min/max (12-1): 000000010000000000000008/000000010000000000000008&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# full backup: 20191107-103443F&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# timestamp start/stop: 2019-11-07 10:34:43 / 2019-11-07 10:35:09&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# wal start/stop: 000000010000000000000008 / 000000010000000000000008&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# database size: 23.5MB, backup size: 23.5MB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# repository size: 2.8MB, repository backup size: 2.8MB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;La sauvegarde de l&amp;rsquo;instance primaire peut être restaurée sur la seconde instance
avec les options &lt;code&gt;--delta&lt;/code&gt; et &lt;code&gt;--type=standby&lt;/code&gt; pour écraser les fichiers erronés
et ajouter le descripteur &lt;code&gt;standby.signal&lt;/code&gt; dans le répertoire de données. pgBackRest
se charge de configurer les options de réplication dans le fichier
&lt;code&gt;postgresql.auto.conf&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_ctlcluster stop &lt;span class="m"&gt;12&lt;/span&gt; lab2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u postgres pgbackrest restore --stanza&lt;span class="o"&gt;=&lt;/span&gt;lab &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --delta --type&lt;span class="o"&gt;=&lt;/span&gt;standby --config&lt;span class="o"&gt;=&lt;/span&gt;/etc/pgbackrest/lab2.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_ctlcluster start &lt;span class="m"&gt;12&lt;/span&gt; lab2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tadaa ! Une connexion est alors établie entre les deux nœuds et l&amp;rsquo;on constate
que l&amp;rsquo;utilisateur &lt;code&gt;streamer&lt;/code&gt; rejoue en asynchrone les transactions de l&amp;rsquo;instance
primaire vers l&amp;rsquo;instance standby.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_stat_replication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- -[ RECORD 1 ]----+------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- pid | 8893
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- usesysid | 16384
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- usename | streamer
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- application_name | 12/lab2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- client_addr | 10.1.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- client_hostname |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- client_port | 38820
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- backend_start | 2019-11-07 10:50:22.424464+00
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- backend_xmin |
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- state | streaming
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- sent_lsn | 0/A000060
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- write_lsn | 0/A000060
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- flush_lsn | 0/A000060
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- replay_lsn | 0/A000060
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- write_lag | 00:00:00.001095
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- flush_lag | 00:00:00.004415
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- replay_lag | 00:00:00.004696
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- sync_priority | 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- sync_state | async
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- reply_time | 2019-11-07 12:39:59.595595+00
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="message"&gt;&lt;p&gt;Pour la rédaction de cet article, je n&amp;rsquo;ai pas véritablement utilisé trois serveurs,
mais bien un seul en réalité. L&amp;rsquo;astuce pour faire tourner deux instances sur le
même port 5432 consiste à ajouter des IP virtuelles sur l&amp;rsquo;interface du serveur
et de faire résoudre les noms de machines par le fichier &lt;code&gt;/etc/hosts&lt;/code&gt; local.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ip add add 10.1.0.1/28 dev ens6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ip add add 10.1.0.2/28 dev ens6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ip add add 10.1.0.3/28 dev ens6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Les instances doivent ensuite être installées/configurées avec les bons paramètres
&lt;code&gt;listen_addresses&lt;/code&gt; et &lt;code&gt;unix_socket_directories&lt;/code&gt; comme suivent :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get install -y postgresql-common
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo vim /etc/postgresql-common/createcluster.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get install -y postgresql-12
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -u postgres mkdir -p /var/run/postgresql/lab{1,2}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_createcluster 12 lab1 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --pgoption listen_addresses=&amp;#39;10.1.0.1&amp;#39; --pgoption port=5432 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --pgoption unix_socket_directories=/var/run/postgresql/lab1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_createcluster 12 lab2 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --pgoption listen_addresses=&amp;#39;10.1.0.2&amp;#39; --pgoption port=5432 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --pgoption unix_socket_directories=/var/run/postgresql/lab2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_ctlcluster 12 lab1 start
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pg_ctlcluster 12 lab2 start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quelques ajustements de droits sur le répertoire &lt;code&gt;/tmp/pgbackrest&lt;/code&gt; pour les
fichiers de verrous (paramètre &lt;code&gt;--lock-path&lt;/code&gt;) et le tour est joué !&lt;/p&gt;
&lt;/div&gt;</description></item></channel></rss>