Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7134c80

Browse files
author
Commitfest Bot
committed
[CF 4997] Return pg_control from pg_backup_stop().
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/4997 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/edc09339-3a4c-4195-8c06-7aa2953ca5be@pgbackrest.org Author(s): David Steele
2 parents 05883bd + 15c5409 commit 7134c80

File tree

16 files changed

+181
-30
lines changed

16 files changed

+181
-30
lines changed

doc/src/sgml/backup.sgml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,9 +1027,12 @@ SELECT * FROM pg_backup_stop(wait_for_archive => true);
10271027
values. The second of these fields should be written to a file named
10281028
<filename>backup_label</filename> in the root directory of the backup. The
10291029
third field should be written to a file named
1030-
<filename>tablespace_map</filename> unless the field is empty. These files are
1030+
<filename>tablespace_map</filename> unless the field is empty. The fourth
1031+
field should be written into a file named
1032+
<filename>global/pg_control</filename> (overwriting the existing file when
1033+
present). These files are
10311034
vital to the backup working and must be written byte for byte without
1032-
modification, which may require opening the file in binary mode.
1035+
modification, which will require opening the file in binary mode.
10331036
</para>
10341037
</listitem>
10351038
<listitem>
@@ -1101,7 +1104,16 @@ SELECT * FROM pg_backup_stop(wait_for_archive => true);
11011104
</para>
11021105

11031106
<para>
1104-
You should, however, omit from the backup the files within the
1107+
You should exclude <filename>global/pg_control</filename> from your backup
1108+
and put the contents of the <parameter>controlfile</parameter> column
1109+
returned from <function>pg_backup_stop</function> in your backup at
1110+
<filename>global/pg_control</filename>. This version of pg_control contains
1111+
safeguards against recovery without backup_label present and is guaranteed
1112+
not to be torn.
1113+
</para>
1114+
1115+
<para>
1116+
You should also omit from the backup the files within the
11051117
cluster's <filename>pg_wal/</filename> subdirectory. This
11061118
slight adjustment is worthwhile because it reduces the risk
11071119
of mistakes when restoring. This is easy to arrange if

doc/src/sgml/func.sgml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28256,6 +28256,11 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
2825628256
<entry><type>boolean</type></entry>
2825728257
</row>
2825828258

28259+
<row>
28260+
<entry><structfield>backup_label_required</structfield></entry>
28261+
<entry><type>boolean</type></entry>
28262+
</row>
28263+
2825928264
</tbody>
2826028265
</tgroup>
2826128266
</table>
@@ -29151,8 +29156,9 @@ stats_timestamp | 2025-03-24 13:55:47.796698+01
2915129156
The result of the function is a single record.
2915229157
The <parameter>lsn</parameter> column holds the backup's ending
2915329158
write-ahead log location (which again can be ignored). The second
29154-
column returns the contents of the backup label file, and the third
29155-
column returns the contents of the tablespace map file. These must be
29159+
column returns the contents of the backup label file, the third column
29160+
returns the contents of the tablespace map file, and the fourth column
29161+
returns the contents of pg_control. These must be
2915629162
stored as part of the backup and are required as part of the restore
2915729163
process.
2915829164
</para>

src/backend/access/transam/xlog.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9590,6 +9590,29 @@ do_pg_abort_backup(int code, Datum arg)
95909590
}
95919591
}
95929592

9593+
/*
9594+
* Create a consistent copy of control data to be used for backup and update it
9595+
* to require a backup label for recovery. Also recalculate the CRC.
9596+
*/
9597+
void
9598+
backup_control_file(uint8_t *controlFile)
9599+
{
9600+
ControlFileData *controlData = ((ControlFileData *)controlFile);
9601+
9602+
memset(controlFile + sizeof(ControlFileData), 0,
9603+
PG_CONTROL_FILE_SIZE - sizeof(ControlFileData));
9604+
9605+
LWLockAcquire(ControlFileLock, LW_SHARED);
9606+
memcpy(controlFile, ControlFile, sizeof(ControlFileData));
9607+
LWLockRelease(ControlFileLock);
9608+
9609+
controlData->backupLabelRequired = true;
9610+
9611+
INIT_CRC32C(controlData->crc);
9612+
COMP_CRC32C(controlData->crc, controlFile, offsetof(ControlFileData, crc));
9613+
FIN_CRC32C(controlData->crc);
9614+
}
9615+
95939616
/*
95949617
* Register a handler that will warn about unterminated backups at end of
95959618
* session, unless this has already been done.

src/backend/access/transam/xlogfuncs.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,26 @@ pg_backup_start(PG_FUNCTION_ARGS)
113113
*
114114
* The backup_label contains the user-supplied label string (typically this
115115
* would be used to tell where the backup dump will be stored), the starting
116-
* time, starting WAL location for the dump and so on. It is the caller's
117-
* responsibility to write the backup_label and tablespace_map files in the
118-
* data folder that will be restored from this backup.
116+
* time, starting WAL location for the dump and so on. The pg_control file
117+
* contains a consistent copy of pg_control that also has a safeguard against
118+
* being used without backup_label. It is the caller's responsibility to write
119+
* the backup_label, pg_control, and tablespace_map files in the data folder
120+
* that will be restored from this backup.
119121
*
120122
* Permission checking for this function is managed through the normal
121123
* GRANT system.
122124
*/
123125
Datum
124126
pg_backup_stop(PG_FUNCTION_ARGS)
125127
{
126-
#define PG_BACKUP_STOP_V2_COLS 3
128+
#define PG_BACKUP_STOP_V2_COLS 4
127129
TupleDesc tupdesc;
128130
Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
129131
bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
130132
bool waitforarchive = PG_GETARG_BOOL(0);
131133
char *backup_label;
134+
bytea *pg_control_bytea;
135+
uint8_t pg_control[PG_CONTROL_FILE_SIZE];
132136
SessionBackupState status = get_backup_status();
133137

134138
/* Initialize attributes information in the tuple descriptor */
@@ -150,9 +154,17 @@ pg_backup_stop(PG_FUNCTION_ARGS)
150154
/* Build the contents of backup_label */
151155
backup_label = build_backup_content(backup_state, false);
152156

157+
/* Build the contents of pg_control */
158+
backup_control_file(pg_control);
159+
160+
pg_control_bytea = (bytea *) palloc(PG_CONTROL_FILE_SIZE + VARHDRSZ);
161+
SET_VARSIZE(pg_control_bytea, PG_CONTROL_FILE_SIZE + VARHDRSZ);
162+
memcpy(VARDATA(pg_control_bytea), pg_control, PG_CONTROL_FILE_SIZE);
163+
153164
values[0] = LSNGetDatum(backup_state->stoppoint);
154165
values[1] = CStringGetTextDatum(backup_label);
155166
values[2] = CStringGetTextDatum(tablespace_map->data);
167+
values[3] = PointerGetDatum(pg_control_bytea);
156168

157169
/* Deallocate backup-related variables */
158170
pfree(backup_label);

src/backend/access/transam/xlogrecovery.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,14 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
709709
}
710710
else
711711
{
712-
/* No backup_label file has been found if we are here. */
712+
/*
713+
* No backup_label file has been found if we are here. Error if the
714+
* control file requires backup_label.
715+
*/
716+
if (ControlFile->backupLabelRequired)
717+
ereport(FATAL,
718+
(errmsg("could not find backup_label required for recovery"),
719+
errhint("backup_label must be present for recovery to proceed")));
713720

714721
/*
715722
* If tablespace_map file is present without backup_label file, there
@@ -984,6 +991,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
984991
{
985992
ControlFile->backupStartPoint = checkPoint.redo;
986993
ControlFile->backupEndRequired = backupEndRequired;
994+
ControlFile->backupLabelRequired = false;
987995

988996
if (backupFromStandby)
989997
{

src/backend/backup/basebackup.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "backup/basebackup_incremental.h"
2424
#include "backup/basebackup_sink.h"
2525
#include "backup/basebackup_target.h"
26+
#include "catalog/pg_control.h"
2627
#include "catalog/pg_tablespace_d.h"
2728
#include "commands/defrem.h"
2829
#include "common/compression.h"
@@ -326,9 +327,9 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
326327

327328
if (ti->path == NULL)
328329
{
329-
struct stat statbuf;
330330
bool sendtblspclinks = true;
331331
char *backup_label;
332+
uint8_t controlFile[PG_CONTROL_FILE_SIZE];
332333

333334
bbsink_begin_archive(sink, "base.tar");
334335

@@ -351,14 +352,10 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
351352
sendtblspclinks, &manifest, InvalidOid, ib);
352353

353354
/* ... and pg_control after everything else. */
354-
if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
355-
ereport(ERROR,
356-
(errcode_for_file_access(),
357-
errmsg("could not stat file \"%s\": %m",
358-
XLOG_CONTROL_FILE)));
359-
sendFile(sink, XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf,
360-
false, InvalidOid, InvalidOid,
361-
InvalidRelFileNumber, 0, &manifest, 0, NULL, 0);
355+
backup_control_file(controlFile);
356+
sendFileWithContent(sink, XLOG_CONTROL_FILE,
357+
(char *)controlFile, PG_CONTROL_FILE_SIZE,
358+
&manifest);
362359
}
363360
else
364361
{

src/backend/catalog/system_functions.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ CREATE OR REPLACE FUNCTION
390390

391391
CREATE OR REPLACE FUNCTION pg_backup_stop (
392392
wait_for_archive boolean DEFAULT true, OUT lsn pg_lsn,
393-
OUT labelfile text, OUT spcmapfile text)
393+
OUT labelfile text, OUT spcmapfile text, OUT controlfile bytea)
394394
RETURNS record STRICT VOLATILE LANGUAGE internal as 'pg_backup_stop'
395395
PARALLEL RESTRICTED;
396396

src/backend/utils/misc/pg_controldata.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
162162
Datum
163163
pg_control_recovery(PG_FUNCTION_ARGS)
164164
{
165-
Datum values[5];
166-
bool nulls[5];
165+
Datum values[6];
166+
bool nulls[6];
167167
TupleDesc tupdesc;
168168
HeapTuple htup;
169169
ControlFileData *ControlFile;
@@ -195,6 +195,9 @@ pg_control_recovery(PG_FUNCTION_ARGS)
195195
values[4] = BoolGetDatum(ControlFile->backupEndRequired);
196196
nulls[4] = false;
197197

198+
values[5] = BoolGetDatum(ControlFile->backupLabelRequired);
199+
nulls[5] = false;
200+
198201
htup = heap_form_tuple(tupdesc, values, nulls);
199202

200203
PG_RETURN_DATUM(HeapTupleGetDatum(htup));

src/bin/pg_controldata/pg_controldata.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ main(int argc, char *argv[])
294294
LSN_FORMAT_ARGS(ControlFile->backupEndPoint));
295295
printf(_("End-of-backup record required: %s\n"),
296296
ControlFile->backupEndRequired ? _("yes") : _("no"));
297+
printf(_("Backup label required: %s\n"),
298+
ControlFile->backupLabelRequired ? _("yes") : _("no"));
297299
printf(_("wal_level setting: %s\n"),
298300
wal_level_str(ControlFile->wal_level));
299301
printf(_("wal_log_hints setting: %s\n"),

src/bin/pg_resetwal/pg_resetwal.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ RewriteControlFile(void)
899899
ControlFile.backupStartPoint = 0;
900900
ControlFile.backupEndPoint = 0;
901901
ControlFile.backupEndRequired = false;
902+
ControlFile.backupLabelRequired = false;
902903

903904
/*
904905
* Force the defaults for max_* settings. The values don't really matter

src/bin/pg_rewind/pg_rewind.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ perform_rewind(filemap_t *filemap, rewind_source *source,
727727
ControlFile_new.minRecoveryPoint = endrec;
728728
ControlFile_new.minRecoveryPointTLI = endtli;
729729
ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
730+
ControlFile_new.backupLabelRequired = true;
730731
if (!dry_run)
731732
update_controlfile(datadir_target, &ControlFile_new, do_sync);
732733
}

src/include/access/xlog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ extern void do_pg_backup_start(const char *backupidstr, bool fast,
296296
StringInfo tblspcmapfile);
297297
extern void do_pg_backup_stop(BackupState *state, bool waitforarchive);
298298
extern void do_pg_abort_backup(int code, Datum arg);
299+
extern void backup_control_file(uint8_t *controlFile);
299300
extern void register_persistent_abort_backup_handler(void);
300301
extern SessionBackupState get_backup_status(void);
301302

src/include/catalog/pg_control.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,16 @@ typedef struct ControlFileData
164164
* If backupEndRequired is true, we know for sure that we're restoring
165165
* from a backup, and must see a backup-end record before we can safely
166166
* start up.
167+
*
168+
* If backupLabelRequired is true, then a backup_label file must be
169+
* present in order for recovery to proceed.
167170
*/
168171
XLogRecPtr minRecoveryPoint;
169172
TimeLineID minRecoveryPointTLI;
170173
XLogRecPtr backupStartPoint;
171174
XLogRecPtr backupEndPoint;
172175
bool backupEndRequired;
176+
bool backupLabelRequired;
173177

174178
/*
175179
* Parameter settings that determine if the WAL can be used for archival

src/include/catalog/pg_proc.dat

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6699,8 +6699,8 @@
66996699
{ oid => '2739', descr => 'finish taking an online backup',
67006700
proname => 'pg_backup_stop', provolatile => 'v', proparallel => 'r',
67016701
prorettype => 'record', proargtypes => 'bool',
6702-
proallargtypes => '{bool,pg_lsn,text,text}', proargmodes => '{i,o,o,o}',
6703-
proargnames => '{wait_for_archive,lsn,labelfile,spcmapfile}',
6702+
proallargtypes => '{bool,pg_lsn,text,text,bytea}', proargmodes => '{i,o,o,o,o}',
6703+
proargnames => '{wait_for_archive,lsn,labelfile,spcmapfile,controlfile}',
67046704
prosrc => 'pg_backup_stop' },
67056705
{ oid => '3436', descr => 'promote standby server',
67066706
proname => 'pg_promote', provolatile => 'v', prorettype => 'bool',
@@ -12304,9 +12304,9 @@
1230412304
{ oid => '3443',
1230512305
descr => 'pg_controldata recovery state information as a function',
1230612306
proname => 'pg_control_recovery', provolatile => 'v', prorettype => 'record',
12307-
proargtypes => '', proallargtypes => '{pg_lsn,int4,pg_lsn,pg_lsn,bool}',
12308-
proargmodes => '{o,o,o,o,o}',
12309-
proargnames => '{min_recovery_end_lsn,min_recovery_end_timeline,backup_start_lsn,backup_end_lsn,end_of_backup_record_required}',
12307+
proargtypes => '', proallargtypes => '{pg_lsn,int4,pg_lsn,pg_lsn,bool,bool}',
12308+
proargmodes => '{o,o,o,o,o,o}',
12309+
proargnames => '{min_recovery_end_lsn,min_recovery_end_timeline,backup_start_lsn,backup_end_lsn,end_of_backup_record_required,backup_label_required}',
1231012310
prosrc => 'pg_control_recovery' },
1231112311

1231212312
{ oid => '3444',

src/test/recovery/t/002_archiving.pl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@
4141
archive_cleanup_command = 'echo archive_cleanup_done > $archive_cleanup_command_file'
4242
recovery_end_command = 'echo recovery_ended_done > $recovery_end_command_file'
4343
));
44+
45+
# Rename backup_label to verify that recovery will not start without it
46+
rename("$data_dir/backup_label", "$data_dir/backup_label.tmp")
47+
or BAIL_OUT "could not move $data_dir/backup_label";
48+
49+
my $res = run_log(
50+
[
51+
'pg_ctl', '-D', $node_standby->data_dir, '-l',
52+
$node_standby->logfile, 'start'
53+
]);
54+
ok(!$res, 'invalid recovery startup fails');
55+
56+
my $logfile = slurp_file($node_standby->logfile());
57+
ok($logfile =~ qr/could not find backup_label required for recovery/,
58+
'could not find backup_label required for recovery');
59+
60+
# Restore backup_label so recovery proceeds normally
61+
rename("$data_dir/backup_label.tmp", "$data_dir/backup_label")
62+
or BAIL_OUT "could not move $data_dir/backup_label";
63+
4464
$node_standby->start;
4565

4666
# Create some content on primary

0 commit comments

Comments
 (0)