more-readable-forcePageWrites.patch
text/x-diff
Filename: more-readable-forcePageWrites.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 198 | 2 |
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 60,67 ****
/* File path names (all relative to $PGDATA) */
- #define BACKUP_LABEL_FILE "backup_label"
- #define BACKUP_LABEL_OLD "backup_label.old"
#define RECOVERY_COMMAND_FILE "recovery.conf"
#define RECOVERY_COMMAND_DONE "recovery.done"
--- 60,65 ----
***************
*** 339,344 **** typedef struct XLogCtlInsert
--- 337,351 ----
char *currpos; /* current insertion point in cache */
XLogRecPtr RedoRecPtr; /* current redo point for insertions */
bool forcePageWrites; /* forcing full-page writes for PITR? */
+
+ /*
+ * exclusiveBackup is true if a backup started with pg_start_backup() is
+ * in progress, and nonExclusiveBackups is a counter indicating the number
+ * of streaming base backups currently in progress. forcePageWrites is
+ * set to true, when either of these is non-zero.
+ */
+ bool exclusiveBackup;
+ int nonExclusiveBackups;
} XLogCtlInsert;
/*
***************
*** 8352,8367 **** pg_start_backup(PG_FUNCTION_ARGS)
backupidstr = text_to_cstring(backupid);
! startpoint = do_pg_start_backup(backupidstr, fast);
snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X",
startpoint.xlogid, startpoint.xrecoff);
PG_RETURN_TEXT_P(cstring_to_text(startxlogstr));
}
XLogRecPtr
! do_pg_start_backup(const char *backupidstr, bool fast)
{
XLogRecPtr checkpointloc;
XLogRecPtr startpoint;
pg_time_t stamp_time;
--- 8359,8396 ----
backupidstr = text_to_cstring(backupid);
! startpoint = do_pg_start_backup(backupidstr, fast, NULL);
snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X",
startpoint.xlogid, startpoint.xrecoff);
PG_RETURN_TEXT_P(cstring_to_text(startxlogstr));
}
+ /*
+ * do_pg_start_backup is the workhorse of the user-visible pg_start_backup()
+ * function. It creates the necessary starting checkpoint and constructs the
+ * backup label file.
+ *
+ * There are two kind of backups: exclusive and non-exclusive. An exclusive
+ * backup is started with pg_start_backup(), and there can be only one active
+ * at a time. The backup label file of an exclusive backup is written to
+ * $PGDATA/backup_label, and it is removed by pg_stop_backup().
+ *
+ * A non-exclusive backup is used for the streaming base backups (see
+ * src/backend/replication/basebackup.c). The difference to exclusive backups
+ * is that the backup label file is not written to disk. Instead, its would-be
+ * contents are returned in *labelfile, and the caller is responsible for
+ * including it in the backup archive as 'backup_label'. There can be many
+ * non-exclusive backups active at the same time, and they don't conflict
+ * with an exclusive backup either.
+ *
+ * Every successfully started non-exclusive backup must be stopped by calling
+ * do_pg_stop_backup() or do_pg_abort_backup().
+ */
XLogRecPtr
! do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile)
{
+ bool exclusive = (labelfile == NULL);
XLogRecPtr checkpointloc;
XLogRecPtr startpoint;
pg_time_t stamp_time;
***************
*** 8371,8381 **** do_pg_start_backup(const char *backupidstr, bool fast)
uint32 _logSeg;
struct stat stat_buf;
FILE *fp;
! if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser or replication role to run a backup")));
if (RecoveryInProgress())
ereport(ERROR,
--- 8400,8411 ----
uint32 _logSeg;
struct stat stat_buf;
FILE *fp;
+ StringInfoData labelfbuf;
! if (exclusive && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser to run a backup")));
if (RecoveryInProgress())
ereport(ERROR,
***************
*** 8389,8394 **** do_pg_start_backup(const char *backupidstr, bool fast)
--- 8419,8430 ----
errmsg("WAL level not sufficient for making an online backup"),
errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start.")));
+ if (strlen(backupidstr) > MAXPGPATH)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("backup label too long (max %d bytes)",
+ MAXPGPATH)));
+
/*
* Mark backup active in shared memory. We must do full-page WAL writes
* during an on-line backup even if not doing so at other times, because
***************
*** 8407,8420 **** do_pg_start_backup(const char *backupidstr, bool fast)
* ensure adequate interlocking against XLogInsert().
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! if (XLogCtl->Insert.forcePageWrites)
{
! LWLockRelease(WALInsertLock);
! ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("a backup is already in progress"),
! errhint("Run pg_stop_backup() and try again.")));
}
XLogCtl->Insert.forcePageWrites = true;
LWLockRelease(WALInsertLock);
--- 8443,8462 ----
* ensure adequate interlocking against XLogInsert().
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! if (exclusive)
{
! if (XLogCtl->Insert.exclusiveBackup)
! {
! LWLockRelease(WALInsertLock);
! ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("a backup is already in progress"),
! errhint("Run pg_stop_backup() and try again.")));
! }
! XLogCtl->Insert.exclusiveBackup = true;
}
+ else
+ XLogCtl->Insert.nonExclusiveBackups++;
XLogCtl->Insert.forcePageWrites = true;
LWLockRelease(WALInsertLock);
***************
*** 8432,8438 **** do_pg_start_backup(const char *backupidstr, bool fast)
RequestXLogSwitch();
/* Ensure we release forcePageWrites if fail below */
! PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) 0);
{
/*
* Force a CHECKPOINT. Aside from being necessary to prevent torn
--- 8474,8480 ----
RequestXLogSwitch();
/* Ensure we release forcePageWrites if fail below */
! PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
{
/*
* Force a CHECKPOINT. Aside from being necessary to prevent torn
***************
*** 8459,8512 **** do_pg_start_backup(const char *backupidstr, bool fast)
XLByteToSeg(startpoint, _logId, _logSeg);
XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg);
/* Use the log timezone here, not the session timezone */
stamp_time = (pg_time_t) time(NULL);
pg_strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, log_timezone));
/*
! * Check for existing backup label --- implies a backup is already
! * running. (XXX given that we checked forcePageWrites above, maybe
! * it would be OK to just unlink any such label file?)
*/
! if (stat(BACKUP_LABEL_FILE, &stat_buf) != 0)
{
! if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
! errmsg("could not stat file \"%s\": %m",
BACKUP_LABEL_FILE)));
}
else
! ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("a backup is already in progress"),
! errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.",
! BACKUP_LABEL_FILE)));
!
! /*
! * Okay, write the file
! */
! fp = AllocateFile(BACKUP_LABEL_FILE, "w");
! if (!fp)
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not create file \"%s\": %m",
! BACKUP_LABEL_FILE)));
! fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n",
! startpoint.xlogid, startpoint.xrecoff, xlogfilename);
! fprintf(fp, "CHECKPOINT LOCATION: %X/%X\n",
! checkpointloc.xlogid, checkpointloc.xrecoff);
! fprintf(fp, "START TIME: %s\n", strfbuf);
! fprintf(fp, "LABEL: %s\n", backupidstr);
! if (fflush(fp) || ferror(fp) || FreeFile(fp))
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not write file \"%s\": %m",
! BACKUP_LABEL_FILE)));
}
! PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) 0);
/*
* We're done. As a convenience, return the starting WAL location.
--- 8501,8567 ----
XLByteToSeg(startpoint, _logId, _logSeg);
XLogFileName(xlogfilename, ThisTimeLineID, _logId, _logSeg);
+ /*
+ * Construct backup label file
+ */
+ initStringInfo(&labelfbuf);
+
/* Use the log timezone here, not the session timezone */
stamp_time = (pg_time_t) time(NULL);
pg_strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, log_timezone));
+ appendStringInfo(&labelfbuf, "START WAL LOCATION: %X/%X (file %s)\n",
+ startpoint.xlogid, startpoint.xrecoff, xlogfilename);
+ appendStringInfo(&labelfbuf, "CHECKPOINT LOCATION: %X/%X\n",
+ checkpointloc.xlogid, checkpointloc.xrecoff);
+ appendStringInfo(&labelfbuf, "START TIME: %s\n", strfbuf);
+ appendStringInfo(&labelfbuf, "LABEL: %s\n", backupidstr);
/*
! * Okay, write the file, or return its contents to caller.
*/
! if (exclusive)
{
! /*
! * Check for existing backup label --- implies a backup is already
! * running. (XXX given that we checked exclusiveBackup above, maybe
! * it would be OK to just unlink any such label file?)
! */
! if (stat(BACKUP_LABEL_FILE, &stat_buf) != 0)
! {
! if (errno != ENOENT)
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not stat file \"%s\": %m",
! BACKUP_LABEL_FILE)));
! }
! else
! ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("a backup is already in progress"),
! errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.",
! BACKUP_LABEL_FILE)));
!
! fp = AllocateFile(BACKUP_LABEL_FILE, "w");
!
! if (!fp)
ereport(ERROR,
(errcode_for_file_access(),
! errmsg("could not create file \"%s\": %m",
BACKUP_LABEL_FILE)));
+ fwrite(labelfbuf.data, labelfbuf.len, 1, fp);
+ if (fflush(fp) || ferror(fp) || FreeFile(fp))
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not write file \"%s\": %m",
+ BACKUP_LABEL_FILE)));
+ pfree(labelfbuf.data);
}
else
! *labelfile = labelfbuf.data;
}
! PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
/*
* We're done. As a convenience, return the starting WAL location.
***************
*** 8518,8526 **** do_pg_start_backup(const char *backupidstr, bool fast)
static void
pg_start_backup_callback(int code, Datum arg)
{
! /* Turn off forcePageWrites on failure */
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! XLogCtl->Insert.forcePageWrites = false;
LWLockRelease(WALInsertLock);
}
--- 8573,8592 ----
static void
pg_start_backup_callback(int code, Datum arg)
{
! bool exclusive = DatumGetBool(arg);
!
! /* Update backup counters and forcePageWrites on failure */
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! if (exclusive)
! {
! Assert(XLogCtl->Insert.exclusiveBackup);
! XLogCtl->Insert.exclusiveBackup = false;
! }
! else
! XLogCtl->Insert.nonExclusiveBackups--;
!
! if (!XLogCtl->Insert.exclusiveBackup && XLogCtl->Insert.nonExclusiveBackups == 0)
! XLogCtl->Insert.forcePageWrites = false;
LWLockRelease(WALInsertLock);
}
***************
*** 8543,8558 **** pg_stop_backup(PG_FUNCTION_ARGS)
XLogRecPtr stoppoint;
char stopxlogstr[MAXFNAMELEN];
! stoppoint = do_pg_stop_backup();
snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X",
stoppoint.xlogid, stoppoint.xrecoff);
PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr));
}
XLogRecPtr
! do_pg_stop_backup(void)
{
XLogRecPtr startpoint;
XLogRecPtr stoppoint;
XLogRecData rdata;
--- 8609,8632 ----
XLogRecPtr stoppoint;
char stopxlogstr[MAXFNAMELEN];
! stoppoint = do_pg_stop_backup(NULL);
snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X",
stoppoint.xlogid, stoppoint.xrecoff);
PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr));
}
+ /*
+ * do_pg_start_backup is the workhorse of the user-visible pg_stop_backup()
+ * function.
+
+ * If labelfile is NULL, this stops an exclusive backup. Otherwise this stops
+ * the non-exclusive backup specified by 'labelfile'.
+ */
XLogRecPtr
! do_pg_stop_backup(char *labelfile)
{
+ bool exclusive = (labelfile == NULL);
XLogRecPtr startpoint;
XLogRecPtr stoppoint;
XLogRecData rdata;
***************
*** 8568,8582 **** do_pg_stop_backup(void)
FILE *lfp;
FILE *fp;
char ch;
- int ich;
int seconds_before_warning;
int waits = 0;
bool reported_waiting = false;
! if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! (errmsg("must be superuser or replication role to run a backup"))));
if (RecoveryInProgress())
ereport(ERROR,
--- 8642,8656 ----
FILE *lfp;
FILE *fp;
char ch;
int seconds_before_warning;
int waits = 0;
bool reported_waiting = false;
+ char *remaining;
! if (exclusive && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser to run a backup")));
if (RecoveryInProgress())
ereport(ERROR,
***************
*** 8591,8628 **** do_pg_stop_backup(void)
errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start.")));
/*
! * OK to clear forcePageWrites
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! XLogCtl->Insert.forcePageWrites = false;
LWLockRelease(WALInsertLock);
! /*
! * Open the existing label file
! */
! lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
! if (!lfp)
{
! if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
BACKUP_LABEL_FILE)));
! ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("a backup is not in progress")));
}
/*
* Read and parse the START WAL LOCATION line (this code is pretty crude,
* but we are not expecting any variability in the file format).
*/
! if (fscanf(lfp, "START WAL LOCATION: %X/%X (file %24s)%c",
&startpoint.xlogid, &startpoint.xrecoff, startxlogfilename,
&ch) != 4 || ch != '\n')
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
/*
* Write the backup-end xlog record
--- 8665,8749 ----
errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start.")));
/*
! * OK to update backup counters and forcePageWrites
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! if (exclusive)
! XLogCtl->Insert.exclusiveBackup = false;
! else
! {
! /*
! * The user-visible pg_start/stop_backup() functions that operate on
! * exclusive backups can be called at any time, but for non-exclusive
! * backups, it is expected that each do_pg_start_backup() call is
! * matched by exactly one do_pg_stop_backup() call.
! */
! Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
! XLogCtl->Insert.nonExclusiveBackups--;
! }
!
! if (!XLogCtl->Insert.exclusiveBackup && XLogCtl->Insert.nonExclusiveBackups == 0)
! XLogCtl->Insert.forcePageWrites = false;
LWLockRelease(WALInsertLock);
! if (exclusive)
{
! /*
! * Read the existing label file into memory.
! */
! struct stat statbuf;
! int r;
!
! if (stat(BACKUP_LABEL_FILE, &statbuf))
! {
! if (errno != ENOENT)
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not stat file \"%s\": %m",
! BACKUP_LABEL_FILE)));
! ereport(ERROR,
! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
! errmsg("a backup is not in progress")));
! }
!
! lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
! if (!lfp)
! {
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
BACKUP_LABEL_FILE)));
! }
! labelfile = palloc(statbuf.st_size + 1);
! r = fread(labelfile, statbuf.st_size, 1, lfp);
! labelfile[statbuf.st_size] = '\0';
!
! /*
! * Close and remove the backup label file
! */
! if (r != 1 || ferror(lfp) || FreeFile(lfp))
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not read file \"%s\": %m",
! BACKUP_LABEL_FILE)));
! if (unlink(BACKUP_LABEL_FILE) != 0)
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not remove file \"%s\": %m",
! BACKUP_LABEL_FILE)));
}
/*
* Read and parse the START WAL LOCATION line (this code is pretty crude,
* but we are not expecting any variability in the file format).
*/
! if (sscanf(labelfile, "START WAL LOCATION: %X/%X (file %24s)%c",
&startpoint.xlogid, &startpoint.xrecoff, startxlogfilename,
&ch) != 4 || ch != '\n')
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
+ remaining = strchr(labelfile, '\n') + 1; /* %n is not portable enough */
/*
* Write the backup-end xlog record
***************
*** 8665,8672 **** do_pg_stop_backup(void)
fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n",
stoppoint.xlogid, stoppoint.xrecoff, stopxlogfilename);
/* transfer remaining lines from label to history file */
! while ((ich = fgetc(lfp)) != EOF)
! fputc(ich, fp);
fprintf(fp, "STOP TIME: %s\n", strfbuf);
if (fflush(fp) || ferror(fp) || FreeFile(fp))
ereport(ERROR,
--- 8786,8792 ----
fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n",
stoppoint.xlogid, stoppoint.xrecoff, stopxlogfilename);
/* transfer remaining lines from label to history file */
! fprintf(fp, "%s", remaining);
fprintf(fp, "STOP TIME: %s\n", strfbuf);
if (fflush(fp) || ferror(fp) || FreeFile(fp))
ereport(ERROR,
***************
*** 8675,8694 **** do_pg_stop_backup(void)
histfilepath)));
/*
- * Close and remove the backup label file
- */
- if (ferror(lfp) || FreeFile(lfp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not read file \"%s\": %m",
- BACKUP_LABEL_FILE)));
- if (unlink(BACKUP_LABEL_FILE) != 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not remove file \"%s\": %m",
- BACKUP_LABEL_FILE)));
-
- /*
* Clean out any no-longer-needed history files. As a side effect, this
* will post a .ready file for the newly created history file, notifying
* the archiver that history file may be archived immediately.
--- 8795,8800 ----
***************
*** 8769,8796 **** do_pg_stop_backup(void)
/*
* do_pg_abort_backup: abort a running backup
*
! * This does just the most basic steps of pg_stop_backup(), by taking the
* system out of backup mode, thus making it a lot more safe to call from
* an error handler.
*/
void
do_pg_abort_backup(void)
{
- /*
- * OK to clear forcePageWrites
- */
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! XLogCtl->Insert.forcePageWrites = false;
! LWLockRelease(WALInsertLock);
! /*
! * Remove backup label file
! */
! if (unlink(BACKUP_LABEL_FILE) != 0)
! ereport(ERROR,
! (errcode_for_file_access(),
! errmsg("could not remove file \"%s\": %m",
! BACKUP_LABEL_FILE)));
}
/*
--- 8875,8899 ----
/*
* do_pg_abort_backup: abort a running backup
*
! * This does just the most basic steps of do_pg_stop_backup(), by taking the
* system out of backup mode, thus making it a lot more safe to call from
* an error handler.
+ *
+ * NB: This is only for aborting a non-exclusive backup that doesn't write
+ * backup_label. A backup started with pg_stop_backup() needs to be finished
+ * with pg_stop_backup().
*/
void
do_pg_abort_backup(void)
{
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
! Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
! XLogCtl->Insert.nonExclusiveBackups--;
! if (!XLogCtl->Insert.exclusiveBackup && XLogCtl->Insert.nonExclusiveBackups == 0)
! XLogCtl->Insert.forcePageWrites = false;
!
! LWLockRelease(WALInsertLock);
}
/*