Обсуждение: Return pg_control from pg_backup_stop().

Поиск
Список
Период
Сортировка

Return pg_control from pg_backup_stop().

От
David Steele
Дата:
Hackers,

This is greatly simplified implementation of the patch proposed in [1] 
and hopefully it addresses the concerns expressed there. Since the 
implementation is quite different it seemed like a new thread was 
appropriate, especially since the old thread title would be very 
misleading regarding the new functionality.

The basic idea is to harden recovery by returning a copy of pg_control 
from pg_backup_stop() that has a flag set to prevent recovery if the 
backup_label file is missing. Instead of backup software copying 
pg_control from PGDATA, it stores an updated version that is returned 
from pg_backup_stop(). This is better for the following reasons:

* The user can no longer remove backup_label and get what looks like a 
successful recovery (while almost certainly causing corruption). If 
backup_label is removed the cluster will not start. The user may try 
pg_resetwal, but that tool makes it pretty clear that corruption will 
result from its use.

* We don't need to worry about backup software seeing a torn copy of 
pg_control, since Postgres can safely read it out of memory and provide 
a valid copy via pg_backup_stop(). This solves torn reads without 
needing to write pg_control via a temp file, which may affect 
performance on a standby.

* For backup from standby, we no longer need to instruct the backup 
software to copy pg_control last. In fact the backup software should not 
copy pg_control from PGDATA at all.

These changes have no impact on current backup software and they are 
free to use the pg_control available from pg_stop_backup() or continue 
to use pg_control from PGDATA. Of course they will miss the benefits of 
getting a consistent copy of pg_control and the backup_label checking, 
but will be no worse off than before.

I'll register this in the July CF.

Regards,
-David

[1] 
https://www.postgresql.org/message-id/2daf8adc-8db7-4204-a7f2-a7e94e2bfa4b@pgmasters.net
Вложения

Re: Return pg_control from pg_backup_stop().

От
David Steele
Дата:
On 1/24/25 13:43, David Steele wrote:
> 
> Rebased and improved a comment and an error.
Rebased to fix breakage caused by the split of func.sgml in 4e23c9e.

Regards,
-David
Вложения

Re: Return pg_control from pg_backup_stop().

От
David Steele
Дата:
On 8/7/25 05:30, David Steele wrote:
> On 1/24/25 13:43, David Steele wrote:
>>
>> Rebased and improved a comment and an error.
> Rebased to fix breakage caused by the split of func.sgml in 4e23c9e.


Rebased to implement simplification added by "Simplify creation of 
built-in functions with default arguments" (759b03b2).

Regards,
-David



Re: Return pg_control from pg_backup_stop().

От
David Steele
Дата:
On 2/20/26 10:10, David Steele wrote:
> On 8/7/25 05:30, David Steele wrote:
>> On 1/24/25 13:43, David Steele wrote:
>>>
>>> Rebased and improved a comment and an error.
>> Rebased to fix breakage caused by the split of func.sgml in 4e23c9e.
> 
> 
> Rebased to implement simplification added by "Simplify creation of 
> built-in functions with default arguments" (759b03b2).

With the patches this time!

Regards,
-David
Вложения

Re: Return pg_control from pg_backup_stop().

От
David Steele
Дата:
On 2/20/26 12:47, David Steele wrote:
> On 2/20/26 10:10, David Steele wrote:
>> On 8/7/25 05:30, David Steele wrote:
>>> On 1/24/25 13:43, David Steele wrote:
>>>>
>>>> Rebased and improved a comment and an error.
>>> Rebased to fix breakage caused by the split of func.sgml in 4e23c9e.
>>
>>
>> Rebased to implement simplification added by "Simplify creation of 
>> built-in functions with default arguments" (759b03b2).

Rebased on "Simplify creation of built-in functions with non-default 
ACLs." (f95d73ed).

Regards,
-David
Вложения

Re: Return pg_control from pg_backup_stop().

От
Haibo Yan
Дата:
Hi David
I have not read the code yet, so this may already be answered there, but I had a question about the proposal itself. This patch protects against a missing backup_label, but what about a wrong one? If a user restores a backup_label file from a different backup, the existence check alone would not detect that. Do we need some consistency check between the returned pg_control copy and the backup_label contents, or is the intended scope here limited to the “missing file” case only?
Regards
Haibo

On Mar 5, 2026, at 5:27 PM, David Steele <david@pgbackrest.org> wrote:

On 2/20/26 12:47, David Steele wrote:
On 2/20/26 10:10, David Steele wrote:
On 8/7/25 05:30, David Steele wrote:
On 1/24/25 13:43, David Steele wrote:

Rebased and improved a comment and an error.
Rebased to fix breakage caused by the split of func.sgml in 4e23c9e.


Rebased to implement simplification added by "Simplify creation of built-in functions with default arguments" (759b03b2).

Rebased on "Simplify creation of built-in functions with non-default ACLs." (f95d73ed).

Regards,
-David<pgcontrol-flag-v8-01-basebackup.patch><pgcontrol-flag-v8-02-sql.patch>

Re: Return pg_control from pg_backup_stop().

От
Michael Paquier
Дата:
On Mon, Mar 16, 2026 at 10:16:58PM -0700, Haibo Yan wrote:
> I have not read the code yet, so this may already be answered there,
> but I had a question about the proposal itself. This patch protects
> against a missing backup_label, but what about a wrong one? If a
> user restores a backup_label file from a different backup, the
> existence check alone would not detect that. Do we need some
> consistency check between the returned pg_control copy and the
> backup_label contents, or is the intended scope here limited to the
> “missing file” case only?

Please note that we use bottom-posting on the lists, as of:
https://en.wikipedia.org/wiki/Posting_style#Bottom-posting
--
Michael

Вложения

Re: Return pg_control from pg_backup_stop().

От
David Steele
Дата:
On 3/17/26 12:16, Haibo Yan wrote:

> I have not read the code yet, so this may already be answered there, but 
> I had a question about the proposal itself. This patch protects against 
> a missing backup_label, but what about a wrong one? If a user restores a 
> backup_label file from a different backup, the existence check alone 
> would not detect that. Do we need some consistency check between the 
> returned pg_control copy and the backup_label contents, or is the 
> intended scope here limited to the “missing file” case only?

Thank you for having a look!

The goal here is only to check for a missing backup_label. The general 
problem is that PostgreSQL suggests that removing backup_label might be 
a good idea so the user does it:

If you are not restoring from a backup, try removing the file 
\"%s/backup_label\"

The user *could* copy a backup_label from another backup and there are 
ways we could detect that but I feel that should be material for a 
separate patch.

Regards,
-David





Re: Return pg_control from pg_backup_stop().

От
Haibo Yan
Дата:
Hi David,

Thank you for the clarification. I have now read the code, and overall it looks good to me. I only had one very small comment.

You currently have:
```
memset(controlFile + sizeof(ControlFileData), 0,
       PG_CONTROL_FILE_SIZE - sizeof(ControlFileData));
memcpy(controlFile, ControlFile, sizeof(ControlFileData));
``` 
This is correct, since only the trailing bytes need to be zeroed before the copy.

I was just wondering whether the following might be slightly clearer:
```
memset(controlFile, 0, PG_CONTROL_FILE_SIZE);
memcpy(controlFile, ControlFile, sizeof(ControlFileData));
```

I do not think this is a real issue, though.

Thanks
Haibo

On Mar 17, 2026, at 12:05 AM, David Steele <david@pgbackrest.org> wrote:

On 3/17/26 12:16, Haibo Yan wrote:

I have not read the code yet, so this may already be answered there, but I had a question about the proposal itself. This patch protects against a missing backup_label, but what about a wrong one? If a user restores a backup_label file from a different backup, the existence check alone would not detect that. Do we need some consistency check between the returned pg_control copy and the backup_label contents, or is the intended scope here limited to the “missing file” case only?

Thank you for having a look!

The goal here is only to check for a missing backup_label. The general problem is that PostgreSQL suggests that removing backup_label might be a good idea so the user does it:

If you are not restoring from a backup, try removing the file \"%s/backup_label\"

The user *could* copy a backup_label from another backup and there are ways we could detect that but I feel that should be material for a separate patch.

Regards,
-David



Re: Return pg_control from pg_backup_stop().

От
Michael Paquier
Дата:
On Tue, Mar 17, 2026 at 11:50:29AM -0700, Haibo Yan wrote:
> Thank you for the clarification. I have now read the code, and
> overall it looks good to me. I only had one very small comment.

(Bottom-posting note from above, please be careful.)

> I was just wondering whether the following might be slightly clearer:
> ```
> memset(controlFile, 0, PG_CONTROL_FILE_SIZE);
> memcpy(controlFile, ControlFile, sizeof(ControlFileData));
> ```
>
> I do not think this is a real issue, though.

         {
             ControlFile->backupStartPoint = checkPoint.redo;
             ControlFile->backupEndRequired = backupEndRequired;
+            ControlFile->backupLabelRequired = false;

It sounds like it is going to be important to document the reason why
the flag is reset here (aka we don't need the backup_label file
anymore).

+backup_control_file(uint8_t *controlFile)
+{
+    ControlFileData *controlData = ((ControlFileData *)controlFile);
+
+    memset(controlFile + sizeof(ControlFileData), 0,
+           PG_CONTROL_FILE_SIZE - sizeof(ControlFileData));
+
+    LWLockAcquire(ControlFileLock, LW_SHARED);
+    memcpy(controlFile, ControlFile, sizeof(ControlFileData));
+    LWLockRelease(ControlFileLock);
+
+    controlData->backupLabelRequired = true;
+
+    INIT_CRC32C(controlData->crc);
+    COMP_CRC32C(controlData->crc, controlFile, offsetof(ControlFileData, crc));
+    FIN_CRC32C(controlData->crc);

I was wondering if we should have an assertion at least to cross-check
that the contents we store in shared memory never go out-of-sync with
the on-disk contents, in the shape of a USE_ASSERT_CHECKING block that
calls get_controlfile() and memcmp()'s the contents between shmem and
the on-disk file, while the LWLock is taken.  We ship the control file
last on purpose, one reason being backups taken from standbys, so that
may be sensible to do.

Another property of the new control file flag that is implied in the
implementation but not documented is that we should never check for
backupLabelRequired when a backup_label is gone.  Actually, the flag
is reset in InitWalRecovery() in the initial steps of recovery, and
the backup_label file is removed much later in StartupXLOG() just
*after* UpdateControlFile() to minimize the window where the contents
of the control file and the backup_label file is removed are
out-of-sync.  This window means that if we crash between the
completion of UpdateControlFile() and the durable_rename() we could
have a flag reset with a backup_label still around.  On restart,
recovery would fail, requiring a manual modification of the control
file, at least.  It sounds to me that this implementation detail
should be documented clearly.

Finally, here is a general opinion.  I like this patch, and it is
basically risk-free for base backups taken with the replication
protocol as we update the control file with the new flag set
on-the-fly.

Now, I am worried about backups that use pg_stop_backup().  Changing
backup APIs has always been a very sensitive area, and this is going
to require operators to update backup tools so as the control file
received as a result of pg_stop_backup() is copied, at the cost of
getting a failure if they don't do so.  I will *not* proceed with this
change without a clear approval from some more committers or senior
hackers that they like this change (approach previously suggested by
Andres, actually, for what I can see).  I am adding in CC a few
committers who have commented on this set of proposals and who have
touched the recovery code in the last few years, for awareness.
The timing is what it is, and we are at the end of a release cycle.
Let's see if we can reach a consensus of some kind.
--
Michael

Вложения