Skip to content

Latest commit

 

History

History
1186 lines (892 loc) · 39.3 KB

File metadata and controls

1186 lines (892 loc) · 39.3 KB

🔝 Retour au Sommaire

19.3.3. Nouveauté PG 18 : Vérifications parallèles (--jobs)

Introduction

L'une des améliorations les plus appréciées de PostgreSQL 18 dans pg_upgrade concerne l'option --jobs qui permet désormais d'effectuer les vérifications en parallèle. Cette fonctionnalité réduit considérablement le temps nécessaire pour valider qu'une migration est possible, particulièrement sur les bases de données volumineuses avec de nombreux objets.

Comprendre la parallélisation

Qu'est-ce que la parallélisation ?

La parallélisation consiste à exécuter plusieurs tâches simultanément au lieu de les traiter une par une (séquentiellement).

Analogie simple : La chaîne de montage

Mode séquentiel (sans parallélisation) :

Un seul ouvrier assemble toute une voiture, puis passe à la suivante :

Ouvrier 1:  [Voiture A] → [Voiture B] → [Voiture C]
            (2 heures)   (2 heures)   (2 heures)

Temps total : 6 heures pour 3 voitures

Mode parallèle (avec parallélisation) :

Trois ouvriers travaillent en même temps sur différentes voitures :

Ouvrier 1:  [Voiture A]  
Ouvrier 2:  [Voiture B]    ← En même temps !  
Ouvrier 3:  [Voiture C]  
            (2 heures chacun)

Temps total : 2 heures pour 3 voitures

Application à PostgreSQL

Dans le contexte de pg_upgrade, au lieu de vérifier les tables une par une, on peut en vérifier plusieurs simultanément.

┌─────────────────────────────────────────────────────────────┐
│  Mode séquentiel (1 job)                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Worker 1:  [Table A] → [Table B] → [Table C] → [Table D]   │
│             (10 min)   (10 min)   (10 min)   (10 min)       │
│                                                             │
│  Temps total : 40 minutes                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  Mode parallèle (4 jobs)                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Worker 1:  [Table A]                                       │
│  Worker 2:  [Table B]    ← Tous en même temps !             │
│  Worker 3:  [Table C]                                       │
│  Worker 4:  [Table D]                                       │
│             (10 min chacun)                                 │
│                                                             │
│  Temps total : 10 minutes (4× plus rapide !)                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Concepts clés

1. CPU Cores (Cœurs de processeur)

Votre serveur possède plusieurs cœurs de processeur (CPU cores) qui peuvent travailler indépendamment :

# Voir le nombre de CPU cores disponibles
lscpu | grep "^CPU(s):"
# CPU(s):              16

# Ou plus simplement
nproc
# 16

Un serveur avec 16 cores peut théoriquement exécuter 16 tâches en même temps.

2. Workers (Processus de travail)

Un worker est un processus qui effectue une partie du travail. Avec --jobs=4, pg_upgrade lance 4 workers qui travaillent en parallèle.

3. Overhead (Surcharge)

La parallélisation n'est pas gratuite :

  • Chaque worker consomme de la mémoire
  • Chaque worker peut générer des I/O disque concurrentes
  • Il y a un coût de coordination entre les workers

Donc, plus de jobs n'est pas toujours mieux !

L'option --jobs dans pg_upgrade

Historique

Avant PostgreSQL 18

L'option --jobs existait déjà dans les versions précédentes de PostgreSQL, mais son utilisation était limitée :

PostgreSQL ≤ 17 :
- --jobs fonctionnait pour la MIGRATION des données
- Mais PAS pour les VÉRIFICATIONS (--check)
- La phase --check restait séquentielle, donc lente

Problème : Sur une base avec 10,000 tables, la phase --check pouvait prendre plusieurs heures, même sur un serveur puissant avec 32 cores.

Nouveauté PostgreSQL 18

PostgreSQL 18 :
✅ --jobs fonctionne pour la MIGRATION (comme avant)
✅ --jobs fonctionne aussi pour les VÉRIFICATIONS (--check) ← NOUVEAU !
✅ Accélération drastique de la phase de validation

Syntaxe et utilisation

Commande de base avec --jobs

pg_upgrade \
  --old-datadir=/var/lib/postgresql/17/main \
  --new-datadir=/var/lib/postgresql/18/main \
  --old-bindir=/usr/lib/postgresql/17/bin \
  --new-bindir=/usr/lib/postgresql/18/bin \
  --jobs=4  # ← 4 workers en parallèle

Avec --check (vérification)

# Vérification avec 4 workers parallèles
pg_upgrade \
  --old-datadir=/var/lib/postgresql/17/main \
  --new-datadir=/var/lib/postgresql/18/main \
  --old-bindir=/usr/lib/postgresql/17/bin \
  --new-bindir=/usr/lib/postgresql/18/bin \
  --jobs=4 \
  --check  # ← Nouveau dans PG 18 : parallélisé !

Combinaison avec --swap

# Migration complète avec parallélisation et swap
pg_upgrade \
  --old-datadir=/var/lib/postgresql/17/main \
  --new-datadir=/var/lib/postgresql/18/main \
  --old-bindir=/usr/lib/postgresql/17/bin \
  --new-bindir=/usr/lib/postgresql/18/bin \
  --jobs=8 \
  --swap

Comment choisir le nombre de jobs ?

Règle générale

Nombre de jobs optimal ≈ 50-75% du nombre de CPU cores

Exemples :
- Serveur 4 cores  → --jobs=2 ou 3
- Serveur 8 cores  → --jobs=4 à 6
- Serveur 16 cores → --jobs=8 à 12
- Serveur 32 cores → --jobs=16 à 24

Pourquoi pas 100% ? :

  • Laisser des ressources pour le système d'exploitation
  • Laisser des ressources pour les opérations I/O
  • Éviter la contention (compétition) entre workers

Facteurs à considérer

1. CPU disponibles

# Vérifier les CPU disponibles
nproc
# 16

# Recommandation : --jobs=8 ou --jobs=12

2. Mémoire disponible

Chaque worker consomme de la mémoire. Formule approximative :

Mémoire par worker ≈ 100-500 MB
(selon la complexité de la base)

Exemple :
- 8 workers × 200 MB = 1.6 GB de RAM nécessaire
- Vérifier la RAM disponible avant de définir --jobs
# Vérifier la RAM disponible
free -h
#               total        used        free
# Mem:           62Gi        15Gi        45Gi  ← Largement suffisant

# Si free < 4 GB, limiter le nombre de jobs

3. Type de stockage

┌─────────────────────────────────────────────────────────────┐
│  Impact du stockage sur --jobs                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  HDD (disque mécanique) :                                   │
│  → --jobs=2 à 4 maximum                                     │
│  → Trop de parallélisme = contention I/O                    │
│  → Les têtes de lecture se déplacent beaucoup               │
│                                                             │
│  SSD SATA :                                                 │
│  → --jobs=4 à 8                                             │
│  → Bon compromis pour la plupart des cas                    │
│                                                             │
│  NVMe SSD :                                                 │
│  → --jobs=8 à 16+                                           │
│  → Peut gérer beaucoup de parallélisme                      │
│  → Latence ultra-faible                                     │
│                                                             │
│  Stockage réseau (NFS, SAN) :                               │
│  → Dépend de la configuration réseau                        │
│  → Tester avec --check d'abord                              │
│  → Généralement --jobs=4 à 8                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4. Nombre de tables et index

Base avec beaucoup d'objets :
- 10,000+ tables → --jobs élevé bénéfique (--jobs=8 à 16)
- 100 tables grosses → --jobs modéré suffisant (--jobs=4)

Raison : Plus il y a d'objets indépendants, plus on peut paralléliser

Tableau de recommandations

Serveur CPU RAM Stockage --jobs recommandé
Petit VPS 2-4 cores 4-8 GB SSD 2
Serveur moyen 8 cores 16-32 GB SSD 4-6
Serveur standard 16 cores 64 GB NVMe 8-12
Serveur puissant 32+ cores 128+ GB NVMe RAID 16-24
Cloud (AWS/Azure) Variables Variables EBS/Premium 4-8

Impact sur les performances

Gains de temps avec --jobs

Scénario 1 : Base moyenne (500 GB, 2,000 tables)

┌─────────────────────────────────────────────────────────────┐
│  Phase --check (vérification avant migration)               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  PostgreSQL 17 (--jobs=1 forcé pour --check)  :             │
│  Durée : 45 minutes                                         │
│                                                             │
│  PostgreSQL 18 avec --jobs=1 :                              │
│  Durée : 45 minutes (identique)                             │
│                                                             │
│  PostgreSQL 18 avec --jobs=4 :                              │
│  Durée : 12 minutes (3.75× plus rapide)                     │
│                                                             │
│  PostgreSQL 18 avec --jobs=8 :                              │
│  Durée : 7 minutes (6.4× plus rapide)                       │
│                                                             │
│  PostgreSQL 18 avec --jobs=16 :                             │
│  Durée : 5 minutes (9× plus rapide)                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Gain : 40 minutes économisées sur la vérification !

Scénario 2 : Grosse base (5 TB, 10,000 tables)

┌─────────────────────────────────────────────────────────────┐
│  Phase --check (vérification avant migration)               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  PostgreSQL 17 (--jobs=1 forcé) :                           │
│  Durée : 4 heures 30 minutes                                │
│                                                             │
│  PostgreSQL 18 avec --jobs=4 :                              │
│  Durée : 1 heure 15 minutes (3.6× plus rapide)              │
│                                                             │
│  PostgreSQL 18 avec --jobs=8 :                              │
│  Durée : 40 minutes (6.75× plus rapide)                     │
│                                                             │
│  PostgreSQL 18 avec --jobs=16 :                             │
│  Durée : 25 minutes (10.8× plus rapide)                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Gain : Plus de 4 heures économisées !

Scénario 3 : Très grosse base (20 TB, 50,000 tables)

┌─────────────────────────────────────────────────────────────┐
│  Phase --check (vérification avant migration)               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  PostgreSQL 17 (--jobs=1) :                                 │
│  Durée : 18 heures                                          │
│  Commentaire : Inacceptable en production !                 │
│                                                             │
│  PostgreSQL 18 avec --jobs=8 :                              │
│  Durée : 2 heures 30 minutes                                │
│                                                             │
│  PostgreSQL 18 avec --jobs=16 :                             │
│  Durée : 1 heure 20 minutes                                 │
│                                                             │
│  PostgreSQL 18 avec --jobs=24 :                             │
│  Durée : 55 minutes                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Gain : Plus de 17 heures économisées !

Graphique de performance

Temps de vérification --check (base 5 TB, 10,000 tables)

5h ┤
   │ ████████████████
   │ ████████████████  PG 17 (jobs=1)
4h ┤ ████████████████
   │ ████████████████
   │
3h ┤
   │
2h ┤
   │
   │ ████
1h ┤ ████  PG 18 (jobs=4)
   │ ██
   │ ██  PG 18 (jobs=8)
   │ █
   │ █ PG 18 (jobs=16)
   └─────────────────────────────
     PG17  j=4  j=8  j=16

Loi des rendements décroissants

Plus on ajoute de workers, moins le gain est proportionnel :

┌─────────────────────────────────────────────────────────────┐
│  Rendements décroissants avec --jobs                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1 → 2 jobs  : Gain de 90% (presque 2× plus rapide)         │
│  2 → 4 jobs  : Gain de 80% (1.8× plus rapide)               │
│  4 → 8 jobs  : Gain de 60% (1.6× plus rapide)               │
│  8 → 16 jobs : Gain de 40% (1.4× plus rapide)               │
│  16 → 32 jobs: Gain de 20% (1.2× plus rapide)               │
│                                                             │
│  Raison : Overhead, contention, limites I/O                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Conclusion : Il existe un point optimal au-delà duquel ajouter des jobs n'aide plus vraiment.

Que vérifie pg_upgrade --check ?

Comprendre ce qui est vérifié aide à comprendre pourquoi la parallélisation est si efficace.

Liste des vérifications

1. Vérifications globales (non parallélisables)

Ces vérifications sont exécutées une seule fois :

✅ Checking cluster versions
✅ Checking database user is the install user
✅ Checking database connection settings
✅ Checking for prepared transactions
✅ Checking for system-defined composite types in user tables

Temps : Quelques secondes à quelques minutes, peu importe --jobs

2. Vérifications par base de données (partiellement parallélisables)

✅ Checking for presence of required libraries
✅ Checking database user is the install user
✅ Checking for prepared transactions

Temps avec --jobs=1 : Proportionnel au nombre de databases
Temps avec --jobs=4 : Divisé par ~3-4

3. Vérifications par table/index (fortement parallélisables) ← IMPACT MAJEUR

✅ Checking for reg* data types in user tables
✅ Checking for contrib/isn with bigint-passing mismatch
✅ Checking for user-defined encoding conversions
✅ Checking for user-defined postfix operators
✅ Checking for incompatible polymorphic functions
✅ Checking for tables WITH OIDS
✅ Checking for invalid "sql_identifier" user columns
✅ Checking for extension updates

C'est ici que --jobs fait toute la différence !

Avec 10,000 tables :

  • --jobs=1 : Vérifie 1 table à la fois → 10,000 étapes séquentielles
  • --jobs=16 : Vérifie 16 tables simultanément → ~625 étapes

Exemple de vérification détaillée

Sans parallélisation (--jobs=1)

Checking for reg* data types in user tables

Table 1/10000: public.users          [====                ] 0.01%
...
(10 secondes par table × 10,000 tables = 27 heures !)

Avec parallélisation (--jobs=8)

Checking for reg* data types in user tables (8 workers)

Worker 1: public.users          [====    ]  
Worker 2: public.orders         [===     ]  
Worker 3: public.products       [======  ]  
Worker 4: public.payments       [==      ]  
Worker 5: public.reviews        [=====   ]  
Worker 6: public.addresses      [====    ]  
Worker 7: public.inventory      [===     ]  
Worker 8: public.shipments      [======  ]  

Progress: [===============                   ] 1250/10000 (12.5%)

(10 secondes × 10,000 / 8 ≈ 3.5 heures)

Utilisation pratique et bonnes pratiques

Procédure de test pour déterminer le --jobs optimal

Étape 1 : Vérifier les ressources

# 1. Nombre de CPU
nproc
# 16

# 2. RAM disponible
free -h | grep "Mem:"
# Mem:    62Gi    15Gi    45Gi

# 3. Type de disque
lsblk -d -o name,rota
# NAME ROTA
# sda     0    ← 0 = SSD, 1 = HDD

# 4. Performance I/O
iostat -x 1 5  # Observer pendant 5 secondes

Étape 2 : Tests progressifs

# Test 1 : Baseline avec 1 worker
time pg_upgrade --jobs=1 --check [options...]
# real: 45m 23s

# Test 2 : 4 workers
time pg_upgrade --jobs=4 --check [options...]
# real: 12m 10s (3.7× plus rapide)

# Test 3 : 8 workers
time pg_upgrade --jobs=8 --check [options...]
# real: 6m 45s (6.7× plus rapide)

# Test 4 : 16 workers
time pg_upgrade --jobs=16 --check [options...]
# real: 5m 20s (8.5× plus rapide)

# Test 5 : 32 workers (peut-être trop ?)
time pg_upgrade --jobs=32 --check [options...]
# real: 5m 15s (8.6× plus rapide - gain marginal)

Résultat : Pour ce serveur, --jobs=16 est optimal. Au-delà, le gain est négligeable.

Étape 3 : Surveiller les ressources pendant le test

# Dans un terminal séparé pendant pg_upgrade
watch -n 1 'echo "=== CPU ==="; mpstat 1 1 | tail -2; \
            echo "=== RAM ==="; free -h | grep Mem; \
            echo "=== I/O ==="; iostat -x 1 1 | tail -n +7'

Observer :

  • CPU utilization : devrait être entre 50-80%
  • RAM : devrait avoir au moins 2-4 GB de libre
  • I/O wait (wa%) : devrait rester < 20%

Si I/O wait > 30% → Réduire --jobs (goulot d'étranglement disque)

Script automatisé pour trouver l'optimal

#!/bin/bash
# find_optimal_jobs.sh
# Teste différentes valeurs de --jobs et recommande l'optimal

OLD_DIR="/var/lib/postgresql/17/main"  
NEW_DIR="/var/lib/postgresql/18/main"  
OLD_BIN="/usr/lib/postgresql/17/bin"  
NEW_BIN="/usr/lib/postgresql/18/bin"  

NCPU=$(nproc)  
MAX_JOBS=$((NCPU * 3 / 4))  # 75% des CPU  

echo "🔍 Tests de performance pg_upgrade --check"  
echo "CPU disponibles : $NCPU"  
echo "Tests jusqu'à : $MAX_JOBS jobs"  
echo ""  

for JOBS in 1 2 4 8 12 16; do
    if [ $JOBS -gt $MAX_JOBS ]; then
        break
    fi

    echo "📊 Test avec --jobs=$JOBS..."

    START=$(date +%s)
    pg_upgrade \
        --old-datadir="$OLD_DIR" \
        --new-datadir="$NEW_DIR" \
        --old-bindir="$OLD_BIN" \
        --new-bindir="$NEW_BIN" \
        --jobs=$JOBS \
        --check > /dev/null 2>&1
    END=$(date +%s)

    DURATION=$((END - START))
    echo "   Durée : ${DURATION}s"
    echo ""
done

echo "✅ Tests terminés. Analysez les résultats ci-dessus."  
echo "💡 Choisissez la valeur où le gain devient marginal."  

Migration en production : Stratégie recommandée

Phase 1 : Préparation (J-7 à J-1)

# 1. Déterminer --jobs optimal sur environnement de test
./find_optimal_jobs.sh

# 2. Documenter la valeur choisie
echo "OPTIMAL_JOBS=8" >> migration_config.env

# 3. Tester la migration complète sur staging
pg_upgrade --jobs=8 --check [options]

Phase 2 : Vérification pré-migration (H-1)

# Vérification rapide avec parallélisation
time pg_upgrade \
    --jobs=8 \
    --check \
    [autres options]

# Si < 30 minutes : OK pour production
# Si > 1 heure : Revoir --jobs ou optimiser la base

Phase 3 : Migration (Fenêtre de maintenance)

# Migration complète avec --jobs optimisé
pg_upgrade \
    --jobs=8 \
    --swap \
    --verbose \
    [autres options]

Bonnes pratiques

✅ À faire

# 1. Toujours tester --check avec --jobs d'abord
pg_upgrade --jobs=8 --check [options]

# 2. Utiliser --verbose pour voir la progression
pg_upgrade --jobs=8 --verbose [options]

# 3. Monitorer les ressources pendant la migration
htop  # ou top, dans un autre terminal

# 4. Documenter la valeur --jobs utilisée
echo "Migration PG 18 effectuée avec --jobs=8" >> migration.log

# 5. Laisser quelques CPU libres pour le système
# Si 16 CPU, utiliser --jobs=12 maximum

❌ À éviter

# 1. Ne pas utiliser --jobs égal au nombre de CPU
# Mauvais : --jobs=16 sur serveur 16-core
# Bon : --jobs=12 sur serveur 16-core

# 2. Ne pas tester --jobs uniquement en production
# Toujours tester sur dev/staging d'abord

# 3. Ne pas ignorer les warnings sur les ressources
# Si pg_upgrade dit "not enough memory", l'écouter !

# 4. Ne pas utiliser --jobs trop élevé sur HDD
# HDD : max --jobs=4
# SSD : --jobs=8 à 16
# NVMe : --jobs=16+

# 5. Ne pas oublier de monitorer pendant la migration

Combinaison avec autres options

--jobs + --check

# Vérification parallélisée (nouveau PG 18)
pg_upgrade \
    --jobs=8 \
    --check \
    [options]

# Économie de temps : Énorme sur grosses bases

--jobs + --swap

# Migration complète optimisée
pg_upgrade \
    --jobs=8 \
    --swap \
    [options]

# Bénéfices : Rapidité + sécurité

--jobs + --link

# Maximum de rapidité (mais sans filet)
pg_upgrade \
    --jobs=16 \
    --link \
    [options]

# Usage : Environnements non-critiques

--jobs + --verbose

# Voir la progression détaillée
pg_upgrade \
    --jobs=8 \
    --verbose \
    [options]

# Affiche quel worker fait quoi

Monitoring et diagnostic

Voir les workers en action

# Dans un terminal séparé pendant pg_upgrade
watch -n 2 'ps aux | grep -E "pg_upgrade|postgres: worker"'

# Sortie exemple :
# postgres  12345  pg_upgrade --jobs=8 ...
# postgres  12346  postgres: worker process (analyze)
# postgres  12347  postgres: worker process (analyze)
# postgres  12348  postgres: worker process (analyze)
# postgres  12349  postgres: worker process (analyze)
# postgres  12350  postgres: worker process (analyze)
# postgres  12351  postgres: worker process (analyze)
# postgres  12352  postgres: worker process (analyze)
# postgres  12353  postgres: worker process (analyze)

Surveiller l'utilisation CPU

# Utilisation CPU par cœur
mpstat -P ALL 1

# Sortie :
# CPU    %usr   %sys   %iowait  %idle
# 0      87.2   8.3    2.1      2.4    ← Worker 1
# 1      89.1   7.5    1.8      1.6    ← Worker 2
# 2      86.7   9.2    2.5      1.6    ← Worker 3
# 3      88.3   8.1    1.9      1.7    ← Worker 4
# 4      85.9   9.4    2.3      2.4    ← Worker 5
# ...

# Si %idle < 5% sur tous les CPU → --jobs pourrait être trop élevé
# Si %iowait > 20% → Goulot d'étranglement disque

Surveiller la mémoire

# Utilisation mémoire en temps réel
watch -n 1 'free -h'

#               total        used        free      shared  buff/cache   available
# Mem:           62Gi        25Gi        10Gi        1.0Gi        26Gi        35Gi

# Si free < 2 GB pendant pg_upgrade → Risque de swap

Surveiller les I/O

# Statistiques I/O
iostat -x 1

# Device   r/s    w/s    rkB/s    wkB/s  await  %util
# sda      234.5  123.2  45678    23456   12.3   78.5

# %util > 95% → Disque saturé, réduire --jobs
# await > 50ms → Latence élevée, réduire --jobs

Logs détaillés

# Logs pg_upgrade avec timestamps
tail -f pg_upgrade_server.log

# Exemple de sortie avec --jobs=8 :
# [2024-11-23 10:15:23] Starting parallel workers (8 workers)
# [2024-11-23 10:15:23] Worker 1: Checking table public.users
# [2024-11-23 10:15:23] Worker 2: Checking table public.orders
# [2024-11-23 10:15:23] Worker 3: Checking table public.products
# [2024-11-23 10:15:24] Worker 1: Completed table public.users
# [2024-11-23 10:15:24] Worker 1: Checking table public.payments
# ...

Cas d'usage réels

Cas 1 : Startup avec base croissante (200 GB)

Contexte :

  • Base : 200 GB, 1,500 tables
  • Serveur : AWS RDS-like, 8 vCPU, 32 GB RAM, SSD
  • Contrainte : Fenêtre de maintenance 4 heures

Stratégie :

# Après tests sur staging, configuration choisie :
JOBS=6  # 75% de 8 vCPU

# Vérification (PG 18)
time pg_upgrade --jobs=6 --check [options]
# Durée : 8 minutes (vs 28 minutes avec --jobs=1)

# Migration réelle
time pg_upgrade --jobs=6 --swap [options]
# Durée totale : 45 minutes

# Résultat : Migration dans la fenêtre, avec temps de confort

Gain : 20 minutes économisées sur la vérification

Cas 2 : Entreprise avec grosse base (3 TB)

Contexte :

  • Base : 3 TB, 8,000 tables
  • Serveur : On-premise, 32 cores, 256 GB RAM, NVMe RAID
  • Contrainte : Downtime < 6 heures

Stratégie :

# Configuration après tests :
JOBS=20  # ~60% de 32 cores (I/O en compte aussi)

# Vérification
time pg_upgrade --jobs=20 --check [options]
# Durée : 22 minutes (vs 3h15 avec --jobs=1)

# Migration réelle
time pg_upgrade --jobs=20 --swap --link [options]
# Durée totale : 3 heures

# Résultat : Dans la fenêtre avec marge

Gain : Presque 3 heures économisées !

Cas 3 : SaaS multi-tenant (500 GB, 50,000 tables)

Contexte :

  • Base : 500 GB, 50,000 petites tables (multi-tenant)
  • Serveur : Cloud, 16 vCPU, 64 GB RAM, SSD Premium
  • Contrainte : Downtime < 2 heures

Défi : Beaucoup de petites tables → --jobs très efficace ici !

Stratégie :

# Configuration optimale pour beaucoup de tables :
JOBS=12

# Vérification (CRUCIAL avec autant de tables)
time pg_upgrade --jobs=12 --check [options]
# Durée : 18 minutes (vs 5+ heures avec --jobs=1 !)

# Migration
time pg_upgrade --jobs=12 --swap [options]
# Durée totale : 1h 20min

# Résultat : Largement dans la fenêtre

Gain : Plus de 4h30 économisées sur la vérification !

Cas 4 : Legacy avec vieux HDD (100 GB)

Contexte :

  • Base : 100 GB, 500 tables
  • Serveur : Ancien serveur, 8 cores, 16 GB RAM, HDD mécanique
  • Contrainte : Budget limité, pas d'upgrade matériel

Attention : HDD = goulot d'étranglement I/O !

Stratégie :

# Tests progressifs :
# --jobs=8 : I/O wait 65%, inefficace
# --jobs=4 : I/O wait 35%, mieux
# --jobs=2 : I/O wait 15%, optimal

JOBS=2  # Limité par le disque, pas le CPU

# Vérification
time pg_upgrade --jobs=2 --check [options]
# Durée : 15 minutes (vs 25 minutes avec --jobs=1)

# Migration
time pg_upgrade --jobs=2 --swap [options]
# Durée : 1h 10min

Leçon : Sur HDD, --jobs élevé ne sert à rien (peut même ralentir)

Limitations et considérations

Limitations techniques

1. Nombre maximum de workers

PostgreSQL impose une limite pratique sur le nombre de workers :

Limite technique : --jobs=64 (maximum)

Mais en pratique :
- Au-delà de 24-32 workers, gains marginaux
- Risque de contention et overhead

2. Types d'opérations non parallélisables

Certaines opérations restent séquentielles même avec --jobs :

Non parallélisables :
❌ Vérification des versions
❌ Vérification des transactions préparées globales
❌ Copie de fichiers système (pg_xact, pg_multixact)
❌ Opérations finales de finalisation

Ces opérations représentent généralement < 10% du temps total.

3. Goulots d'étranglement possibles

┌─────────────────────────────────────────────────────────────┐
│  Goulots d'étranglement limitant l'efficacité de --jobs     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. I/O disque (le plus fréquent)                           │
│     → HDD saturé dès --jobs=4                               │
│     → Solution : SSD/NVMe ou limiter --jobs                 │
│                                                             │
│  2. Mémoire insuffisante                                    │
│     → Swap commence = performances s'effondrent             │
│     → Solution : Plus de RAM ou réduire --jobs              │
│                                                             │
│  3. CPU insuffisant                                         │
│     → Moins fréquent sur serveurs modernes                  │
│     → Solution : Limiter --jobs au nombre de cores          │
│                                                             │
│  4. Réseau (pour stockage réseau type NFS)                  │
│     → Latence réseau limite le parallélisme                 │
│     → Solution : Stockage local ou --jobs modéré            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Considérations de sécurité et stabilité

Charge sur le système

# Pendant pg_upgrade avec --jobs élevé
# Le système peut être fortement sollicité

# Exemple : --jobs=16 sur serveur partagé
# → Peut impacter d'autres services
# → Planifier pendant une fenêtre maintenance complète

Consommation de connexions

Chaque worker peut ouvrir des connexions à la base :

--jobs=8 × ~5 connexions par worker = ~40 connexions

Vérifier max_connections :  
SHOW max_connections;  -- Doit être > nombre de workers × 10  

Logs et espace disque

Plus de workers = plus de logs :

# Vérifier l'espace avant migration
df -h /var/log/postgresql/

# Avec --jobs=16, logs peuvent atteindre 1-2 GB
# pour une migration complète

Dépannage et résolution de problèmes

Problème 1 : Migration lente malgré --jobs

Symptômes :

pg_upgrade --jobs=16 [options]
# Progression : 1 table / 30 secondes (très lent)

Diagnostic :

# Vérifier I/O wait
iostat -x 1
# %iowait > 50% → Disque saturé

# Vérifier la mémoire
free -h
# free < 1 GB → Mémoire insuffisante

Solution :

# Réduire --jobs
pg_upgrade --jobs=4 [options]  # Au lieu de 16

# Ou upgrade vers SSD si possible

Problème 2 : Erreur "too many workers"

Symptômes :

ERROR: number of workers (32) exceeds maximum allowed (24)

Solution :

# Réduire à la valeur recommandée
pg_upgrade --jobs=24 [options]

Problème 3 : Out of memory

Symptômes :

FATAL: out of memory  
ERROR: worker process crashed  

Diagnostic :

# Vérifier la mémoire disponible
free -h
# Si available < 4 GB → Problème

# Vérifier le swap
swapon --show
# Si swap utilisé activement → Problème

Solution :

# Réduire --jobs pour consommer moins de RAM
pg_upgrade --jobs=4 [options]

# Ou augmenter la RAM du serveur

Problème 4 : Deadlocks ou workers bloqués

Symptômes :

# Progression s'arrête, workers semblent bloqués
Worker 3: [Waiting...]  
Worker 5: [Waiting...]  

Diagnostic :

# Vérifier les processus
ps aux | grep postgres

# Vérifier les locks
# (nécessite que l'ancien cluster soit démarré en mode spécial)

Solution :

# Annuler et relancer avec moins de workers
Ctrl+C  # Annuler pg_upgrade  
pg_upgrade --jobs=4 [options]  # Réessayer avec moins de workers  

Comparaison avec les versions précédentes

PostgreSQL 17 vs PostgreSQL 18

┌─────────────────────────────────────────────────────────────┐
│  Option --jobs : PG 17 vs PG 18                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  PostgreSQL 17 :                                            │
│  ├─ --jobs=N pour MIGRATION : ✅ Oui                        │
│  └─ --jobs=N pour --check   : ❌ Non (forcé à 1)            │
│                                                             │
│  PostgreSQL 18 :                                            │
│  ├─ --jobs=N pour MIGRATION : ✅ Oui                        │
│  └─ --jobs=N pour --check   : ✅ Oui (NOUVEAU !)            │
│                                                             │
│  Impact :                                                   │
│  → Phase --check 5-10× plus rapide dans PG 18               │
│  → Validation avant migration beaucoup plus praticable      │
│  → Itérations de tests plus rapides                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Tableau récapitulatif

Opération PG ≤ 17 PG 18 Gain
--check avec --jobs=1 45 min 45 min -
--check avec --jobs=8 45 min (forcé à 1) 6 min 7.5×
Migration avec --jobs=8 2h 2h -
Migration totale 2h45 2h06 ~30%

Témoignages (fiction illustrative)

DBA chez une FinTech :

"Avant PG 18, la phase --check prenait 3 heures sur notre base de 5 TB. On ne pouvait la lancer qu'une fois par jour en DEV. Avec PG 18 et --jobs=12, c'est 20 minutes. On peut tester beaucoup plus souvent !"

DevOps chez un e-commerce :

"La parallélisation de --check change tout. On peut valider notre migration le matin, faire des ajustements, et retester l'après-midi. Avant, chaque test prenait toute la nuit."

Conclusion

La parallélisation de --check avec --jobs dans PostgreSQL 18 est une amélioration qui peut sembler technique, mais qui a un impact pratique énorme sur les migrations en production.

Récapitulatif des bénéfices

  • Vitesse : Vérifications 5-10× plus rapides
  • Confiance : Tests itératifs possibles avant migration
  • Flexibilité : Validation rapide même sur grosses bases
  • Productivité : Moins de temps d'attente pour les équipes
  • Sécurité : Plus de tests = moins de surprises

Points clés à retenir

  1. --jobs fonctionne désormais pour --check (nouveau PG 18)
  2. Choisir --jobs = 50-75% des CPU disponibles (règle générale)
  3. Toujours tester sur DEV/staging d'abord pour trouver l'optimal
  4. Monitorer les ressources (CPU, RAM, I/O) pendant les tests
  5. HDD = max --jobs=4 ; SSD/NVMe = --jobs=8-16+

Recommandations finales

Pour une migration réussie avec --jobs :

1. Tester --check avec différentes valeurs de --jobs
2. Trouver le point optimal (gain vs ressources)
3. Documenter la configuration choisie
4. Utiliser cette configuration en production
5. Combiner avec --swap pour une migration optimale

Prochaines sections recommandées

Pour une compréhension complète de pg_upgrade dans PostgreSQL 18 :

  • Section 19.3.1 : Préservation des statistiques (complémentaire)
  • Section 19.3.2 : Option --swap pour sécurité et rapidité
  • Section 19.3.4 : Stratégies Blue/Green pour downtime minimal
  • Section 19.4 : Troubleshooting des migrations

Note : Retenez l'essentiel : PostgreSQL 18 permet de vérifier votre migration beaucoup plus rapidement en utilisant plusieurs "workers" qui travaillent en parallèle. C'est comme avoir plusieurs personnes qui vérifient différentes parties de votre base en même temps au lieu d'une seule personne qui fait tout séquentiellement. Le résultat ? Des migrations plus rapides et plus sûres !

⏭️ Stratégies Blue/Green et Logical Replication