Développons une nouvelle fonctionnalité pour PostgreSQL (10/13)

La réponse de Tom Lane n'a pas traîné. Écoutons la parole de Saint-Thomas :)

You certainly need to do *something* about that. But are you sure there aren't any existing record types that will work? Look at CREATE/DROP DATABASE.

Hmmm, donc si j'essaie de piger, je devrais trouver un enregistrement de WAL pour la suppression du répertoire dans le code correspondant à DROP DATABASE et, du coup, un pour le déplacement de fichiers dans le code correspondant à CREATE DATABASE.

Ouvrons le fichier src/backend/commands/dbcommands.c et recherchons la fonction createdb. À la fin de cette fonction, il y a une boucle sur des tablespaces où la fonction copydir est utilisée. Justement celle que nous utilisons pour copier les fichiers d'un tablespace vers un autre. Et tout de suite après se trouve un code d'ajout d'enregistrement dans les WAL pour la copie de fichiers. Il nous faut donc placer ce code après l'appel à la fonction copydir et le modifier un peu pour qu'il prenne en compte nos propres variables. Après un peu de personnalisation, on se trouve avec ce code :

 xl_dbase_create_rec xlrec;
 XLogRecData rdata1;
 	
 xlrec.db_id = db_id;
 xlrec.tablespace_id = dst_tblspcoid;
 xlrec.src_db_id = db_id;
 xlrec.src_tablespace_id = src_tblspcoid;
 
 rdata0.data = (char *) &xlrec;
 rdata0.len = sizeof(xl_dbase_create_rec);
 rdata0.buffer = InvalidBuffer;
 rdata0.next = NULL;
 
 (void) XLogInsert(RM_DBASE_ID, XLOG_DBASE_CREATE, rdata);

La modification a consisté à changer les variables contenant les OID de la base et des deux tablespaces. Particulièrement simple.

Ensuite, il nous faut lire la fonction dropdb. En fait, le code qui nous intéresse se trouve dans la fonction remove_dbtablespaces qui, comme son nom l'indique, s'occupe de supprimer des tablespaces. Là-aussi, nous trouvons le bout de code qui gère l'enregistrement dans les WAL. Personnalisons-le avec les variables de la fonction movedb :

 xl_dbase_drop_rec xlrec;
 XLogRecData rdata1;
 
 xlrec.db_id = db_id;
 xlrec.tablespace_id = dsttablespace;
 
 rdata0.data = (char *) &xlrec;
 rdata0.len = sizeof(xl_dbase_drop_rec);
 rdata0.buffer = InvalidBuffer;
 rdata0.next = NULL;
 
 (void) XLogInsert(RM_DBASE_ID, XLOG_DBASE_DROP, rdata);

Et voilà ! Après l'étape compilation et installation, le meilleur moyen de savoir si les enregistrements sont bien compris et utilisés est de faire un scénario PITR. Ce dernier est assez simple:

  • mise en place de la configuration pour le PITR ;
  • création d'une base de données ;
  • ajout d'une table dans cette nouvelle base ;
  • insertion de données ;
  • déplacement de cette base entière vers un autre tablespace ;
  • insérer de nouveau des données (démontrant ainsi que le déplacement a bien été intégré par le moteur) ;
  • un SELECT pg_switch_xlog() pour s'assurer d'avoir tous les WAL nécessaires à la restauration.

Ensuite, il faut supprimer le répertoire data et (ne surtout pas oublier) le contenu du répertoire du tablespace. En restaurant l'archive PITR, le déplacement doit aussi avoir eu lieu. Il suffit de vérifier que $PGDATA/base ne contient pas la base de données. Elle ne doit se trouver que dans le répertoire du tablespace.

Le scénario est là, je ne fais pas un copier/coller de tout ça, ça prendrait beaucoup trop de place pour un intérêt assez faible. Sachez néanmoins que les résultats sont très bons. Pour comparaison, je l'avais fait avant mes modifs dans le code et on voyait bien que les WAL manquaient pour gérer copie et suppression.

Bref, le patch est pratiquement terminé. J'ai ensuite modifié le fichier de documentation en SGML de ALTER DATABASE, corrigé deux/trois choses (variables inutilisés, réordonnancement des vérifications, etc.). Le patch est maintenant prêt à être dévoilé. Je viens de l'envoyer sur pgsql-hackers. J'attends les commentaires (avec impatience et appréhension...:) ).

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet