Re: Possible race condition in pg_basebackup

From: Andrey Borodin <x4mmm(at)yandex-team(dot)ru>
To: Nick Ivanov <nick(dot)ivanov(at)enterprisedb(dot)com>
Cc: pgsql-hackers mailing list <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Possible race condition in pg_basebackup
Date: 2026-08-21 11:28:22
Message-ID: ECFEEF21-61E9-4068-9D61-C33C69DCC270@yandex-team.ru
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

> On 21 Aug 2026, at 13:30, Nick Ivanov <nick(dot)ivanov(at)enterprisedb(dot)com> wrote:

Hi Nick,

I think your analysis is correct. More precisely, the problematic
sequence seems to be:

1. The first BASE_BACKUP takes its starting checkpoint and returns its
REDO location as xlogstart.
2. There is no slot protecting xlogstart yet.
3. Another checkpoint occurs before the slot is created. In your test it
is forced by another BASE_BACKUP, but it could also be a scheduled or
manually requested checkpoint.
4. That checkpoint can recycle the segment containing xlogstart.
5. Only then does the first pg_basebackup enter StartLogStreamer(), create
its slot with RESERVE_WAL, and request WAL starting at xlogstart.

RESERVE_WAL can only reserve WAL which still exists when the slot is
created. It cannot retrospectively protect the earlier base-backup start
point. The active-backup counter established by pg_backup_start() makes
WAL contain the required full-page images; it does not retain WAL
segments.

Interestingly, essentially this race was noticed during the 2017 review
of --create-slot. Jeff Janes asked whether the slot had to be created
before the checkpoint because streaming starts at the checkpoint's REDO
location [0]. It was thought that a subsequent checkpoint before the WAL
streamer connected was unlikely enough that the concern might be only
theoretical [1]. Your concurrent backups make that subsequent-checkpoint
scenario easy to hit, but the identity of whoever requested the checkpoint
is not important. A base backup must not depend on no checkpoint occurring
in this interval.

There is also a mismatch with the current documentation, which says that
--create-slot creates the slot "before starting the backup" [2]. In the
current code, pg_basebackup sends BASE_BACKUP and obtains xlogstart before
StartLogStreamer() creates the slot.

I tried a small client-side proof-of-concept which creates the slot before
BASE_BACKUP. It closes this window, but leaves the slot inactive during
the starting checkpoint, which interacts badly with
idle_replication_slot_timeout. It also does not protect an existing slot
whose restart_lsn is still NULL. Therefore, I think this needs a design
discussion rather than just moving client-side calls around.

Perhaps BASE_BACKUP should retain WAL from the exact REDO location of the
checkpoint it selected until the WAL streamer has taken over. This also
handles backups from a standby, where the selected restartpoint may be
older than the current replay location. The server knows the exact LSN
and can release such a backup-owned retention horizon on backup completion
or abort, without transferring a temporary slot between the two client
connections.

Pre-creating the physical slots with WAL reserved is a reliable workaround.
A positive wal_keep_size also makes the window less likely, but is a
size-based cushion rather than protection tied to these backups.

As an operational aside, when provisioning several replicas at once I
would normally store one reusable base backup with WAL-G or pgBackRest and
restore it several times. That avoids making the primary transmit the
same cluster and take several backup checkpoints. But this does not make
the pg_basebackup race acceptable; I think you found a real bug.

Thank you!

Best regards, Andrey Borodin.

[0] https://www.postgresql.org/message-id/1505248760.16872.1.camel%40credativ.de
[1] https://www.postgresql.org/message-id/CAMkU%3D1z_OucAFdersKxqjKsNUwFCrjvqV0%3Dd-5o9Q17nR53s_g%40mail.gmail.com
[2] https://www.postgresql.org/docs/current/app-pgbasebackup.html

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Andres Freund 2026-08-21 11:32:14 Re: Tracking role modification timestamps in pg_authid / pg_roles
Previous Message Andrew Dunstan 2026-08-21 11:05:15 Re: Tracking role modification timestamps in pg_authid / pg_roles