LOXODATA

Enrichir ses connaissances de PostgreSQL

2017-08-17   1379 mots, 7 minutes de lecture   Lætitia AVROT

Il arrive parfois qu’on se pose des questions très pointues sur le fonctionnement de notre SGBDR préféré. Plutôt que de rester avec des questions sans réponse, voici 4 méthodes pour enrichir vos connaissances de PostgreSQL.

Pour l’exemple de cet article, nous nous poserons la question suivante :

Lors d’une restauration (« recovery » en anglais) avec une archive_command correctement configurée, si un fichier WAL est disponible à la fois dans les archives et dans le répertoire pg_xlog, lequel est utilisé en priorité ?

Regarder dans la documentation

Effectivement, la première source reste la documentation. Voyons si nous trouvons la réponse quelque part sur le fonctionnement de la restauration. Je ne regarde que la documentation de la version 9.6.

Je commence par regarder le chapitre 27. Recovery Configuration. Ce chapitre présente et explique les différents paramètres, mais ne rentre pas dans les détails de l’exécution de la restauration.

Je cherche ensuite dans le chapitre 30.1 Reliability. Là encore, même si le sujet de la restauration est évoqué, rien n’indique le fonctionnement dans le cas qui nous concerne.

Je cherche enfin dans le chapitre 25.3. Continuous Archiving and Point-in-Time Recovery (PITR). Et je trouve cette phrase qui répond à la question :

WAL segments that cannot be found in the archive will be sought in pg_xlog/; this allows use of recent un-archived segments. However, segments that are available from the archive will be used in preference to files in pg_xlog/.

Ce sont donc les WAL archivés qui sont utilisé de préférence sur les WALs disponibles dans le répertoire pg_xlog. Problème résolu !

Regarder dans le code

Mais où continuer à chercher si je n’ai pas trouvé la réponse dans la documentation ?

Il faut aller voir dans le code. Nous avons la chance de travailler avec un outil open source, c’est l’occasion d’ouvrir le capot !

Le code de PostgreSQL est fabuleusement propre et extrêmement bien documenté. N’hésitez pas à aller y faire un tour, je suis sûre que vous serez agréablement surpris.

Pour terminer cet aparté sur le code de PostgreSQL, si vous arrivez dans le code PostgreSQL, je vous suggère de lire les différents fichier README. C’est un excellent moyen d’en apprendre plus sur PostgreSQL.

La première étape consiste à récupérer le code en local. Plusieurs méthodes sont possible :

Pour cet article, j’ai travaillé à partir de la version 9.6.3 de PostgreSQL.

Pour faire une recherche dans le code PostgreSQL, j’utilise grep. Vous pouvez utiliser l’outil de votre choix, mais personnellement, je n’ai pas encore trouvé mieux. Sans surprise, c’est le répertoire src qui comporte les sources.

Le plus dur est de trouver un mot-clé efficace qui me rapporte des résultats mais pas trop. Dans mon cas, je suis sûre que le mot-clé recovery est beaucoup trop large car je l’ai déjà trouvé de nombreuses fois dans les commentaires, lors de mes lectures du code. Vous pouvez aussi simplement vous en rendre compte en lançant grep sur ce mot-clé. N’oubliez pas d’exclure les fichiers .po qui sont les fichiers de traduction de PostgreSQL. J’ajoute l’option --directories=recurse car je souhaite faire une recherche récursive sur tout un répertoire.

cd src
grep restore_command --directories=recurse --exclude=*.po .

Voici le résultat que cela me donne :

./test/perl/PostgresNode.pm:Restoring WAL segments from archives using restore_command can be enabled
./test/perl/PostgresNode.pm:restore_command = '$copy_command'
./backend/replication/README:restorable using restore_command, it starts up the walreceiver process
./backend/postmaster/startup.c:static volatile sig_atomic_t in_restore_command = false;
./backend/postmaster/startup.c:	if (in_restore_command)
./backend/postmaster/startup.c:	 * Set in_restore_command to tell the signal handler that we should exit
./backend/postmaster/startup.c:	in_restore_command = true;
./backend/postmaster/startup.c:	in_restore_command = false;
./backend/access/transam/xlogarchive.c:	/* In standby mode, restore_command might not be supplied */
./backend/access/transam/xlogarchive.c:	 * from archive because the restore_command may inadvertently restore
./backend/access/transam/xlog.c:	XLOG_FROM_ARCHIVE,			/* restored using restore_command */
./backend/access/transam/xlog.c:		if (strcmp(item->name, "restore_command") == 0)
./backend/access/transam/xlog.c:					(errmsg_internal("restore_command = '%s'",
./backend/access/transam/xlog.c:					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
./backend/access/transam/xlog.c:					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
./backend/access/transam/xlog.c:		 * before reaching this point; e.g. because restore_command or
./backend/access/transam/xlog.c: * The segment can be fetched via restore_command, or via walreceiver having
./backend/access/transam/recovery.conf.sample:# restore_command
./backend/access/transam/recovery.conf.sample:#restore_command = ''		# e.g. 'cp /mnt/server/archivedir/%f %p'
./backend/access/transam/recovery.conf.sample:# This can be useful for cleaning up after the restore_command.
./backend/access/transam/recovery.conf.sample:# restore_command and/or primary_conninfo.

D’après les résultats, il me semble que je trouverai des choses intéressantes dans le fichier ./backend/access/transam/xlog.c. (On peut aussi se tromper et devoir faire plusieurs fichiers.)

Dans le fichier ./backend/access/transam/xlog.c, je trouve ce code (que j’ai épuré) :

if (lastSourceFailed)
{
[...]
}
else if (currentSource == XLOG_FROM_PG_XLOG)
{
        /*
            * We just successfully read a file in pg_xlog. We prefer files in
            * the archive over ones in pg_xlog, so try the next file again
            * from the archive first.
            */
        if (InArchiveRecovery)
                currentSource = XLOG_FROM_ARCHIVE;
}

Le commentaire indique clairement qu’on préfère les fichiers archivés à ceux présents dans pg_xlog. Problème résolu !

Créer un cas de test

Je vais créer un cas de test de toutes pièces me permettant de savoir quel fichier est préféré. Pour ce faire, je monte une instance PostrgreSQL de test et j’active l’archivage. Je fais une sauvegarde avec pg_basebackup, je switche quelques fichiers WAL et j’arrête l’instance.

Je tente ensuite une restauration en copiant ma sauvegarde dans PGDATA. Je prends soin de mettre dans le répertoire pg_xlog mes fichiers WALs archivés. Dans le répertoire des archives, je “corromps” un des fichiers WAL en ouvrant le fichier et en ajoutant et/ou supprimant des caractères au hasard (c’est efficace).

Puis, je crée le fichier recovery.conf et je redémarre l’instance.

Les logs de l’instance me donnent ça :

<2017-07-09 18:19:51 CEST - 3065 - >LOG:  début de la restauration de l'archive
<2017-07-09 18:19:51 CEST - 3065 - >LOG:  restauration du journal de transactions « 00000002.history » à partir de l'archive
<2017-07-09 18:19:51 CEST - 3065 - >LOG:  restauration du journal de transactions « 000000020000000000000099 » à partir de l'archive
<2017-07-09 18:19:51 CEST - 3094 - postgres>FATAL:  le système de bases de données se lance
<2017-07-09 18:19:51 CEST - 3065 - >LOG:  la ré-exécution commence à 0/99000028
<2017-07-09 18:19:51 CEST - 3065 - >LOG:  état de restauration cohérent atteint à 0/99000130
<2017-07-09 18:19:51 CEST - 3065 - >LOG:  restauration du journal de transactions « 00000002000000000000009A » à partir de l'archive
<2017-07-09 18:19:52 CEST - 3101 - postgres>FATAL:  le système de bases de données se lance
<2017-07-09 18:19:52 CEST - 3065 - >FATAL:  le fichier d'archive « 00000002000000000000009B » a la mauvaise taille : 16777223 au lieu de 16777216
<2017-07-09 18:19:52 CEST - 3063 - >LOG:  processus de lancement (PID 3065) quitte avec le code de sortie 1
<2017-07-09 18:19:52 CEST - 3063 - >LOG:  arrêt des autres processus serveur actifs
<2017-07-09 18:19:52 CEST - 3063 - >LOG:  le système de base de données est arrêté
<2017-07-09 18:21:29 CEST - 3140 - >LOG:  le système de bases de données a été interrompu lors d'une récupération à 2017-07-09 17:43:19 CEST
	(moment de la journalisation)
<2017-07-09 18:21:29 CEST - 3140 - >ASTUCE :  Si c'est arrivé plus d'une fois, des données ont pu être corrompues et vous
	pourriez avoir besoin de choisir une cible de récupération antérieure.

La restauration n’ayant pas fonctionné pour cause de fichier corrompu, c’est qu’il n’a pas regardé les fichiers présents dans pg_xlog, mais ceux archivés. Le processus de recovery préfère donc aller chercher les WAL archivés à ceux présents dans pg_xlog. Problème résolu !

Demander à mon cercle de confiance

En tant qu’informaticienne, je trouve très important d’avoir un cercle de pairs à qui je peux poser ce genre de question. Effectivement, vous ne pouvez pas tout savoir, mais parmi les personnes que vous connaissez, il y a des chances que quelqu’un le sache. Il suffit donc de demander.

Pourquoi ai-je mis ce point en dernier ? Pour deux raisons :

  • d’abord parce que par expérience je sais que je retiens quelque chose d’autant mieux que je me suis arraché les cheveux avant pour essayer de trouver comment ça marchait 
  • et ensuite parce que je trouve que c’est le plus élémentaire des respects envers mes interlocuteurs, que de chercher avant de poser une question.

Conclusion

Voici donc plusieurs méthodes vous permettant d’enrichir vos connaissances sur PostgreSQL, et gratuitement en plus ! Ce serait dommage de rester avec des questions non résolues, non ?