Reviers, le 10 février 2025

Cela fait plusieurs mois maintenant que nous avons envoyé des patchs sur la liste de discussion des développeurs de PostgreSQL pour améliorer la supervision de la parallélisation. J’avais promis de revenir vers vous à ce sujet. Je l’ai fait lors d’une conférence à la pgsession 17 et je vais revenir rapidement ici sur ce sujet pour ceux et celles qui n’ont pas pu y assister.

Guillaume Lelarge

En septembre 2024, Benoît et moi avons envoyé quatre patchs pour améliorer la supervision de la parallélisation d’une requête. Détaillons ces patchs et le retour qu’ils ont reçu.

Patch #1 : traces supplémentaires

L’idée des quatre participants (Benoît, Franck, Jehan-Guillaume et moi) du cowork de Nantes est d’avoir une trace permettant de savoir quand la parallélisation d’un nœud d’une requête a été demandée, le niveau de parallélisation demandé (nombre de workers planifiés), et le niveau de parallélisation atteint (nombre de workers réellement exécutés). Voici un exemple de la trace proposée :

LOG:  1 parallel nodes planned (1 obtained all their workers, 0 obtained none), 2 workers planned (2 workers launched)

Savoir qu’il n’y a aucune requête parallélisée ou, au contraire, savoir qu’il y en a plein, peut aider à quantifier les ressources allouées à la machine (ici, le nombre de CPU).

Savoir qu’il y a eu moins de workers exécutés que planifiés peut démontrer un problème de configuration (notamment sur les paramètres max_worker_processes, max_parallel_workers et max_parallel_workers_per_gather).

À Dalibo, on tient à conseiller nos clients en nous basant sur des constatations et force est d’avouer que cette trace supplémentaire nous aiderait grandement.

Ces traces ont donc un intérêt et le patch a logiquement suscité un intérêt de la part de la communauté. Suite aux réactions, le patch a été amélioré. Il en est aujourd’hui à sa version 6 et est divisé en plusieurs parties :

  • V6_0001-Add-a-guc-for-parallel-worker-logging.patch ajoute un paramètre de configuration (log_parallel_workers) permettant de contrôler la trace sur la parallélisation avec trois options : désactivé, activé pour toutes les opérations, activé seulement quand des workers manquaient.

  • V6_0002-Implements-logging-for-parallel-worker-usage-in-inde.patch ajoute la trace sur les requêtes CREATE INDEX (attention, seuls les index B-tree et BRIN peuvent profiter de la parallélisation à leur création).

  • V6_0003-Setup-counters-for-parallel-vacuums.patch ajoute les compteurs de parallélisation pour tracer la parallélisation des requêtes VACUUM.

  • V6_0004-Implements-logging-for-parallel-worker-usage-in-vacu.patch ajoute les traces sur la parallélisation des requêtes VACUUM.

  • V6_0005-Implements-logging-for-parallel-worker-usage-in-quer.patch ajoute les traces sur la parallélisation des requêtes SELECT (les compteurs ont été ajoutés par un autre patch dont nous parlerons après).

La trace est devenue plus simple :

launched 3 parallel workers (planned: 4)

Le but de la division en plusieurs patchs est de mieux comprendre la structure du patch global, de faciliter sa relecture, puis de permettre au commiter d’intégrer uniquement les parties qui lui semblent intéressantes et finalisées, si le patch global nécessite encore des discussions. Cela donne ainsi plus de chance au développeur de voir au moins une partie de son patch intégrée.

Actuellement, ce patch n’a pas été intégré. Il fait partie du commit fest de janvier, à l’état Needs review.

Patch #2 : pg_stat_database

Cette vue nous intéresse pour cumuler par base le nombre de workers de parallélisation planifiés et le nombre de ceux réellement exécutés. Le patch initial proposait quatre nouvelles colonnes, deux pour les requêtes SELECT et deux pour les requêtes DDL (actuellement, seules les commandes DDL CREATE INDEX et VACUUM sont parallélisables).

Là aussi, une bonne discussion a eu lieu sur l’intérêt de ces différentes colonnes. Autant la présence de colonnes pour les requêtes SELECT ne suscite pas trop d’objections, autant celle des colonnes pour les requêtes DDL est fortement contestée.

L’argument principal contre ces colonnes est qu’un CREATE INDEX et un VACUUM sont des opérations manuelles et que l’opérateur peut voir lui-même quand il lance l’opération si cette opération est parallélisée. Pour moi, cet argument a du sens. Pour les « petits malins » qui objecteront qu’un VACUUM est automatisé via le processus autovacuum, je répondrais qu’ils ont bien raison, mais que ce VACUUM automatisé n’utilise pas la clause PARALLEL qui permettrait sa parallélisation. Pour le dire autrement, l’autovacuum ne peut pas exécuter un VACUUM parallélisé actuellement.

Donc l’argument est difficilement contestable et, de ce fait, seule la moitié du patch a été appliquée dans ce commit. Et voici le résultat lorsqu’on interroge ces nouvelles colonnes :

SELECT datname, parallel_workers_to_launch, parallel_workers_launched
FROM pg_stat_database
WHERE datname IS NOT null;

  datname  | parallel_workers_to_launch | parallel_workers_launched
-----------+----------------------------+---------------------------
 postgres  |                         50 |                        45
 template1 |                          0 |                         0
 template0 |                          0 |                         0
(3 rows)

Patch #3 : pg_stat_statements

La vue pg_stat_statements donne des informations sur les requêtes exécutées. Connaître la durée d’exécution, le nombre d’exécutions, l’utilisation du cache ou de JIT sur ces requêtes aide beaucoup à leur optimisation. Il nous a donc semblé intéressant d’ajouter des informations sur la parallélisation par requête.

Le premier patch était un peu naïf, proposant ainsi sept nouvelles colonnes. Beaucoup ont été contestées et au final, le patch, en sa version 3, a été divisé en deux parties, bien plus modestes :

  • v3-0001-Introduce-two-new-counters-in-EState.patch ajoute les compteurs de parallélisation pour tracer la parallélisation des requêtes SELECT (ces compteurs sont aussi utilisés par les nouvelles colonnes de la vue pg_stat_database).

  • v3-0002-Add-parallel-columns-to-pg_stat_statements.patch ajoute les deux colonnes acceptées dans pg_stat_statements.

Ces deux parties ont été acceptées et intégrées (commit du patch v3-0001, et commit du patch v3-0002). Ce que je retire de la discussion sur ce patch, c’est qu’il faut proposer un nombre très limité de changements à la fois pour qu’il y ait une chance que ce soit accepté. Et de faire ça plusieurs fois si nécessaire, en avançant petit à petit, par itération.

Voici le résultat lorsqu’on interroge ces deux nouvelles colonnes :

SELECT query, parallel_workers_to_launch, parallel_workers_launched
FROM pg_stat_statements
WHERE query LIKE 'SELECT%t1%';

                query                | parallel_workers_to_launch | parallel_workers_launched
-------------------------------------+----------------------------+---------------------------
 SELECT count(*) FROM t1             |                          2 |                         2
 SELECT count(*) FROM t1 WHERE id>$1 |                          4 |                         4
 SELECT * FROM t1                    |                          0 |                         0
(3 rows)

Patch #4 : pg_stat_all_tables et pg_stat_all_indexes

Je ne vais pas m’éterniser sur ce patch. Il m’avait semblé intéressant de connaître les tables qui étaient parcourues en parallélisé, pour améliorer la configuration spécifique des tables en question avec un :

ALTER TABLE ... WITH (parallel_workers=X);

Le retour a été unanime. L’intérêt est très limité, voire nul. Je reconnais qu’il est limité, mais je réfute qu’il est nul. Ceci étant dit, je n’ai pas d’arguments supplémentaires pour défendre cette position, et j’ai donc préféré consacrer mon énergie à l’argumentation pour les autres patchs, notamment celui sur les traces.

Ce patch n’est officiellement pas rejeté, il serait certainement mieux que je le déclare abandonné.

Pour finir

Deux patchs acceptés sur quatre, et partiellement en plus. C’est une demi-victoire. Il n’empêche que nous aurons ainsi plus de métriques sur la parallélisation et que cela pourrait nous aider à améliorer sa configuration. Et rien n’empêche de revenir plus tard avec les bouts non acceptés et quelques arguments supplémentaires, venant de l’expérience rencontrée chez nos clients.

Tout le travail réalisé pour écrire un patch et réussir à le faire intégrer peut sembler lourd mais la qualité du code et sa stabilité sont à ce prix.

Cet article sera mis à jour quand les vidéos de la pgsession 17 seront disponibles pour y mettre l lien vers ma conférence.


DALIBO

DALIBO est le spécialiste français de PostgreSQL®. Nous proposons du support, de la formation et du conseil depuis 2005.