Compiler et patcher avec pgenv
- 7 minutes de lecture
Parmi les quelques outils de mon quotidien, il y en a un très sobre et bigrement efficace répondant au nom de pgenv, un gestionnaire des versions PostgreSQL. Ce projet est publié sous licence MIT par David E. Wheeler, auteur de l’extension pgTAP dont j’avais déjà vanté les mérites dans un autre article.
Cet outil concerne principalement les contributeur⋅rices au projet PostgreSQL et les
quelques DBA féru⋅es d’expérimentations, car pgenv
permet de compiler et
d’exécuter toutes les versions majeures et mineures du système de base de données
open-source le plus avancé du monde.
À l’épreuve de la compilation
PostgreSQL est particulièrement simple à compiler. Avec un poste de travail
sous Unix, GNU/Linux ou BSD et quelques dépendances, à savoir gcc
, make
,
patch
et git
, il est facile d’exécuter une instance dans la version cible de son
choix.
$ git clone git://git.postgresql.org/git/postgresql.git
$ cd postgresql
$ git checkout REL_13_1
$ export PREFIX=/tmp/postgres/devel
$ ./configure --prefix=$PREFIX
$ make && make install
$ cd contrib
$ make && make install
Dès lors que les librairies et les binaires sont disponibles, il est très aisé de contruire sa première instance et de s’y connecter !
$ export PATH=$PREFIX/bin:$PATH
$ export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH
$ export PGDATA=/tmp/postgres/data
$ initdb --username $(whoami) --auth=peer --data-checksums
$ pg_ctl start --log=$PGDATA/server.log
$ createdb $(whoami)
$ psql -tc "select version()"
PostgreSQL 13.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 10.2.0, 64-bit
$ pg_ctl stop
Le faire à la main m’a amusé quelques minutes et écrire un script pour automatiser
le déploiement des versions mineures à la demande m’a vite traversé l’esprit.
Ne réinventons pas la roue et voyons ce que propose pgenv
!
Un script pour les compiler tous
La page d’accueil du projet reprend l’installation rapide du script dans votre
sous-répertoire ~/.pgenv
. Téléchargeons et compilons la version qui nous interesse.
$ pgenv available
Available PostgreSQL Versions
================================================
...
PostgreSQL 10
------------------------------------------------
10.0 10.1 10.2 10.3 10.4 10.5
10.6 10.7 10.8 10.9 10.10 10.11
10.12 10.13 10.14 10.15
PostgreSQL 11
------------------------------------------------
11.0 11.1 11.2 11.3 11.4 11.5
11.6 11.7 11.8 11.9 11.10
PostgreSQL 12
------------------------------------------------
12.0 12.1 12.2 12.3 12.4 12.5
PostgreSQL 13
------------------------------------------------
13beta1 13beta2 13beta3 13rc1 13.0 13.1
Comme pour mon précédent exemple, je réinstalle une version 13.1 avec pgenv
à l’aide de l’option build
. Le script déploie également les librairies de contrib
et la documentation.
$ export PGENV_ROOT=/var/lib/pgenv
$ export PATH=$PGENV_ROOT/pgsql/bin:$PATH
$ export LD_LIBRARY_PATH=$PGENV_ROOT/pgsql/lib:$LD_LIBRARY_PATH
$ pgenv build 13.1
PostgreSQL, contrib, and documentation installation complete.
pgenv configuration written to file /var/lib/pgenv/.pgenv.13.1.conf
PostgreSQL 13.1 built
On retrouve dans l’arborescence $PGENV_ROOT
, la présence de l’archive .tar.bz2
du projet, requise pour l’étape de compilation. Le $PREFIX
quant à lui, est
automatiquement positionné sur le répertoire $PGENV_ROOT/pgsql-13.1
.
/var/lib/pgenv
├── .pgenv.13.1.conf
├── pgsql-13.1
│ ├── bin
│ ├── include
│ ├── lib
│ └── share
└── src
├── postgresql-13.1
└── postgresql-13.1.tar.bz2
Pour être fidèle à ma première partie, je vais configurer correctement les
paramètres de la commande initdb
dans le fichier de configuration dédié à la
version 13.1.
$ pgenv config edit 13.1
# Path to the cluster log file (mandatory)
PGENV_LOG="$PGENV_ROOT/pgsql/data/server.log"
# Initdb flags
PGENV_INITDB_OPTS="--username $(whoami) --auth=peer --data-checksums"
Ainsi, lors de la première utilisation de cette version 13.1, pgenv
va lancer
la commande initdb
pour alimenter le répertoire de données avec mon compte
comme propriétaire et démarrer le processus postgres
.
$ pgenv use 13.1
Using PGENV_ROOT /var/lib/pgenv
Data page checksums are enabled.
Success. You can now start the database server using:
/var/lib/pgenv/pgsql/bin/pg_ctl -D /var/lib/pgenv/pgsql/data -l logfile start
PostgreSQL 13.1 started
Logging to /var/lib/pgenv/pgsql/data/server.log
$ createdb $(whoami)
$ psql -tc "select version()"
PostgreSQL 13.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 10.2.0, 64-bit
Et avec ceci ?
Comme indiqué en introduction, l’intérêt d’un tel gestionnaire réside dans sa capacité d’installer plusieurs versions différentes dans la même arborescence et de basculer de l’une à l’autre.
Imaginons que nous souhaitons disposer d’une version 10 de PostgreSQL avec le
même genre de configuration que la version 13 précédente. pgenv
supporte un
fichier d’environnement global, nommé .pgenv.conf
, que je reconstruis à
partir de mon précédent fichier d’instance 13.1.
$ cp $PGENV_ROOT/.pgenv.13.1.conf $PGENV_ROOT/.pgenv.conf
$ pgenv build 10.15
$ pgenv use latest 10
$ createdb $(whoami)
$ psql -c "show data_checksums"
data_checksums
----------------
on
(1 row)
Nous nous retrouvons bien avec un instance dont les sommes de contrôle ont été
activées, grâce à l’option PGENV_INITDB_OPTS
citée plus haut.
Je m’étais questionné sur la capacité de pgenv
de lancer simultanément deux
environnements pour mettre en place de la réplication logique, par exemple.
Conclusion, il s’agit d’une des limites de l’outil, puisque ce n’est pas
son but premier. Et pour cause, à chaque fois que l’on appelle la commande
pgenv use
, le script arrête l’instance courante avant de basculer sur la
deuxième.
$ pgenv use latest 10
Using PGENV_ROOT /var/lib/pgenv
PostgreSQL 13.1 stopped
PostgreSQL 10.15 started
Logging to /var/lib/pgenv/pgsql/data/server.log
En complément, pgenv
met en place un lien symbolique dans la racine $PGENV_ROOT
à chaque changement de version courante. Ce lien a été ajouté au préalable
dans la variable $PATH
pour garantir la bonne compatibilité des binaires avec
les données.
/var/lib/pgenv
├── .pgenv.13.1.conf
├── .pgenv.conf
├── pgsql -> pgsql-10.15
├── pgsql-10.15
├── pgsql-13.1
└── src
Ce lien symbolique nous oblige à manipuler toutes les autres instances avec des
chemins absolus, une surcharge de leurs paramètres port
ou listen_addresses
et de faire appel à la bonne version de la commande pg_ctl
. Il est donc possible
de faire de la réplication, mais oubliez pgenv
pour la gestion des processus
d’instances.
Dans la cour des grands
Nous sommes en décembre 2020 à l’heure de la rédaction de cet article, et la communauté PostgreSQL travaille activement sur le développement de la prochaine version 14 du logiciel. Chaque année, les contributeur⋅rices du monde entier se retrouvent en ligne autour du Commitfest pour étudier les nouvelles propositions de fonctionnalités ou de correction de bogues.
En août dernier, Tatsuro Yamada proposait d’enrichir les méta-commandes de
l’invite psql
afin de lister les statistiques étendues rattachées aux
tables de la base courante. Cette fonctionnalité est donc étudiée à travers les
échanges électroniques et suivie sur une page dédiée du Commitfest.
Le contributeur produit alors un fichier .patch
qu’il obtient avec la commande
git diff
et dont le résultat est compatible avec la commande patch. Ainsi,
n’importe quel relecteur peut l’intégrer dans son projet et dérouler ses tests sur
la nouvelle instance compilée.
C’est là qu’intervient une chouette fonctionnalité de l’outil pgenv
. Ce dernier
propose d’appliquer une série de patchs dans une phase préliminaire dès lors qu’on
lui présente un fichier d’index pour la version associée, qui contiendra le chemin
absolu des fichiers à parcourir.
/var/lib/pgenv
└── patch
├── 13
│ └── 0001-Add-dX-command-on-psql-rebased-on-7e5e1bba03.patch
└── index
└── patch.13
Comme on le voit dans mon arborescence, j’ai téléchargé la dernière version
communiquée par le développeur et je l’ai déclarée dans le fichier index.13
.
Lors de la recompilation de la version concernée, on constate que le patch
est bien pris en compte.
$ export PGENV_DEBUG=1
$ pgenv clear
$ pgenv rebuild 13.1
Using PGENV_ROOT /var/lib/pgenv
[DEBUG] Patch index file [/var/lib/pgenv/patch/index/patch.13]
[DEBUG] Applying patch [0001-Add-dX-command-on-psql-rebased-on-7e5e1bba03.patch]
into source tree /var/lib/pgenv/src/postgresql-13.1
Applied patch 0001-Add-dX-command-on-psql-rebased-on-7e5e1bba03.patch
PostgreSQL 13.1 built
Et la fonctionnalité devient disponible sur l’instance !
florent=# \dX
List of extended statistics
Schema | Name | Definition | Ndistinct | Dependencies | MCV
--------+-------+--------------+-----------+--------------+---------
public | stts1 | a, b FROM t1 | | defined |
public | stts2 | a, b FROM t1 | defined | defined |
public | stts3 | a, b FROM t1 | defined | defined | defined
public | stts4 | b, c FROM t2 | defined | defined | defined
(4 rows)
Le retrait des patchs n’est pas supporté par pgenv
mais l’opération reste
triviale avec la commande patch
et son option --reverse
. Cela nous permet
de conserver les sources sans devoir les télécharger à nouveau !
$ cd $PGENV_ROOT/src/postgresql-13.1
$ index=$PGENV_ROOT/patch/index/patch.13
$ for f in $(cat $index); do patch --reverse -p1 < $f; done
patching file doc/src/sgml/ref/psql-ref.sgml
Hunk #1 succeeded at 1903 (offset -15 lines).
patching file src/bin/psql/command.c
Hunk #1 succeeded at 929 (offset 1 line).
patching file src/bin/psql/describe.c
Hunk #1 succeeded at 4377 (offset -24 lines).
patching file src/bin/psql/describe.h
patching file src/bin/psql/help.c
patching file src/bin/psql/tab-complete.c
Hunk #1 succeeded at 1479 (offset -21 lines).
Hunk #2 succeeded at 3771 (offset -127 lines).
Conclusion
Pour tout vous dire, je ne sais plus me séparer de pgenv
sauf en de rares
exceptions où mes tests nécessitent une distribution GNU/Linux spécifique, comme
CentOS ou Debian. Une machine virtuelle fournie par Vagrant est tout aussi
fiable, notamment lorsqu’il s’agit de déboguer un paquet d’installation ou
une dépendance particulière.
$ sudo vagrant box list
centos/7 (libvirt, 2004.01)
debian/buster64 (libvirt, 10.4.0)
debian/stretch64 (libvirt, 9.12.0)