Skip to content

Commit 7117685

Browse files
committed
Implement backup API functions for non-exclusive backups
Previously non-exclusive backups had to be done using the replication protocol and pg_basebackup. With this commit it's now possible to make them using pg_start_backup/pg_stop_backup as well, as long as the backup program can maintain a persistent connection to the database. Doing this, backup_label and tablespace_map are returned as results from pg_stop_backup() instead of being written to the data directory. This makes the server safe from a crash during an ongoing backup, which can be a problem with exclusive backups. The old syntax of the functions remain and work exactly as before, but since the new syntax is safer this should eventually be deprecated and removed. Only reference documentation is included. The main section on backup still needs to be rewritten to cover this, but since that is already scheduled for a separate large rewrite, it's not included in this patch. Reviewed by David Steele and Amit Kapila
1 parent 9457b59 commit 7117685

File tree

9 files changed

+265
-53
lines changed

9 files changed

+265
-53
lines changed

doc/src/sgml/func.sgml

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17478,7 +17478,7 @@ SELECT set_config('log_statement_stats', 'off', false);
1747817478
</row>
1747917479
<row>
1748017480
<entry>
17481-
<literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal>
17481+
<literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> <optional>, <parameter>exclusive</> <type>boolean</> </optional></optional>)</function></literal>
1748217482
</entry>
1748317483
<entry><type>pg_lsn</type></entry>
1748417484
<entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry>
@@ -17488,7 +17488,14 @@ SELECT set_config('log_statement_stats', 'off', false);
1748817488
<literal><function>pg_stop_backup()</function></literal>
1748917489
</entry>
1749017490
<entry><type>pg_lsn</type></entry>
17491-
<entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry>
17491+
<entry>Finish performing exclusive on-line backup (restricted to superusers or replication roles)</entry>
17492+
</row>
17493+
<row>
17494+
<entry>
17495+
<literal><function>pg_stop_backup(<parameter>exclusive</> <type>boolean</>)</function></literal>
17496+
</entry>
17497+
<entry><type>setof record</type></entry>
17498+
<entry>Finish performing exclusive or non-exclusive on-line backup (restricted to superusers or replication roles)</entry>
1749217499
</row>
1749317500
<row>
1749417501
<entry>
@@ -17537,15 +17544,19 @@ SELECT set_config('log_statement_stats', 'off', false);
1753717544
</table>
1753817545

1753917546
<para>
17540-
<function>pg_start_backup</> accepts an
17541-
arbitrary user-defined label for the backup. (Typically this would be
17542-
the name under which the backup dump file will be stored.) The function
17543-
writes a backup label file (<filename>backup_label</>) and, if there
17544-
are any links in the <filename>pg_tblspc/</> directory, a tablespace map
17545-
file (<filename>tablespace_map</>) into the database cluster's data
17546-
directory, performs a checkpoint, and then returns the backup's starting
17547-
transaction log location as text. The user can ignore this result value,
17548-
but it is provided in case it is useful.
17547+
<function>pg_start_backup</> accepts an arbitrary user-defined label for
17548+
the backup. (Typically this would be the name under which the backup dump
17549+
file will be stored.) When used in exclusive mode, the function writes a
17550+
backup label file (<filename>backup_label</>) and, if there are any links
17551+
in the <filename>pg_tblspc/</> directory, a tablespace map file
17552+
(<filename>tablespace_map</>) into the database cluster's data directory,
17553+
performs a checkpoint, and then returns the backup's starting transaction
17554+
log location as text. The user can ignore this result value, but it is
17555+
provided in case it is useful. When used in non-exclusive mode, the
17556+
contents of these files are instead returned by the
17557+
<function>pg_stop_backup</> function, and should be written to the backup
17558+
by the caller.
17559+
1754917560
<programlisting>
1755017561
postgres=# select pg_start_backup('label_goes_here');
1755117562
pg_start_backup
@@ -17560,10 +17571,17 @@ postgres=# select pg_start_backup('label_goes_here');
1756017571
</para>
1756117572

1756217573
<para>
17563-
<function>pg_stop_backup</> removes the label file and, if it exists,
17564-
the <filename>tablespace_map</> file created by
17565-
<function>pg_start_backup</>, and creates a backup history file in
17566-
the transaction log archive area. The history file includes the label given to
17574+
In an exclusive backup, <function>pg_stop_backup</> removes the label file
17575+
and, if it exists, the <filename>tablespace_map</> file created by
17576+
<function>pg_start_backup</>. In a non-exclusive backup, the contents of
17577+
the <filename>backup_label</> and <filename>tablespace_map</> are returned
17578+
in the result of the function, and should be written to files in the
17579+
backup (and not in the data directory).
17580+
</para>
17581+
17582+
<para>
17583+
The function also creates a backup history file in the transaction log
17584+
archive area. The history file includes the label given to
1756717585
<function>pg_start_backup</>, the starting and ending transaction log locations for
1756817586
the backup, and the starting and ending times of the backup. The return
1756917587
value is the backup's ending transaction log location (which again

src/backend/access/transam/xlog.c

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9768,8 +9768,8 @@ XLogFileNameP(TimeLineID tli, XLogSegNo segno)
97689768
*/
97699769
XLogRecPtr
97709770
do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
9771-
char **labelfile, DIR *tblspcdir, List **tablespaces,
9772-
char **tblspcmapfile, bool infotbssize,
9771+
StringInfo labelfile, DIR *tblspcdir, List **tablespaces,
9772+
StringInfo tblspcmapfile, bool infotbssize,
97739773
bool needtblspcmapfile)
97749774
{
97759775
bool exclusive = (labelfile == NULL);
@@ -9783,8 +9783,6 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
97839783
XLogSegNo _logSegNo;
97849784
struct stat stat_buf;
97859785
FILE *fp;
9786-
StringInfoData labelfbuf;
9787-
StringInfoData tblspc_mapfbuf;
97889786

97899787
backup_started_in_recovery = RecoveryInProgress();
97909788

@@ -9981,7 +9979,8 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
99819979
/*
99829980
* Construct tablespace_map file
99839981
*/
9984-
initStringInfo(&tblspc_mapfbuf);
9982+
if (exclusive)
9983+
tblspcmapfile = makeStringInfo();
99859984

99869985
datadirpathlen = strlen(DataDir);
99879986

@@ -10054,7 +10053,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1005410053
if (tablespaces)
1005510054
*tablespaces = lappend(*tablespaces, ti);
1005610055

10057-
appendStringInfo(&tblspc_mapfbuf, "%s %s\n", ti->oid, ti->path);
10056+
appendStringInfo(tblspcmapfile, "%s %s\n", ti->oid, ti->path);
1005810057

1005910058
pfree(buflinkpath.data);
1006010059
#else
@@ -10073,23 +10072,24 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1007310072
/*
1007410073
* Construct backup label file
1007510074
*/
10076-
initStringInfo(&labelfbuf);
10075+
if (exclusive)
10076+
labelfile = makeStringInfo();
1007710077

1007810078
/* Use the log timezone here, not the session timezone */
1007910079
stamp_time = (pg_time_t) time(NULL);
1008010080
pg_strftime(strfbuf, sizeof(strfbuf),
1008110081
"%Y-%m-%d %H:%M:%S %Z",
1008210082
pg_localtime(&stamp_time, log_timezone));
10083-
appendStringInfo(&labelfbuf, "START WAL LOCATION: %X/%X (file %s)\n",
10083+
appendStringInfo(labelfile, "START WAL LOCATION: %X/%X (file %s)\n",
1008410084
(uint32) (startpoint >> 32), (uint32) startpoint, xlogfilename);
10085-
appendStringInfo(&labelfbuf, "CHECKPOINT LOCATION: %X/%X\n",
10085+
appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n",
1008610086
(uint32) (checkpointloc >> 32), (uint32) checkpointloc);
10087-
appendStringInfo(&labelfbuf, "BACKUP METHOD: %s\n",
10087+
appendStringInfo(labelfile, "BACKUP METHOD: %s\n",
1008810088
exclusive ? "pg_start_backup" : "streamed");
10089-
appendStringInfo(&labelfbuf, "BACKUP FROM: %s\n",
10089+
appendStringInfo(labelfile, "BACKUP FROM: %s\n",
1009010090
backup_started_in_recovery ? "standby" : "master");
10091-
appendStringInfo(&labelfbuf, "START TIME: %s\n", strfbuf);
10092-
appendStringInfo(&labelfbuf, "LABEL: %s\n", backupidstr);
10091+
appendStringInfo(labelfile, "START TIME: %s\n", strfbuf);
10092+
appendStringInfo(labelfile, "LABEL: %s\n", backupidstr);
1009310093

1009410094
/*
1009510095
* Okay, write the file, or return its contents to caller.
@@ -10123,7 +10123,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1012310123
(errcode_for_file_access(),
1012410124
errmsg("could not create file \"%s\": %m",
1012510125
BACKUP_LABEL_FILE)));
10126-
if (fwrite(labelfbuf.data, labelfbuf.len, 1, fp) != 1 ||
10126+
if (fwrite(labelfile->data, labelfile->len, 1, fp) != 1 ||
1012710127
fflush(fp) != 0 ||
1012810128
pg_fsync(fileno(fp)) != 0 ||
1012910129
ferror(fp) ||
@@ -10132,10 +10132,12 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1013210132
(errcode_for_file_access(),
1013310133
errmsg("could not write file \"%s\": %m",
1013410134
BACKUP_LABEL_FILE)));
10135-
pfree(labelfbuf.data);
10135+
/* Allocated locally for exclusive backups, so free separately */
10136+
pfree(labelfile->data);
10137+
pfree(labelfile);
1013610138

1013710139
/* Write backup tablespace_map file. */
10138-
if (tblspc_mapfbuf.len > 0)
10140+
if (tblspcmapfile->len > 0)
1013910141
{
1014010142
if (stat(TABLESPACE_MAP, &stat_buf) != 0)
1014110143
{
@@ -10159,7 +10161,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1015910161
(errcode_for_file_access(),
1016010162
errmsg("could not create file \"%s\": %m",
1016110163
TABLESPACE_MAP)));
10162-
if (fwrite(tblspc_mapfbuf.data, tblspc_mapfbuf.len, 1, fp) != 1 ||
10164+
if (fwrite(tblspcmapfile->data, tblspcmapfile->len, 1, fp) != 1 ||
1016310165
fflush(fp) != 0 ||
1016410166
pg_fsync(fileno(fp)) != 0 ||
1016510167
ferror(fp) ||
@@ -10170,13 +10172,9 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1017010172
TABLESPACE_MAP)));
1017110173
}
1017210174

10173-
pfree(tblspc_mapfbuf.data);
10174-
}
10175-
else
10176-
{
10177-
*labelfile = labelfbuf.data;
10178-
if (tblspc_mapfbuf.len > 0)
10179-
*tblspcmapfile = tblspc_mapfbuf.data;
10175+
/* Allocated locally for exclusive backups, so free separately */
10176+
pfree(tblspcmapfile->data);
10177+
pfree(tblspcmapfile);
1018010178
}
1018110179
}
1018210180
PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
@@ -10283,7 +10281,16 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
1028310281
*/
1028410282
WALInsertLockAcquireExclusive();
1028510283
if (exclusive)
10284+
{
10285+
if (!XLogCtl->Insert.exclusiveBackup)
10286+
{
10287+
WALInsertLockRelease();
10288+
ereport(ERROR,
10289+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
10290+
errmsg("exclusive backup not in progress")));
10291+
}
1028610292
XLogCtl->Insert.exclusiveBackup = false;
10293+
}
1028710294
else
1028810295
{
1028910296
/*

0 commit comments

Comments
 (0)