@@ -462,6 +462,29 @@ typedef union WALInsertLockPadded
462462char pad [PG_CACHE_LINE_SIZE ];
463463}WALInsertLockPadded ;
464464
465+ /*
466+ * State of an exclusive backup, necessary to control concurrent activities
467+ * across sessions when working on exclusive backups.
468+ *
469+ * EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually
470+ * running, to be more precise pg_start_backup() is not being executed for
471+ * an exclusive backup and there is no exclusive backup in progress.
472+ * EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an
473+ * exclusive backup.
474+ * EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished
475+ * running and an exclusive backup is in progress. pg_stop_backup() is
476+ * needed to finish it.
477+ * EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an
478+ * exclusive backup.
479+ */
480+ typedef enum ExclusiveBackupState
481+ {
482+ EXCLUSIVE_BACKUP_NONE = 0 ,
483+ EXCLUSIVE_BACKUP_STARTING ,
484+ EXCLUSIVE_BACKUP_IN_PROGRESS ,
485+ EXCLUSIVE_BACKUP_STOPPING
486+ }ExclusiveBackupState ;
487+
465488/*
466489 * Shared state data for WAL insertion.
467490 */
@@ -503,13 +526,15 @@ typedef struct XLogCtlInsert
503526bool fullPageWrites ;
504527
505528/*
506- * exclusiveBackup is true if a backup started with pg_start_backup() is
507- * in progress, and nonExclusiveBackups is a counter indicating the number
508- * of streaming base backups currently in progress. forcePageWrites is set
509- * to true when either of these is non-zero. lastBackupStart is the latest
510- * checkpoint redo location used as a starting point for an online backup.
529+ * exclusiveBackupState indicates the state of an exclusive backup
530+ * (see comments of ExclusiveBackupState for more details).
531+ * nonExclusiveBackups is a counter indicating the number of streaming
532+ * base backups currently in progress. forcePageWrites is set to true
533+ * when either of these is non-zero. lastBackupStart is the latest
534+ * checkpoint redo location used as a starting point for an online
535+ * backup.
511536 */
512- bool exclusiveBackup ;
537+ ExclusiveBackupState exclusiveBackupState ;
513538int nonExclusiveBackups ;
514539XLogRecPtr lastBackupStart ;
515540
@@ -847,6 +872,7 @@ static void xlog_outrec(StringInfo buf, XLogReaderState *record);
847872#endif
848873static void xlog_outdesc (StringInfo buf ,XLogReaderState * record );
849874static void pg_start_backup_callback (int code ,Datum arg );
875+ static void pg_stop_backup_callback (int code ,Datum arg );
850876static bool read_backup_label (XLogRecPtr * checkPointLoc ,
851877bool * backupEndRequired ,bool * backupFromStandby );
852878static bool read_tablespace_map (List * * tablespaces );
@@ -9845,15 +9871,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
98459871WALInsertLockAcquireExclusive ();
98469872if (exclusive )
98479873{
9848- if (XLogCtl -> Insert .exclusiveBackup )
9874+ /*
9875+ * At first, mark that we're now starting an exclusive backup,
9876+ * to ensure that there are no other sessions currently running
9877+ * pg_start_backup() or pg_stop_backup().
9878+ */
9879+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_NONE )
98499880{
98509881WALInsertLockRelease ();
98519882ereport (ERROR ,
98529883(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
98539884errmsg ("a backup is already in progress" ),
98549885errhint ("Run pg_stop_backup() and try again." )));
98559886}
9856- XLogCtl -> Insert .exclusiveBackup = true ;
9887+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING ;
98579888}
98589889else
98599890XLogCtl -> Insert .nonExclusiveBackups ++ ;
@@ -10110,7 +10141,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1011010141{
1011110142/*
1011210143 * Check for existing backup label --- implies a backup is already
10113- * running. (XXX given that we checkedexclusiveBackup above,
10144+ * running. (XXX given that we checkedexclusiveBackupState above,
1011410145 * maybe it would be OK to just unlink any such label file?)
1011510146 */
1011610147if (stat (BACKUP_LABEL_FILE ,& stat_buf )!= 0 )
@@ -10191,6 +10222,16 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
1019110222}
1019210223PG_END_ENSURE_ERROR_CLEANUP (pg_start_backup_callback , (Datum )BoolGetDatum (exclusive ));
1019310224
10225+ /*
10226+ * Mark that start phase has correctly finished for an exclusive backup.
10227+ */
10228+ if (exclusive )
10229+ {
10230+ WALInsertLockAcquireExclusive ();
10231+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
10232+ WALInsertLockRelease ();
10233+ }
10234+
1019410235/*
1019510236 * We're done. As a convenience, return the starting WAL location.
1019610237 */
@@ -10209,16 +10250,16 @@ pg_start_backup_callback(int code, Datum arg)
1020910250WALInsertLockAcquireExclusive ();
1021010251if (exclusive )
1021110252{
10212- Assert (XLogCtl -> Insert .exclusiveBackup );
10213- XLogCtl -> Insert .exclusiveBackup = false ;
10253+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING );
10254+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
1021410255}
1021510256else
1021610257{
1021710258Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
1021810259XLogCtl -> Insert .nonExclusiveBackups -- ;
1021910260}
1022010261
10221- if (! XLogCtl -> Insert .exclusiveBackup &&
10262+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
1022210263XLogCtl -> Insert .nonExclusiveBackups == 0 )
1022310264{
1022410265XLogCtl -> Insert .forcePageWrites = false;
@@ -10228,6 +10269,24 @@ pg_start_backup_callback(int code, Datum arg)
1022810269cfs_control_gc (SavedGCState );/* Restore CFS GC activity */
1022910270}
1023010271
10272+ /*
10273+ * Error cleanup callback for pg_stop_backup
10274+ */
10275+ static void
10276+ pg_stop_backup_callback (int code ,Datum arg )
10277+ {
10278+ bool exclusive = DatumGetBool (arg );
10279+
10280+ /* Update backup status on failure */
10281+ WALInsertLockAcquireExclusive ();
10282+ if (exclusive )
10283+ {
10284+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING );
10285+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
10286+ }
10287+ WALInsertLockRelease ();
10288+ }
10289+
1023110290/*
1023210291 * do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup()
1023310292 * function.
@@ -10290,20 +10349,91 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
1029010349errmsg ("WAL level not sufficient for making an online backup" ),
1029110350errhint ("wal_level must be set to \"replica\" or \"logical\" at server start." )));
1029210351
10293- /*
10294- * OK to update backup counters and forcePageWrites
10295- */
10296- WALInsertLockAcquireExclusive ();
1029710352if (exclusive )
1029810353{
10299- if (!XLogCtl -> Insert .exclusiveBackup )
10354+ /*
10355+ * At first, mark that we're now stopping an exclusive backup,
10356+ * to ensure that there are no other sessions currently running
10357+ * pg_start_backup() or pg_stop_backup().
10358+ */
10359+ WALInsertLockAcquireExclusive ();
10360+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS )
1030010361{
1030110362WALInsertLockRelease ();
1030210363ereport (ERROR ,
1030310364(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
1030410365errmsg ("exclusive backup not in progress" )));
1030510366}
10306- XLogCtl -> Insert .exclusiveBackup = false;
10367+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING ;
10368+ WALInsertLockRelease ();
10369+
10370+ /*
10371+ * Remove backup_label. In case of failure, the state for an exclusive
10372+ * backup is switched back to in-progress.
10373+ */
10374+ PG_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum )BoolGetDatum (exclusive ));
10375+ {
10376+ /*
10377+ * Read the existing label file into memory.
10378+ */
10379+ struct stat statbuf ;
10380+ int r ;
10381+
10382+ if (stat (BACKUP_LABEL_FILE ,& statbuf ))
10383+ {
10384+ /* should not happen per the upper checks */
10385+ if (errno != ENOENT )
10386+ ereport (ERROR ,
10387+ (errcode_for_file_access (),
10388+ errmsg ("could not stat file \"%s\": %m" ,
10389+ BACKUP_LABEL_FILE )));
10390+ ereport (ERROR ,
10391+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10392+ errmsg ("a backup is not in progress" )));
10393+ }
10394+
10395+ lfp = AllocateFile (BACKUP_LABEL_FILE ,"r" );
10396+ if (!lfp )
10397+ {
10398+ ereport (ERROR ,
10399+ (errcode_for_file_access (),
10400+ errmsg ("could not read file \"%s\": %m" ,
10401+ BACKUP_LABEL_FILE )));
10402+ }
10403+ labelfile = palloc (statbuf .st_size + 1 );
10404+ r = fread (labelfile ,statbuf .st_size ,1 ,lfp );
10405+ labelfile [statbuf .st_size ]= '\0' ;
10406+
10407+ /*
10408+ * Close and remove the backup label file
10409+ */
10410+ if (r != 1 || ferror (lfp )|| FreeFile (lfp ))
10411+ ereport (ERROR ,
10412+ (errcode_for_file_access (),
10413+ errmsg ("could not read file \"%s\": %m" ,
10414+ BACKUP_LABEL_FILE )));
10415+ if (unlink (BACKUP_LABEL_FILE )!= 0 )
10416+ ereport (ERROR ,
10417+ (errcode_for_file_access (),
10418+ errmsg ("could not remove file \"%s\": %m" ,
10419+ BACKUP_LABEL_FILE )));
10420+
10421+ /*
10422+ * Remove tablespace_map file if present, it is created only if there
10423+ * are tablespaces.
10424+ */
10425+ unlink (TABLESPACE_MAP );
10426+ }
10427+ PG_END_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum )BoolGetDatum (exclusive ));
10428+ }
10429+
10430+ /*
10431+ * OK to update backup counters and forcePageWrites
10432+ */
10433+ WALInsertLockAcquireExclusive ();
10434+ if (exclusive )
10435+ {
10436+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
1030710437}
1030810438else
1030910439{
@@ -10317,66 +10447,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
1031710447XLogCtl -> Insert .nonExclusiveBackups -- ;
1031810448}
1031910449
10320- if (! XLogCtl -> Insert .exclusiveBackup &&
10450+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
1032110451XLogCtl -> Insert .nonExclusiveBackups == 0 )
1032210452{
1032310453XLogCtl -> Insert .forcePageWrites = false;
1032410454}
1032510455WALInsertLockRelease ();
1032610456
10327- if (exclusive )
10328- {
10329- /*
10330- * Read the existing label file into memory.
10331- */
10332- struct stat statbuf ;
10333- int r ;
10334-
10335- if (stat (BACKUP_LABEL_FILE ,& statbuf ))
10336- {
10337- if (errno != ENOENT )
10338- ereport (ERROR ,
10339- (errcode_for_file_access (),
10340- errmsg ("could not stat file \"%s\": %m" ,
10341- BACKUP_LABEL_FILE )));
10342- ereport (ERROR ,
10343- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10344- errmsg ("a backup is not in progress" )));
10345- }
10346-
10347- lfp = AllocateFile (BACKUP_LABEL_FILE ,"r" );
10348- if (!lfp )
10349- {
10350- ereport (ERROR ,
10351- (errcode_for_file_access (),
10352- errmsg ("could not read file \"%s\": %m" ,
10353- BACKUP_LABEL_FILE )));
10354- }
10355- labelfile = palloc (statbuf .st_size + 1 );
10356- r = fread (labelfile ,statbuf .st_size ,1 ,lfp );
10357- labelfile [statbuf .st_size ]= '\0' ;
10358-
10359- /*
10360- * Close and remove the backup label file
10361- */
10362- if (r != 1 || ferror (lfp )|| FreeFile (lfp ))
10363- ereport (ERROR ,
10364- (errcode_for_file_access (),
10365- errmsg ("could not read file \"%s\": %m" ,
10366- BACKUP_LABEL_FILE )));
10367- if (unlink (BACKUP_LABEL_FILE )!= 0 )
10368- ereport (ERROR ,
10369- (errcode_for_file_access (),
10370- errmsg ("could not remove file \"%s\": %m" ,
10371- BACKUP_LABEL_FILE )));
10372-
10373- /*
10374- * Remove tablespace_map file if present, it is created only if there
10375- * are tablespaces.
10376- */
10377- unlink (TABLESPACE_MAP );
10378- }
10379-
1038010457/*
1038110458 * Read and parse the START WAL LOCATION line (this code is pretty crude,
1038210459 * but we are not expecting any variability in the file format).
@@ -10615,7 +10692,7 @@ do_pg_abort_backup(void)
1061510692Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
1061610693XLogCtl -> Insert .nonExclusiveBackups -- ;
1061710694
10618- if (! XLogCtl -> Insert .exclusiveBackup &&
10695+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
1061910696XLogCtl -> Insert .nonExclusiveBackups == 0 )
1062010697{
1062110698XLogCtl -> Insert .forcePageWrites = false;