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 */