multiple_inprogress_backups2.patch
text/x-diff
Filename: multiple_inprogress_backups2.patch
Type: text/x-diff
Part: 1
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: unified
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 105 | 46 |
| src/backend/replication/basebackup.c | 53 | 29 |
| src/include/access/xlog.h | 2 | 2 |
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5b6a230..3d0f813 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -338,7 +338,8 @@ typedef struct XLogCtlInsert
XLogPageHeader currpage; /* points to header of block in cache */
char *currpos; /* current insertion point in cache */
XLogRecPtr RedoRecPtr; /* current redo point for insertions */
- bool forcePageWrites; /* forcing full-page writes for PITR? */
+ int forcePageWrites; /* forcing full-page writes for PITR? */
+ bool exclusiveBackup; /* a backup was started with pg_start_backup() */
} XLogCtlInsert;
/*
@@ -8313,7 +8314,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
backupidstr = text_to_cstring(backupid);
- startpoint = do_pg_start_backup(backupidstr, fast);
+ startpoint = do_pg_start_backup(backupidstr, fast, true, NULL);
snprintf(startxlogstr, sizeof(startxlogstr), "%X/%X",
startpoint.xlogid, startpoint.xrecoff);
@@ -8321,7 +8322,7 @@ pg_start_backup(PG_FUNCTION_ARGS)
}
XLogRecPtr
-do_pg_start_backup(const char *backupidstr, bool fast)
+do_pg_start_backup(const char *backupidstr, bool fast, bool exclusive, char **labelfilename)
{
XLogRecPtr checkpointloc;
XLogRecPtr startpoint;
@@ -8368,15 +8369,19 @@ do_pg_start_backup(const char *backupidstr, bool fast)
* ensure adequate interlocking against XLogInsert().
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
- if (XLogCtl->Insert.forcePageWrites)
+ if (exclusive)
{
- 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.")));
+ 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;
}
- XLogCtl->Insert.forcePageWrites = true;
+ XLogCtl->Insert.forcePageWrites++;
LWLockRelease(WALInsertLock);
/*
@@ -8393,7 +8398,7 @@ 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);
+ PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
{
/*
* Force a CHECKPOINT. Aside from being necessary to prevent torn
@@ -8427,29 +8432,55 @@ do_pg_start_backup(const char *backupidstr, bool fast)
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?)
+ * Okay, write the file
*/
- if (stat(BACKUP_LABEL_FILE, &stat_buf) != 0)
+ if (exclusive)
{
- if (errno != ENOENT)
+ /*
+ * 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_for_file_access(),
- errmsg("could not stat file \"%s\": %m",
- BACKUP_LABEL_FILE)));
+ (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");
}
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)));
+ {
+ char historyfilename[MAXFNAMELEN];
+ /* Calculate name for temp file */
+ XLByteToSeg(startpoint, _logId, _logSeg);
+ BackupHistoryFileName(historyfilename, ThisTimeLineID,
+ _logId, _logSeg, startpoint.xrecoff % XLogSegSize);
+
+ *labelfilename = palloc(MAXPGPATH);
+ snprintf(*labelfilename, MAXPGPATH, "%s/%s.%s",
+ PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
+ historyfilename);
+
+ /* Open file */
+ fp = AllocateFile(*labelfilename, PG_BINARY_W);
+ if (!fp)
+ {
+ /* As in OpenTemporaryFile, try to make the temp-file directory */
+ mkdir(PG_TEMP_FILES_DIR, S_IRWXU);
- /*
- * Okay, write the file
- */
- fp = AllocateFile(BACKUP_LABEL_FILE, "w");
+ fp = AllocateFile(*labelfilename, PG_BINARY_W);
+ }
+ }
if (!fp)
ereport(ERROR,
(errcode_for_file_access(),
@@ -8467,7 +8498,7 @@ do_pg_start_backup(const char *backupidstr, bool fast)
errmsg("could not write file \"%s\": %m",
BACKUP_LABEL_FILE)));
}
- PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) 0);
+ PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
/*
* We're done. As a convenience, return the starting WAL location.
@@ -8479,8 +8510,15 @@ do_pg_start_backup(const char *backupidstr, bool fast)
static void
pg_start_backup_callback(int code, Datum arg)
{
- /* Turn off forcePageWrites on failure */
+ bool exclusive = DatumGetBool(arg);
+
+ /* Decrement forcePageWrites on failure */
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
+ if (exclusive)
+ {
+ Assert(XLogCtl->Insert.exclusiveBackup);
+ XLogCtl->Insert.exclusiveBackup = false;
+ }
XLogCtl->Insert.forcePageWrites = false;
LWLockRelease(WALInsertLock);
}
@@ -8504,16 +8542,20 @@ pg_stop_backup(PG_FUNCTION_ARGS)
XLogRecPtr stoppoint;
char stopxlogstr[MAXFNAMELEN];
- stoppoint = do_pg_stop_backup();
+ stoppoint = do_pg_stop_backup(NULL);
snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X",
stoppoint.xlogid, stoppoint.xrecoff);
PG_RETURN_TEXT_P(cstring_to_text(stopxlogstr));
}
+/*
+ * If labelfile is NULL, this stops an exclusive backup.
+ */
XLogRecPtr
-do_pg_stop_backup(void)
+do_pg_stop_backup(const char *labelfile)
{
+ bool exclusive;
XLogRecPtr startpoint;
XLogRecPtr stoppoint;
XLogRecData rdata;
@@ -8534,6 +8576,14 @@ do_pg_stop_backup(void)
int waits = 0;
bool reported_waiting = false;
+ if (labelfile == NULL)
+ {
+ exclusive = true;
+ labelfile = BACKUP_LABEL_FILE;
+ }
+ else
+ exclusive = false;
+
if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -8555,20 +8605,28 @@ do_pg_stop_backup(void)
* OK to clear forcePageWrites
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
- XLogCtl->Insert.forcePageWrites = false;
+ if (exclusive)
+ {
+ if (XLogCtl->Insert.exclusiveBackup)
+ {
+ XLogCtl->Insert.forcePageWrites--;
+ XLogCtl->Insert.exclusiveBackup = false;
+ }
+ }
+ XLogCtl->Insert.forcePageWrites--;
LWLockRelease(WALInsertLock);
/*
* Open the existing label file
*/
- lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
+ lfp = AllocateFile(labelfile, "r");
if (!lfp)
{
if (errno != ENOENT)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
- BACKUP_LABEL_FILE)));
+ labelfile)));
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("a backup is not in progress")));
@@ -8643,7 +8701,7 @@ do_pg_stop_backup(void)
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m",
BACKUP_LABEL_FILE)));
- if (unlink(BACKUP_LABEL_FILE) != 0)
+ if (unlink(labelfile) != 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not remove file \"%s\": %m",
@@ -8730,9 +8788,13 @@ 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
+ * 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)
@@ -8741,17 +8803,14 @@ do_pg_abort_backup(void)
* OK to clear forcePageWrites
*/
LWLockAcquire(WALInsertLock, LW_EXCLUSIVE);
- XLogCtl->Insert.forcePageWrites = false;
- LWLockRelease(WALInsertLock);
-
/*
- * Remove backup label file
+ * The caller should take care to not call us only if a backup, but
+ * be extra paranoid to avoid wrapping forcePageWrites around if the
+ * caller screws up.
*/
- if (unlink(BACKUP_LABEL_FILE) != 0)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not remove file \"%s\": %m",
- BACKUP_LABEL_FILE)));
+ if (XLogCtl->Insert.forcePageWrites > 0)
+ XLogCtl->Insert.forcePageWrites--;
+ LWLockRelease(WALInsertLock);
}
/*
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index de5fa22..a3d4e5c 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -31,13 +31,17 @@
#include "utils/memutils.h"
#include "utils/ps_status.h"
+/* XXX: copied from xlog.c */
+#define BACKUP_LABEL_FILE "backup_label"
+
static int64 sendDir(char *path, int basepathlen, bool sizeonly);
-static void sendFile(char *path, int basepathlen, struct stat * statbuf);
+static void sendFile(char *readfilename, char *tarfilename,
+ struct stat * statbuf);
static void _tarWriteHeader(char *filename, char *linktarget,
struct stat * statbuf);
static void send_int8_string(StringInfoData *buf, int64 intval);
static void SendBackupHeader(List *tablespaces);
-static void SendBackupDirectory(char *location, char *spcoid);
+static void SendBackupDirectories(List *tablespaces, char *labelfilepath);
static void base_backup_cleanup(int code, Datum arg);
typedef struct
@@ -75,6 +79,7 @@ SendBaseBackup(const char *options)
tablespaceinfo *ti;
MemoryContext backup_context;
MemoryContext old_context;
+ char *labelfilepath;
backup_context = AllocSetContextCreate(CurrentMemoryContext,
"Streaming base backup context",
@@ -145,26 +150,19 @@ SendBaseBackup(const char *options)
}
FreeDir(dir);
- do_pg_start_backup(backup_label, true);
+ do_pg_start_backup(backup_label, true, false, &labelfilepath);
PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
{
- ListCell *lc;
-
/* Send tablespace header */
SendBackupHeader(tablespaces);
- /* Send off our tablespaces one by one */
- foreach(lc, tablespaces)
- {
- ti = (tablespaceinfo *) lfirst(lc);
-
- SendBackupDirectory(ti->path, ti->oid);
- }
+ /* Send the tars */
+ SendBackupDirectories(tablespaces, labelfilepath);
}
PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
- do_pg_stop_backup();
+ do_pg_stop_backup(labelfilepath);
MemoryContextSwitchTo(old_context);
MemoryContextDelete(backup_context);
@@ -250,20 +248,44 @@ SendBackupHeader(List *tablespaces)
}
static void
-SendBackupDirectory(char *location, char *spcoid)
+SendBackupDirectories(List *tablespaces, char *labelfilepath)
{
StringInfoData buf;
+ ListCell *lc;
- /* Send CopyOutResponse message */
- pq_beginmessage(&buf, 'H');
- pq_sendbyte(&buf, 0); /* overall format */
- pq_sendint(&buf, 0, 2); /* natts */
- pq_endmessage(&buf);
+ /* Send off our tablespaces one by one */
+ foreach(lc, tablespaces)
+ {
+ tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
+ char *location = ti->path;
+
+ /* Send CopyOutResponse message */
+ pq_beginmessage(&buf, 'H');
+ pq_sendbyte(&buf, 0); /* overall format */
+ pq_sendint(&buf, 0, 2); /* natts */
+ pq_endmessage(&buf);
+
+ /*
+ * tar up the data directory if NULL, otherwise the tablespace
+ * In the main tar, also include the backup_label.
+ */
+ if (location == NULL)
+ {
+ struct stat statbuf;
+
+ if (lstat(labelfilepath, &statbuf) != 0)
+ ereport(ERROR,
+ (errcode(errcode_for_file_access()),
+ errmsg("could not read temporary backup label file \"%s\": %m",
+ labelfilepath)));
- /* tar up the data directory if NULL, otherwise the tablespace */
- sendDir(location == NULL ? "." : location,
- location == NULL ? 1 : strlen(location),
- false);
+ sendFile(labelfilepath, BACKUP_LABEL_FILE, &statbuf);
+
+ location = ".";
+ }
+
+ sendDir(location, strlen(location), false);
+ }
/* Send CopyDone message */
pq_putemptymessage('c');
@@ -360,7 +382,7 @@ sendDir(char *path, int basepathlen, bool sizeonly)
/* Add size, rounded up to 512byte block */
size += ((statbuf.st_size + 511) & ~511);
if (!sizeonly)
- sendFile(pathbuf, basepathlen, &statbuf);
+ sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf);
size += 512; /* Size of the header of the file */
}
else
@@ -418,7 +440,7 @@ _tarChecksum(char *header)
/* Given the member, write the TAR header & send the file */
static void
-sendFile(char *filename, int basepathlen, struct stat * statbuf)
+sendFile(char *readfilename, char *tarfilename, struct stat * statbuf)
{
FILE *fp;
char buf[32768];
@@ -426,11 +448,13 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
pgoff_t len = 0;
size_t pad;
- fp = AllocateFile(filename, "rb");
+ pg_usleep(100000);
+
+ fp = AllocateFile(readfilename, "rb");
if (fp == NULL)
ereport(ERROR,
(errcode(errcode_for_file_access()),
- errmsg("could not open file \"%s\": %m", filename)));
+ errmsg("could not open file \"%s\": %m", readfilename)));
/*
* Some compilers will throw a warning knowing this test can never be true
@@ -439,9 +463,9 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
if (statbuf->st_size > MAX_TAR_MEMBER_FILELEN)
ereport(ERROR,
(errmsg("archive member \"%s\" too large for tar format",
- filename)));
+ tarfilename)));
- _tarWriteHeader(filename + basepathlen + 1, NULL, statbuf);
+ _tarWriteHeader(tarfilename, NULL, statbuf);
while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0)
{
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 74d3427..fb8d715 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -312,8 +312,8 @@ extern void HandleStartupProcInterrupts(void);
extern void StartupProcessMain(void);
extern void WakeupRecovery(void);
-extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast);
-extern XLogRecPtr do_pg_stop_backup(void);
+extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast, bool exclusive, char **labelfilepath);
+extern XLogRecPtr do_pg_stop_backup(const char *labelfile);
extern void do_pg_abort_backup(void);
#endif /* XLOG_H */