Sauvegarde PITR avec pgBackRest

Module I4

Dalibo SCOP

26.09

10 septembre 2026

Sur ce document

Formation Module I4
Titre Sauvegarde PITR avec pgBackRest
Révision 26.09
PDF https://dali.bo/i4_pdf
EPUB https://dali.bo/i4_epub
HTML https://dali.bo/i4_html
Slides https://dali.bo/i4_slides
TP https://dali.bo/i4_tp
TP (solutions) https://dali.bo/i4_solutions

Licence Creative Commons CC-BY-NC-SA

Cette formation est sous licence CC-BY-NC-SA. Vous êtes libre de la redistribuer et/ou modifier aux conditions suivantes :

  • Paternité
  • Pas d’utilisation commerciale (y compris IA)
  • Partage des conditions initiales à l’identique

Marques déposées

PostgreSQL® Postgres® et le logo Slonik sont des marques déposées par PostgreSQL Community Association of Canada.

Versions de PostgreSQL couvertes

Ce document ne couvre que les versions supportées de PostgreSQL au moment de sa rédaction, soit les versions 14 à 18.

Sauvegarde PITR avec pgBackRest

pgBackRest

PgbackRest

pgBackRest - Présentation générale

  • David Steele (Crunchy Data)
  • Langage : C
  • Licence : MIT (libre)
  • Type d’interface : CLI (ligne de commande)

pgBackRest - Fonctionnalités

  • Gère la sauvegarde et la restauration PITR
    • pull ou push, multidépôts
    • mono- ou multiserveur
  • Indépendant des commandes système
    • protocole dédié
  • Sauvegardes complètes, différentielles ou incrémentales
  • Compression, multi-dépôts
  • Multithread, sauvegarde depuis un secondaire, archivage asynchrone…
  • Projet mature

pgBackRest - Sauvegardes

  • Type de sauvegarde : physique/PITR (à chaud)
  • Type de stockage : local, push ou pull
  • Planification : crontab (ou autre)
  • Compression des WAL

pgBackRest - Sauvegardes « push »

  • Le serveur « pousse » sauvegardes & archives

  • Et les « tire » lors d’une restauration

pgBackRest - Sauvegardes « pull »

  • Le serveur de sauvegarde « tire » les sauvegardes
  • L’archivage reste en « push »

  • Restauration depuis le serveur PostgreSQL

pgBackRest - Restauration

  • Exécutée sur le serveur de BDD (pull)
    • où que soit le serveur
  • Point dans le temps :
    • date
    • identifiant de transaction
    • timeline
    • point de restauration (pg_create_restore_point())
  • pgBackrest copie et prépare
  • PostgreSQL fait la restauration

pgBackRest - Installation

  • Accéder au dépôt communautaire PGDG
    • paquet pgbackrest
  • Même version sur :
    • tous les serveurs PostgreSQL
    • celui de backup
  • Mises à jour assez fréquentes
    • les appliquer rapidement

pgBackRest - Utilisation

Usage:
    pgbackrest [options] [command]

Commands:
    archive-get     Get a WAL segment from the archive
    archive-push    Push a WAL segment to the archive
    backup          Backup a database cluster
    check           Check the configuration
    expire          Expire backups that exceed retention
    info            Retrieve information about backups
    repo-get        Get a file from a repository
    restore         Restore a database cluster
    server          pgBackRest server
    stanza-create   Create the required stanza data
    stanza-delete   Delete a stanza
    stanza-upgrade  Upgrade a stanza
    start           Allow pgBackRest processes to run
    stop            Stop pgBackRest processes from running
    verify          Verify contents of the repository.
    version         Get version.
    

pgBackRest - Stanza

  • Stanza = instance répartie dans un cluster (groupe de serveurs)
    • la même pour primaire & secondaire
  • Ex : erp_prod, dwh
    • sans n° de version

pgBackRest - Emplacement des fichiers de configuration

  • /etc/pgbackrest/pgbackrest.conf
    • /etc/pgbackrest.conf
    • pgbackrest --config=…
    • /etc/pgbackrest/conf.d/*.conf
    • pgbackrest --config-include-path=…

pgBackRest - Sections

  • [global]
  • [global:archive-push], [global:archive-get]
  • Stanzas :
    • [nomstanza1]
    • [nomstanza2]
  • Surcharges :
    • entre sections
    • en CLI par --nom-option

pgBackRest - Configuration PostgreSQL : archivage

  • postgresql.conf
archive_mode = on
wal_level = replica
archive_command = 'pgbackrest --stanza=erp_prod archive-push %p'
archive_timeout = '? min'   # selon RPO

pgBackRest - Configuration globale

  • Dans pgbackrest.conf :
[global]
repo1-path=/var/lib/pgbackrest
process-max=4
log-level-console=info
log-level-file=detail

pgBackRest - Exemples de déclaration de dépôts

[global]
repo1-path=/mnt/NFS/backups
repo1-retention-full=5
repo1-retention-diff=3

repo2-host-type=ssh
repo2-host=backup_serveur
repo2-host-user=postgres
repo2-path=/srv/depot2
repo2-retention-full=2
repo2-retention-diff=1

repo3-type=s3
repo3-path=/repo
repo3-s3-endpoint=s3.backup.local
repo3-s3-bucket=pgbackrest
repo3-s3-verify-tls=n
repo3-s3-key=XXXX
repo3-s3-key-secret=XXXXXXX
repo3-retention-full=10
repo3-retention-diff=1

pgBackRest - Rétention

  • Type de rétention des sauvegardes complètes
repo1-retention-full-type=count|time
  • Nombre de sauvegardes complètes
repo1-retention-full=2
  • Nombre de sauvegardes différentielles
repo1-retention-diff=3
  • Expiration sur demande :
pgbackrest --stanza=--set=…   expire
pgbackrest --stanza=--oldest  expire

pgBackRest - Accès au dépôt par SSH

  • Échange des clés SSH entre instances (postgres) & le dépôt

  • Accès depuis serveurs PostgreSQL au serveur de sauvegarde :

    [global]
    repo1-host=serveur_depot
    repo1-host-user=postgres
    repo1-host-port=22
  • Accès depuis le serveur de sauvegardes aux instances :

    
    [nomstanza]
    pg1-host=principal
    pg1-host-port=22                          
    pg1-host-user=postgres

pgBackRest - Configuration TLS

Alternative au SSH :

  • {repo1|pg1}-host-type = tls
  • Certificats à fournir
  • Service dédié sur serveurs PG et serveur de sauvegardes

pgBackRest - Configuration par stanza

[erp_prod]
pg1-path=/var/lib/pgsql/17/data
pg1-port=5432
pg1-database=postgres

pgBackRest - Initialiser le répertoire de stockage

  • Initialisation par stanza :
$ sudo -u postgres pgbackrest --stanza=erp_prod stanza-create
  • Vérification :
$ sudo -u postgres pgbackrest --stanza=erp_prod check

pgBackRest - Effectuer une sauvegarde

  • Déclencher une nouvelle sauvegarde :
$ sudo -u postgres pgbackrest --stanza=erp_prod --type=full backup
  • --type=full, --type=diff, --type=incr
  • La plupart des paramètres peuvent être surchargés

pgBackRest - Autres options de sauvegardes

# Journaux dans l'archive
archive-copy=y
# Sauvegarder depuis un secondaire
backup-standby=y
# Checkpoint immédiat
start-fast=y
# Délai de réception des journaux (secondes)
archive-timeout=120

pgBackRest - Lister les sauvegardes

  • Lister les sauvegardes présentes et leur taille
$ sudo -u postgres pgbackrest --stanza=erp_prod info
  • ou une sauvegarde spécifique (backup set)
$ sudo -u postgres pgbackrest --stanza=erp_prod --set 20221026-071751F info

pgBackRest - Dépôts multiples

  • Plusieurs dépôts simultanés possibles, tous types
    • sauvegarde & rétention indépendants
    • archivage sur tous les dépôts (asynchrone conseillé !)
    • --repo1-option=… , appel avec --repo=1

pgBackRest - Compression

Variantes possibles selon les différentes étapes :

# Backup : compression extrême et lente
compress-type=zst
compress-level=9
process-max=16

# Archives uniquement : compression la plus rapide possible
[global:archive-push]
compress-type=lz4
compress-level=1
process-max=2

pgBackRest - Mode asynchrone

Parallélisation de l’archivage/restauration :

archive-async=y
spool-path=/var/spool/pgbackrest
# spool pour la restauration
archive-get-queue-max=4GB

[global:archive-push]
# restauration : pas trop de processus
process-max=4

[global:archive-get]
# restauration : pas trop de processus
process-max=2
  • Gros gain en temps

pgBackRest - Sécurité contre la saturation de pg_wal

  • Abandon de l’archivage si trop de retard :
archive-push-queue-max = 20GB
  • Sauvegarde à relancer !

pgBackRest - Bundling et sauvegarde incrémentale en mode block

  • Regrouper les petits fichiers dans des bundles
repo1-bundle=y
  • Sauvegarde incrémentale en mode block (requiert le bundling)
repo1-bundle=y
repo1-block=y

pgBackRest - Restauration : commande

pgbackrest --stanza=erp_prod \
--pg1-path=/var/lib/postgresql/copie
restore
  • dans postgresql.auto.conf :
# Recovery settings generated by pgBackRest restore on 2026-09-02 18:44:49
restore_command = 'pgbackrest --pg1-path=/var/lib/postgresql/copie
--stanza=defo15 archive-get %f "%p"'

pgBackRest - Restauration : option

  • Nombreuses options à la restauration, notamment :
    • --delta (gros gain de temps parfois)
    • --db-exclude/--db-include (ne restaure pas tout !)
    • --archive_mode=off (sécurité)
    • --target / --type

pgBackRest - Exemple de restauration à une date précise

pgbackrest --stanza=erp_prod \
  --type=time \
  --target='2020-07-16 11:07:00' \
  --target-timeline=4 \
  --set=20200716-102845F \
  --delta \
  restore

pgBackRest - Exemple de restauration d’un secondaire

pgbackrest --stanza=erp_prod \
  --type=standby \
  --pg1-path=/var/lib/postgresql/secondaire \
  restore
  • Ajoute recovery.signal, restore_command
  • Pour streaming, dans pgbackrest.conf :
recovery-option=primary_conninfo='host=primaire port=5432 user=repli'

pgBackRest - Mises à jour

  • Mise à jour de pgBackRest
    • faire en même temps sur toutes les machines
  • Mise à jour mineure de PostgreSQL
    • transparent
  • Mise à jour majeure de PostgreSQL (pg_upgrade)
    • mise à jour des chemins
    • pgbackrest --stanza=… stanza-upgrade

pgBackRest - Traces

  • /var/log/pgbackrest : par stanza+commandes
    • NOMSTANZA-archive-push-async.log
    • NOMSTANZA-backup.log
    • NOMSTANZA-expire.log
  • Sur serveur TLS :
    • /var/log/pgbackrest/all-server.log
  • log-level-console, log-level-file

pgBackRest - Conclusion

  • Un outil de sauvegarde très puissant
  • Nombreuses options

Quiz

Travaux pratiques

Utilisation de pgBackRest (Optionnel)

Travaux pratiques (solutions)

Utilisation de pgBackRest

NB : Ce TP a été mis à jour pour PostgreSQL 17. Adapter le numéro de version dans les chemins au besoin.

Installer pgBackRest à partir des paquets du PGDG.

L’installation du paquet est triviale avec les paquets du PGDG :

 # dnf install pgbackrest    # Rocky Linux
 # apt install pgbackrest    # Debian/Ubuntu

En vous aidant de https://pgbackrest.org/user-guide.html#quickstart :

  • configurer pgBackRest pour sauvegarder le serveur PostgreSQL en local dans /var/lib/pgsql/backups ;
  • le nom de la stanza sera instance_dev ;
  • prévoir de ne conserver qu’une seule sauvegarde complète.

Le ficher de configuration de pgBackRest est /etc/pgbackrest.conf. Le modifier ainsi :

[global]
repo1-path=/var/lib/pgsql/backups
repo1-retention-full=1

[instance_dev]
# chemin de l'instance PostgreSQL
pg1-path=/var/lib/pgsql/17/data

(Les chemins ci-dessus sont ceux par défaut des paquets RPM du PGDG. Sous Debian/Ubuntu, les données sont dans /var/lib/postgresql/17/main. Adapter les autres chemins en fonction.)

Configurer l’archivage des journaux de transactions de PostgreSQL avec pgBackRest.

Le fichier de configuration de PostgreSQL doit être modifié au besoin ainsi.

wal_level = replica
archive_mode = on
archive_command = 'pgbackrest --stanza=instance_dev archive-push %p'

Redémarrer PostgreSQL :

 # systemctl restart postgresql-17

Initialiser le répertoire de stockage des sauvegardes et vérifier la configuration de l’archivage.

Sous l’utilisateur postgres :

pgbackrest --stanza=instance_dev --log-level-console=info stanza-create
… P00   INFO: stanza-create command begin 2.54.1: --exec-id=116151-5ba090e6 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --stanza=instance_dev
… P00   INFO: stanza-create for stanza 'instance_dev' on repo1
… P00   INFO: stanza-create command end: completed successfully (56ms)

Vérifier la configuration de pgBackRest et de l’archivage :

pgbackrest --stanza=instance_dev --log-level-console=info check

pgBackRest force ainsi un archivage :

… P00   INFO: check command begin 2.54.1: --exec-id=116153-45ee6160 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --stanza=instance_dev
… P00   INFO: check repo1 configuration (primary)
… P00   INFO: check repo1 archive for WAL (primary)
… P00   INFO: WAL segment 0000000200000000000000D8 successfully archived to '/var/lib/pgsql/backups/archive/instance_dev/17-1/0000000200000000/0000000200000000000000D8-81ecb9751dd627ba196fca377e9e6d0a2aa6fd05.gz' on repo1
… P00   INFO: check command end: completed successfully (409ms)

Vérifier que l’archivage fonctionne en vérifiant que ce répertoire n’est pas vide :

ls -alR /var/lib/pgsql/backups/archive/instance_dev/17-1/

On peut le vérifier aussi du côté PostgreSQL :

SELECT * FROM pg_stat_archiver \gx
-[ RECORD 1 ]------+------------------------------
archived_count     | 4
last_archived_wal  | 0000000200000000000000D8
last_archived_time | 2025-01-13 19:03:13.400874+01
failed_count       | 0
last_failed_wal    | 
last_failed_time   | 
stats_reset        | 2025-01-13 18:47:37.39799+01

Autre méthode, regarder le nom du processus archiver, qui contient le nom du dernier journal archivé :

$ ps faux|grep archiver
…
postgres  211745  0.0  0.1 502568  7124 ?        Ss   14:14   0:00  \_ postgres: archiver last was 0000000200000000000000D8

Lancer une sauvegarde complète. Afficher les détails de cette sauvegarde.

pgbackrest --stanza=instance_dev --type=full \
           --log-level-console=info backup

Noter le soin avec lequel pgBackRest vérifie que l’archivage est fonctionnel avant la sauvegarde, et l’attente du dernier journal avant d’assurer que la sauvegarde est terminée :

… P00   INFO: backup command begin 2.54.1: --exec-id=116270-de7f5e35 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --repo1-retention-full=1 --stanza=instance_dev --type=full
… P00   INFO: execute non-exclusive backup start: backup begins after the next regular checkpoint completes
… P00   INFO: backup start archive = 0000000200000000000000DC, lsn = 0/DC000028
… P00   INFO: check archive for prior segment 0000000200000000000000DB


… P00   INFO: execute non-exclusive backup stop and wait for all WAL segments to archive
… P00   INFO: backup stop archive = 0000000200000000000000DC, lsn = 0/DC000158
… P00   INFO: check archive for segment(s) 0000000200000000000000DC:0000000200000000000000DC
… P00   INFO: new backup label = 20250113-190921F
… P00   INFO: full backup size = 3.5GB, file total = 1594
… P00   INFO: backup command end: completed successfully (74648ms)
… P00   INFO: expire command begin 2.54.1: --exec-id=116270-de7f5e35 --log-level-console=info --repo1-path=/var/lib/pgsql/backups --repo1-retention-full=1 --stanza=instance_dev
… P00   INFO: repo1: expire full backup 20250113-190649F
… P00   INFO: repo1: remove expired backup 20250113-190649F
… P00   INFO: repo1: 17-1 remove archive, start = 0000000200000000000000D8, stop = 0000000200000000000000DB
… P00   INFO: expire command end: completed successfully (109ms)

Lister les sauvegardes :

pgbackrest --stanza=instance_dev info
stanza: instance_dev
    status: ok
    cipher: none

    db (current)
        wal archive min/max (17): 0000000200000000000000DC/0000000200000000000000DC

        full backup: 20250113-190921F
            timestamp start/stop: 2025-01-13 19:09:21+01 / 2025-01-13 19:10:35+01
            wal start/stop: 0000000200000000000000DC / 0000000200000000000000DC
            database size: 3.5GB, database backup size: 3.5GB
            repo1: backup set size: 197.7MB, backup size: 197.7MB

Ajouter des données : \ Ajouter une table avec 1 million de lignes. \ Forcer la rotation du journal de transaction courant afin de s’assurer que les dernières modifications sont archivées. \ Vérifier que le journal concerné est bien dans les archives.

La table suivante fait 35 Mo, qui seront intégralement écrits dans les journaux :

CREATE TABLE matable AS SELECT i FROM generate_series(1,1000000) i ;

Pour ce test, il est possible de forcer la rotation du journal avec pg_switch_wal. Dans la vie réelle, il y a de l’activité dans la base et le journal sera assez vite archivé. Sans cela il pourrait ne pas être sauvegardé.

pg_switch_wal renvoie un LSN peu lisible, comme 0/E8000180, où E8 correspond à la fin du nom du journal. On peut ajouter pg_walfile_name() pour voir plus clairement le nom du journal à archiver :

SELECT pg_walfile_name ( pg_switch_wal() );
      pg_walfile_name 
--------------------------
 0000000200000000000000E8

Vérifier que le journal concerné est bien dans le répertoire de sauvegarde des archives de pgBackRest, soit dans notre exemple /var/lib/pgsql/backups/archive/instance_dev/17-1/. La copie devrait être ici instantanée, mais en production ça ne ne l’est pas forcément.

Simulation d’un incident : noter l’heure puis supprimer tout le contenu de la table.

Noter l’heure exacte avant de détruire des données :

SELECT now() ;
 2025-01-13 19:21:22.043403+01
TRUNCATE TABLE matable;

Restaurer les données telles que juste avant l’incident à l’aide de pgBackRest. \ Avant de redémarrer PostgreSQL, consulter les fichiers que pgBackRest a créé ou modifié dans le PGDATA. \ Redémarrer.

D’abord, stopper PostgreSQL (sinon pgBackRest refusera de toucher aux données) :

sudo systemctl stop postgresql-17

En tant que postgres, lancer la commande de restauration avec une heure juste avant la destruction des données :

pgbackrest --stanza=instance_dev --log-level-console=info \
--delta                         \
--type=time                     \
--target="2025-01-13 19:21:22"  \
--target-exclusive              \
--target-action=promote         \
restore

Noter déjà le mode « delta » pour accélérer la restauration, et le type de restauration time avec une heure.

… P00   INFO: restore command begin 2.54.1: --delta --exec-id=116554-29ea7f39 --log-level-console=info --pg1-path=/var/lib/pgsql/17/data --repo1-path=/var/lib/pgsql/backups --stanza=instance_dev --target="2025-01-13 19:21:22" --target-action=promote --target-exclusive --type=time
… P00   INFO: repo1: restore backup set 20250113-190921F, recovery will start at 2025-01-13 19:09:21
… P00   INFO: remove invalid files/links/paths from '/var/lib/pgsql/17/data'
… P00   INFO: write updated /var/lib/pgsql/17/data/postgresql.auto.conf
… P00   INFO: restore global/pg_control (performed last to ensure aborted restores cannot be started)
… P00   INFO: restore size = 3.5GB, file total = 1594
… P00   INFO: restore command end: completed successfully (4909ms)

La restauration du base backup est un succès, mais il va falloir rejouer les journaux archivés.

pgBackRest a créé ou modifié ces fichiers :

$ ls -alrt  /var/lib/pgsql/17/data


-rw-------.  1 postgres postgres   353 13 janv. 19:15 postgresql.auto.conf
-rw-------.  1 postgres postgres     0 13 janv. 19:15 recovery.signal
  • recovery.signal signalera à PostgreSQL qu’il est en mode restauration, et pas en redémarrage après un crash ;
  • postgresql.auto.conf contient des paramètres qui vont surcharger postgresql.conf :
# Recovery settings generated by pgBackRest restore on 2025-01-13 19:15:22
restore_command = 'pgbackrest --stanza=instance_dev archive-get %f "%p"'
recovery_target_time = '2025-01-13 19:21:22'
recovery_target_inclusive = 'false'
recovery_target_action = 'promote'

On y trouve :

  • la restore_command pour récupérer les journaux dans le dépôt, commande que pgBackRest a préparé en fonction de sa configuration et des paramètres de la ligne de commande de restauration ;
  • recovery_target_time indique l’heure cible ;
  • recovery_target_inclusive = 'false' arrête la restauration juste avant cette heure pour ne pas rejouer la destruction des données (le défaut est de rejouter jusqu’à l’heure cible incluse) ;
  • recovery_target_action = 'promote' demande à PostgreSQL de s’ouvrir en écriture après le rejeu.

Démarrer PostgreSQL :

sudo systemctl start postgresql-17

Attendre la fin de la restauration dans les traces :

# Attention, le nom du fichier dépend du jour
tail -n100  /var/lib/pgsql/17/data/log/postgresql-Mon.log
2025-01-13 19:26:38.946 CET [116631] LOG:  database system was interrupted; last known up at 2025-01-13 19:09:21 CET
2025-01-13 19:26:39.032 CET [116631] LOG:  starting backup recovery with redo LSN 0/DC000028, checkpoint LSN 0/DC000080, on timeline ID 2
2025-01-13 19:26:39.108 CET [116631] LOG:  restored log file "0000000200000000000000DC" from archive
2025-01-13 19:26:39.168 CET [116631] LOG:  starting point-in-time recovery to 2025-01-13 19:21:22+01
2025-01-13 19:26:39.176 CET [116631] LOG:  redo starts at 0/DC000028
2025-01-13 19:26:39.236 CET [116631] LOG:  restored log file "0000000200000000000000DD" from archive
2025-01-13 19:26:39.298 CET [116631] LOG:  completed backup recovery with redo LSN 0/DC000028 and end LSN 0/DC000158
2025-01-13 19:26:39.298 CET [116631] LOG:  consistent recovery state reached at 0/DC000158
2025-01-13 19:26:39.298 CET [116626] LOG:  database system is ready to accept read-only connections
2025-01-13 19:26:39.584 CET [116631] LOG:  restored log file "0000000200000000000000DE" from archive
2025-01-13 19:26:39.893 CET [116631] LOG:  restored log file "0000000200000000000000DF" from archive
2025-01-13 19:26:40.169 CET [116631] LOG:  restored log file "0000000200000000000000E0" from archive
2025-01-13 19:26:40.458 CET [116631] LOG:  restored log file "0000000200000000000000E1" from archive
2025-01-13 19:26:40.609 CET [116631] LOG:  restored log file "0000000200000000000000E2" from archive
2025-01-13 19:26:40.758 CET [116631] LOG:  restored log file "0000000200000000000000E3" from archive
2025-01-13 19:26:40.907 CET [116631] LOG:  restored log file "0000000200000000000000E4" from archive
2025-01-13 19:26:41.029 CET [116631] LOG:  restored log file "0000000200000000000000E5" from archive
2025-01-13 19:26:41.291 CET [116631] LOG:  restored log file "0000000200000000000000E6" from archive
2025-01-13 19:26:41.592 CET [116631] LOG:  restored log file "0000000200000000000000E7" from archive
2025-01-13 19:26:41.876 CET [116631] LOG:  restored log file "0000000200000000000000E8" from archive
2025-01-13 19:26:42.238 CET [116631] LOG:  restored log file "0000000200000000000000E9" from archive
2025-01-13 19:26:42.333 CET [116631] LOG:  recovery stopping before commit of transaction 32096, time 2025-01-13 19:21:40.349582+01
2025-01-13 19:26:42.333 CET [116631] LOG:  redo done at 0/E904E420 system usage: CPU: user: 1.18 s, system: 0.18 s, elapsed: 3.15 s
2025-01-13 19:26:42.333 CET [116631] LOG:  last completed transaction was at log time 2025-01-13 19:20:51.370895+01
2025-01-13 19:26:42.403 CET [116631] LOG:  restored log file "0000000200000000000000E9" from archive
2025-01-13 19:26:42.498 CET [116631] LOG:  selected new timeline ID: 3
2025-01-13 19:26:42.612 CET [116631] LOG:  archive recovery complete
2025-01-13 19:26:42.615 CET [116629] LOG:  checkpoint starting: end-of-recovery immediate wait
2025-01-13 19:26:42.806 CET [116629] LOG:  checkpoint complete: wrote 4490 buffers (27.4%); 0 WAL file(s) added, 0 removed, 13 recycled; write=0.040 s, sync=0.114 s, total=0.194 s; sync files=36, longest=0.102 s, average=0.004 s; distance=213304 kB, estimate=213304 kB; lsn=0/E904E420, redo lsn=0/E904E420
2025-01-13 19:26:42.814 CET [116626] LOG:  database system is ready to accept connections

Vérifier les logs et la présence des données disparues.

La trace ci-dessus indique bien :

  • la restauration de divers journaux ;
  • l’arrivée au point de cohérence qui permet au moins d’avoir une instance utilisable telle qu’à la fin du base backup (consistent recovery state) ;
  • le changement vers une nouvelle timeline (selected new timeline ID: 3), comme après toute restauration ;
  • et l’heure de fin de la dernière transaction rejouée (last completed transaction was at …).

Les lignes perdues sont bien revenues :

SELECT count(*) FROM matable ;
  count
---------
 1000000

Remarque :

Sans spécifier de --target-action=promote, on obtiendrait dans les traces de PostgreSQL, après restore :

LOG:  recovery has paused
HINT:  Execute pg_wal_replay_resume() to continue.