@@ -459,6 +459,29 @@ typedef union WALInsertLockPadded
459459 char pad [PG_CACHE_LINE_SIZE ];
460460} WALInsertLockPadded ;
461461
462+ /*
463+ * State of an exclusive backup, necessary to control concurrent activities
464+ * across sessions when working on exclusive backups.
465+ *
466+ * EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually
467+ * running, to be more precise pg_start_backup() is not being executed for
468+ * an exclusive backup and there is no exclusive backup in progress.
469+ * EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an
470+ * exclusive backup.
471+ * EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished
472+ * running and an exclusive backup is in progress. pg_stop_backup() is
473+ * needed to finish it.
474+ * EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an
475+ * exclusive backup.
476+ */
477+ typedef enum ExclusiveBackupState
478+ {
479+ EXCLUSIVE_BACKUP_NONE = 0 ,
480+ EXCLUSIVE_BACKUP_STARTING ,
481+ EXCLUSIVE_BACKUP_IN_PROGRESS ,
482+ EXCLUSIVE_BACKUP_STOPPING
483+ } ExclusiveBackupState ;
484+
462485/*
463486 * Shared state data for WAL insertion.
464487 */
@@ -500,13 +523,15 @@ typedef struct XLogCtlInsert
500523 bool fullPageWrites ;
501524
502525 /*
503- * exclusiveBackup is true if a backup started with pg_start_backup() is
504- * in progress, and nonExclusiveBackups is a counter indicating the number
505- * of streaming base backups currently in progress. forcePageWrites is set
506- * to true when either of these is non-zero. lastBackupStart is the latest
507- * checkpoint redo location used as a starting point for an online backup.
526+ * exclusiveBackupState indicates the state of an exclusive backup
527+ * (see comments of ExclusiveBackupState for more details).
528+ * nonExclusiveBackups is a counter indicating the number of streaming
529+ * base backups currently in progress. forcePageWrites is set to true
530+ * when either of these is non-zero. lastBackupStart is the latest
531+ * checkpoint redo location used as a starting point for an online
532+ * backup.
508533 */
509- bool exclusiveBackup ;
534+ ExclusiveBackupState exclusiveBackupState ;
510535 int nonExclusiveBackups ;
511536 XLogRecPtr lastBackupStart ;
512537
@@ -845,6 +870,7 @@ static void xlog_outrec(StringInfo buf, XLogReaderState *record);
845870#endif
846871static void xlog_outdesc (StringInfo buf , XLogReaderState * record );
847872static void pg_start_backup_callback (int code , Datum arg );
873+ static void pg_stop_backup_callback (int code , Datum arg );
848874static bool read_backup_label (XLogRecPtr * checkPointLoc ,
849875 bool * backupEndRequired , bool * backupFromStandby );
850876static bool read_tablespace_map (List * * tablespaces );
@@ -9872,15 +9898,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
98729898 WALInsertLockAcquireExclusive ();
98739899 if (exclusive )
98749900 {
9875- if (XLogCtl -> Insert .exclusiveBackup )
9901+ /*
9902+ * At first, mark that we're now starting an exclusive backup,
9903+ * to ensure that there are no other sessions currently running
9904+ * pg_start_backup() or pg_stop_backup().
9905+ */
9906+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_NONE )
98769907 {
98779908 WALInsertLockRelease ();
98789909 ereport (ERROR ,
98799910 (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
98809911 errmsg ("a backup is already in progress" ),
98819912 errhint ("Run pg_stop_backup() and try again." )));
98829913 }
9883- XLogCtl -> Insert .exclusiveBackup = true ;
9914+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING ;
98849915 }
98859916 else
98869917 XLogCtl -> Insert .nonExclusiveBackups ++ ;
@@ -10135,7 +10166,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1013510166 {
1013610167 /*
1013710168 * Check for existing backup label --- implies a backup is already
10138- * running. (XXX given that we checked exclusiveBackup above,
10169+ * running. (XXX given that we checked exclusiveBackupState above,
1013910170 * maybe it would be OK to just unlink any such label file?)
1014010171 */
1014110172 if (stat (BACKUP_LABEL_FILE , & stat_buf ) != 0 )
@@ -10216,6 +10247,16 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1021610247 }
1021710248 PG_END_ENSURE_ERROR_CLEANUP (pg_start_backup_callback , (Datum ) BoolGetDatum (exclusive ));
1021810249
10250+ /*
10251+ * Mark that start phase has correctly finished for an exclusive backup.
10252+ */
10253+ if (exclusive )
10254+ {
10255+ WALInsertLockAcquireExclusive ();
10256+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
10257+ WALInsertLockRelease ();
10258+ }
10259+
1021910260 /*
1022010261 * We're done. As a convenience, return the starting WAL location.
1022110262 */
@@ -10234,23 +10275,41 @@ pg_start_backup_callback(int code, Datum arg)
1023410275 WALInsertLockAcquireExclusive ();
1023510276 if (exclusive )
1023610277 {
10237- Assert (XLogCtl -> Insert .exclusiveBackup );
10238- XLogCtl -> Insert .exclusiveBackup = false ;
10278+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING );
10279+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
1023910280 }
1024010281 else
1024110282 {
1024210283 Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
1024310284 XLogCtl -> Insert .nonExclusiveBackups -- ;
1024410285 }
1024510286
10246- if (! XLogCtl -> Insert .exclusiveBackup &&
10287+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
1024710288 XLogCtl -> Insert .nonExclusiveBackups == 0 )
1024810289 {
1024910290 XLogCtl -> Insert .forcePageWrites = false;
1025010291 }
1025110292 WALInsertLockRelease ();
1025210293}
1025310294
10295+ /*
10296+ * Error cleanup callback for pg_stop_backup
10297+ */
10298+ static void
10299+ pg_stop_backup_callback (int code , Datum arg )
10300+ {
10301+ bool exclusive = DatumGetBool (arg );
10302+
10303+ /* Update backup status on failure */
10304+ WALInsertLockAcquireExclusive ();
10305+ if (exclusive )
10306+ {
10307+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING );
10308+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
10309+ }
10310+ WALInsertLockRelease ();
10311+ }
10312+
1025410313/*
1025510314 * do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup()
1025610315 * function.
@@ -10313,20 +10372,91 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
1031310372 errmsg ("WAL level not sufficient for making an online backup" ),
1031410373 errhint ("wal_level must be set to \"replica\" or \"logical\" at server start." )));
1031510374
10316- /*
10317- * OK to update backup counters and forcePageWrites
10318- */
10319- WALInsertLockAcquireExclusive ();
1032010375 if (exclusive )
1032110376 {
10322- if (!XLogCtl -> Insert .exclusiveBackup )
10377+ /*
10378+ * At first, mark that we're now stopping an exclusive backup,
10379+ * to ensure that there are no other sessions currently running
10380+ * pg_start_backup() or pg_stop_backup().
10381+ */
10382+ WALInsertLockAcquireExclusive ();
10383+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS )
1032310384 {
1032410385 WALInsertLockRelease ();
1032510386 ereport (ERROR ,
1032610387 (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
1032710388 errmsg ("exclusive backup not in progress" )));
1032810389 }
10329- XLogCtl -> Insert .exclusiveBackup = false;
10390+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING ;
10391+ WALInsertLockRelease ();
10392+
10393+ /*
10394+ * Remove backup_label. In case of failure, the state for an exclusive
10395+ * backup is switched back to in-progress.
10396+ */
10397+ PG_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10398+ {
10399+ /*
10400+ * Read the existing label file into memory.
10401+ */
10402+ struct stat statbuf ;
10403+ int r ;
10404+
10405+ if (stat (BACKUP_LABEL_FILE , & statbuf ))
10406+ {
10407+ /* should not happen per the upper checks */
10408+ if (errno != ENOENT )
10409+ ereport (ERROR ,
10410+ (errcode_for_file_access (),
10411+ errmsg ("could not stat file \"%s\": %m" ,
10412+ BACKUP_LABEL_FILE )));
10413+ ereport (ERROR ,
10414+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10415+ errmsg ("a backup is not in progress" )));
10416+ }
10417+
10418+ lfp = AllocateFile (BACKUP_LABEL_FILE , "r" );
10419+ if (!lfp )
10420+ {
10421+ ereport (ERROR ,
10422+ (errcode_for_file_access (),
10423+ errmsg ("could not read file \"%s\": %m" ,
10424+ BACKUP_LABEL_FILE )));
10425+ }
10426+ labelfile = palloc (statbuf .st_size + 1 );
10427+ r = fread (labelfile , statbuf .st_size , 1 , lfp );
10428+ labelfile [statbuf .st_size ] = '\0' ;
10429+
10430+ /*
10431+ * Close and remove the backup label file
10432+ */
10433+ if (r != 1 || ferror (lfp ) || FreeFile (lfp ))
10434+ ereport (ERROR ,
10435+ (errcode_for_file_access (),
10436+ errmsg ("could not read file \"%s\": %m" ,
10437+ BACKUP_LABEL_FILE )));
10438+ if (unlink (BACKUP_LABEL_FILE ) != 0 )
10439+ ereport (ERROR ,
10440+ (errcode_for_file_access (),
10441+ errmsg ("could not remove file \"%s\": %m" ,
10442+ BACKUP_LABEL_FILE )));
10443+
10444+ /*
10445+ * Remove tablespace_map file if present, it is created only if there
10446+ * are tablespaces.
10447+ */
10448+ unlink (TABLESPACE_MAP );
10449+ }
10450+ PG_END_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10451+ }
10452+
10453+ /*
10454+ * OK to update backup counters and forcePageWrites
10455+ */
10456+ WALInsertLockAcquireExclusive ();
10457+ if (exclusive )
10458+ {
10459+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
1033010460 }
1033110461 else
1033210462 {
@@ -10340,66 +10470,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
1034010470 XLogCtl -> Insert .nonExclusiveBackups -- ;
1034110471 }
1034210472
10343- if (! XLogCtl -> Insert .exclusiveBackup &&
10473+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
1034410474 XLogCtl -> Insert .nonExclusiveBackups == 0 )
1034510475 {
1034610476 XLogCtl -> Insert .forcePageWrites = false;
1034710477 }
1034810478 WALInsertLockRelease ();
1034910479
10350- if (exclusive )
10351- {
10352- /*
10353- * Read the existing label file into memory.
10354- */
10355- struct stat statbuf ;
10356- int r ;
10357-
10358- if (stat (BACKUP_LABEL_FILE , & statbuf ))
10359- {
10360- if (errno != ENOENT )
10361- ereport (ERROR ,
10362- (errcode_for_file_access (),
10363- errmsg ("could not stat file \"%s\": %m" ,
10364- BACKUP_LABEL_FILE )));
10365- ereport (ERROR ,
10366- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10367- errmsg ("a backup is not in progress" )));
10368- }
10369-
10370- lfp = AllocateFile (BACKUP_LABEL_FILE , "r" );
10371- if (!lfp )
10372- {
10373- ereport (ERROR ,
10374- (errcode_for_file_access (),
10375- errmsg ("could not read file \"%s\": %m" ,
10376- BACKUP_LABEL_FILE )));
10377- }
10378- labelfile = palloc (statbuf .st_size + 1 );
10379- r = fread (labelfile , statbuf .st_size , 1 , lfp );
10380- labelfile [statbuf .st_size ] = '\0' ;
10381-
10382- /*
10383- * Close and remove the backup label file
10384- */
10385- if (r != 1 || ferror (lfp ) || FreeFile (lfp ))
10386- ereport (ERROR ,
10387- (errcode_for_file_access (),
10388- errmsg ("could not read file \"%s\": %m" ,
10389- BACKUP_LABEL_FILE )));
10390- if (unlink (BACKUP_LABEL_FILE ) != 0 )
10391- ereport (ERROR ,
10392- (errcode_for_file_access (),
10393- errmsg ("could not remove file \"%s\": %m" ,
10394- BACKUP_LABEL_FILE )));
10395-
10396- /*
10397- * Remove tablespace_map file if present, it is created only if there
10398- * are tablespaces.
10399- */
10400- unlink (TABLESPACE_MAP );
10401- }
10402-
1040310480 /*
1040410481 * Read and parse the START WAL LOCATION line (this code is pretty crude,
1040510482 * but we are not expecting any variability in the file format).
@@ -10636,7 +10713,7 @@ do_pg_abort_backup(void)
1063610713 Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
1063710714 XLogCtl -> Insert .nonExclusiveBackups -- ;
1063810715
10639- if (! XLogCtl -> Insert .exclusiveBackup &&
10716+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
1064010717 XLogCtl -> Insert .nonExclusiveBackups == 0 )
1064110718 {
1064210719 XLogCtl -> Insert .forcePageWrites = false;
0 commit comments