Image de couverture : PostgreSQL 19 : REPACK, la compaction de table sans bloquer vos utilisateurs
tech

PostgreSQL 19 : REPACK, la compaction de table sans bloquer vos utilisateurs

22 April 2026
5 min de lecture
8 vues
Sébastien Muler

PostgreSQL 19 : REPACK, la compaction de table sans bloquer vos utilisateurs

Source originale : Understanding PostgreSQL REPACK Through repack.c — Chao Li, Highgo Software Inc. (avril 2026)

Chaque DELETE ou UPDATE laisse derrière lui des dead tuples : des lignes mortes qui gonflent votre table sans jamais vraiment disparaître. VACUUM les marque réutilisables, VACUUM FULL réécrit la table — mais au prix d'un verrou exclusif qui paralyse toute activité. PostgreSQL 19 introduit REPACK, une troisième voie qui réécrit physiquement la table tout en la gardant accessible pendant presque toute l'opération.

Cet article décrypte le fonctionnement interne de REPACK à travers l'analyse de repack.c, et vous donne une checklist pratique avant de l'utiliser en production.


Pourquoi REPACK et pas VACUUM FULL ?

Critère VACUUM VACUUM FULL REPACK CONCURRENTLY
Libère l'espace OS ✗ (partiellement)
Table accessible en lecture
Table accessible en écriture ✓ (sauf phase finale)
Durée du verrou exclusif Aucun Toute l'opération Quelques secondes

REPACK en mode concurrent fonctionne en trois temps :

  1. Copie d'un snapshot : les tuples vivants sont copiés dans un nouveau fichier heap, basé sur un snapshot de départ.
  2. Replay des changements concurrents : via le décodage logique (logical decoding), toutes les modifications intervenues pendant la copie sont rejouées sur le nouveau fichier.
  3. Lock-and-swap : un verrou exclusif très court est pris le temps d'échanger les relfilenode (le pointeur physique vers le fichier de données) et de mettre à jour les catalogues système.

L'OID de la table, ses privilèges, ses dépendances et ses relations d'héritage restent intacts. Seul le fichier physique sous-jacent est remplacé.


✅ Checklist avant d'exécuter REPACK en production

1. Tester sur un réplica d'abord

Avant tout passage en production, exécutez REPACK sur un réplica de streaming ou une copie de la base :

-- Vérifier le bloat avant
SELECT
  relname,
  pg_size_pretty(pg_relation_size(oid)) AS taille_actuelle,
  n_dead_tup,
  n_live_tup
FROM pg_stat_user_tables
WHERE relname = 'ma_table';

-- Lancer REPACK (PostgreSQL 19+)
REPACK TABLE ma_table CONCURRENTLY;

-- Vérifier après
SELECT pg_size_pretty(pg_relation_size('ma_table'::regclass));

Cela vous donne une estimation réaliste du gain d'espace et de la durée de l'opération.

2. Vérifier que le décodage logique est activé

REPACK CONCURRENTLY s'appuie sur le décodage logique pour rejouer les changements concurrents. Votre instance doit avoir :

# postgresql.conf
wal_level = logical          -- obligatoire
max_replication_slots = 5    -- doit rester > 0 disponible
max_wal_senders = 5

3. Surveiller les métriques clés pendant l'opération

Ouvrez un œil sur ces indicateurs :

  • pg_stat_replication : lag de réplication — REPACK génère du WAL supplémentaire
  • pg_replication_slots : un slot temporaire est créé pendant REPACK CONCURRENTLY ; vérifiez qu'il est bien supprimé en fin d'opération
  • pg_locks : surveillez l'acquisition du verrou AccessExclusiveLock lors du swap final
  • Espace disque : la table est intégralement dupliquée le temps de l'opération — prévoyez 2× taille_table disponible
  • pg_stat_progress_cluster : PostgreSQL 19 expose la progression via cette vue

4. Anticiper la fenêtre de verrou

Le swap final (relfilenode swap) requiert un AccessExclusiveLock de quelques secondes. Pour minimiser l'impact :

  • Planifiez l'opération hors des pics d'activité
  • Utilisez lock_timeout pour éviter une attente indéfinie en cas de transaction longue :
SET lock_timeout = '5s';
REPACK TABLE ma_table CONCURRENTLY;
  • Si le timeout est atteint, REPACK abandonne proprement — la table originale est intacte.

5. Préparer un plan de rollback

REPACK est conçu pour être safe, mais un plan de rollback reste indispensable :

  • Avant : notez la taille et les stats de la table (pg_relation_size, pg_stat_user_tables)
  • Pendant : un SIGINT ou une annulation de la requête stoppe l'opération ; la table originale n'est pas affectée tant que le swap n'a pas eu lieu
  • Après le swap : il n'y a pas de retour arrière automatique — la nouvelle table devient la référence. Ayez une sauvegarde récente (pg_dump ou snapshot PITR)

⚠️ 3 warnings à connaître avant le swap de relfilenode

1. Les index sont reconstruits, pas juste réutilisés

REPACK reconstruit tous les index de la table, ce qui peut prendre du temps sur des tables volumineuses avec de nombreux index. Vérifiez au préalable leur nombre et leur taille.

2. Le slot de réplication logique temporaire doit être nettoyé

Si REPACK est interrompu brutalement (crash, kill -9), le slot de réplication logique créé temporairement peut rester actif et bloquer la purge des WAL, entraînant une saturation disque. Vérifiez régulièrement :

SELECT slot_name, active, restart_lsn
FROM pg_replication_slots
WHERE slot_name LIKE 'repack%';

Supprimez manuellement tout slot orphelin avec pg_drop_replication_slot().

3. Les triggers et règles sont temporairement désactivés

Pendant la phase de copie initiale, certains triggers peuvent ne pas s'appliquer de la même façon sur le nouveau fichier. Vérifiez que vos contraintes d'intégrité et vos triggers AFTER se comportent comme attendu après l'opération.


Conclusion

REPACK est une avancée majeure de PostgreSQL 19 pour la maintenance des bases volumineuses en production. Il comble le fossé entre VACUUM (trop partiel) et VACUUM FULL (trop bloquant) en combinant réécriture complète et accessibilité continue de la table.

L'analyse de repack.c révèle toute la complexité orchestrée pour rendre cela possible : décodage logique, gestion des snapshots, reconstruction d'index, swap de relfilenode et gestion fine des verrous. Une commande simple en surface, un chef-d'œuvre d'ingénierie en dessous.

Pour les équipes MulerTech gérant des bases PostgreSQL sous Symfony avec un trafic continu, REPACK CONCURRENTLY peut devenir un outil de maintenance de choix — à condition de suivre la checklist ci-dessus et de ne jamais l'expérimenter pour la première fois directement en production.

Partager cet article