@@ -348,6 +348,29 @@ typedef struct XLogwrtResult
348
348
XLogRecPtr Flush ; /* last byte + 1 flushed */
349
349
} XLogwrtResult ;
350
350
351
+ /*
352
+ * State of an exclusive backup, necessary to control concurrent activities
353
+ * across sessions when working on exclusive backups.
354
+ *
355
+ * EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually
356
+ * running, to be more precise pg_start_backup() is not being executed for
357
+ * an exclusive backup and there is no exclusive backup in progress.
358
+ * EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an
359
+ * exclusive backup.
360
+ * EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished
361
+ * running and an exclusive backup is in progress. pg_stop_backup() is
362
+ * needed to finish it.
363
+ * EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an
364
+ * exclusive backup.
365
+ */
366
+ typedef enum ExclusiveBackupState
367
+ {
368
+ EXCLUSIVE_BACKUP_NONE = 0 ,
369
+ EXCLUSIVE_BACKUP_STARTING ,
370
+ EXCLUSIVE_BACKUP_IN_PROGRESS ,
371
+ EXCLUSIVE_BACKUP_STOPPING
372
+ } ExclusiveBackupState ;
373
+
351
374
/*
352
375
* Shared state data for XLogInsert.
353
376
*/
@@ -370,13 +393,15 @@ typedef struct XLogCtlInsert
370
393
bool fullPageWrites ;
371
394
372
395
/*
373
- * exclusiveBackup is true if a backup started with pg_start_backup() is
374
- * in progress, and nonExclusiveBackups is a counter indicating the number
375
- * of streaming base backups currently in progress. forcePageWrites is set
376
- * to true when either of these is non-zero. lastBackupStart is the latest
377
- * checkpoint redo location used as a starting point for an online backup.
396
+ * exclusiveBackupState indicates the state of an exclusive backup
397
+ * (see comments of ExclusiveBackupState for more details).
398
+ * nonExclusiveBackups is a counter indicating the number of streaming
399
+ * base backups currently in progress. forcePageWrites is set to true
400
+ * when either of these is non-zero. lastBackupStart is the latest
401
+ * checkpoint redo location used as a starting point for an online
402
+ * backup.
378
403
*/
379
- bool exclusiveBackup ;
404
+ ExclusiveBackupState exclusiveBackupState ;
380
405
int nonExclusiveBackups ;
381
406
XLogRecPtr lastBackupStart ;
382
407
} XLogCtlInsert ;
@@ -693,6 +718,7 @@ static bool CheckForStandbyTrigger(void);
693
718
static void xlog_outrec (StringInfo buf , XLogRecord * record );
694
719
#endif
695
720
static void pg_start_backup_callback (int code , Datum arg );
721
+ static void pg_stop_backup_callback (int code , Datum arg );
696
722
static bool read_backup_label (XLogRecPtr * checkPointLoc ,
697
723
bool * backupEndRequired , bool * backupFromStandby );
698
724
static void rm_redo_error_callback (void * arg );
@@ -8699,15 +8725,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
8699
8725
LWLockAcquire (WALInsertLock , LW_EXCLUSIVE );
8700
8726
if (exclusive )
8701
8727
{
8702
- if (XLogCtl -> Insert .exclusiveBackup )
8728
+ /*
8729
+ * At first, mark that we're now starting an exclusive backup,
8730
+ * to ensure that there are no other sessions currently running
8731
+ * pg_start_backup() or pg_stop_backup().
8732
+ */
8733
+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_NONE )
8703
8734
{
8704
8735
LWLockRelease (WALInsertLock );
8705
8736
ereport (ERROR ,
8706
8737
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
8707
8738
errmsg ("a backup is already in progress" ),
8708
8739
errhint ("Run pg_stop_backup() and try again." )));
8709
8740
}
8710
- XLogCtl -> Insert .exclusiveBackup = true ;
8741
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING ;
8711
8742
}
8712
8743
else
8713
8744
XLogCtl -> Insert .nonExclusiveBackups ++ ;
@@ -8867,7 +8898,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
8867
8898
{
8868
8899
/*
8869
8900
* Check for existing backup label --- implies a backup is already
8870
- * running. (XXX given that we checked exclusiveBackup above,
8901
+ * running. (XXX given that we checked exclusiveBackupState above,
8871
8902
* maybe it would be OK to just unlink any such label file?)
8872
8903
*/
8873
8904
if (stat (BACKUP_LABEL_FILE , & stat_buf ) != 0 )
@@ -8908,6 +8939,16 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
8908
8939
}
8909
8940
PG_END_ENSURE_ERROR_CLEANUP (pg_start_backup_callback , (Datum ) BoolGetDatum (exclusive ));
8910
8941
8942
+ /*
8943
+ * Mark that start phase has correctly finished for an exclusive backup.
8944
+ */
8945
+ if (exclusive )
8946
+ {
8947
+ LWLockAcquire (WALInsertLock , LW_EXCLUSIVE );
8948
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
8949
+ LWLockRelease (WALInsertLock );
8950
+ }
8951
+
8911
8952
/*
8912
8953
* We're done. As a convenience, return the starting WAL location.
8913
8954
*/
@@ -8926,23 +8967,41 @@ pg_start_backup_callback(int code, Datum arg)
8926
8967
LWLockAcquire (WALInsertLock , LW_EXCLUSIVE );
8927
8968
if (exclusive )
8928
8969
{
8929
- Assert (XLogCtl -> Insert .exclusiveBackup );
8930
- XLogCtl -> Insert .exclusiveBackup = false ;
8970
+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING );
8971
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
8931
8972
}
8932
8973
else
8933
8974
{
8934
8975
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
8935
8976
XLogCtl -> Insert .nonExclusiveBackups -- ;
8936
8977
}
8937
8978
8938
- if (! XLogCtl -> Insert .exclusiveBackup &&
8979
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
8939
8980
XLogCtl -> Insert .nonExclusiveBackups == 0 )
8940
8981
{
8941
8982
XLogCtl -> Insert .forcePageWrites = false;
8942
8983
}
8943
8984
LWLockRelease (WALInsertLock );
8944
8985
}
8945
8986
8987
+ /*
8988
+ * Error cleanup callback for pg_stop_backup
8989
+ */
8990
+ static void
8991
+ pg_stop_backup_callback (int code , Datum arg )
8992
+ {
8993
+ bool exclusive = DatumGetBool (arg );
8994
+
8995
+ /* Update backup status on failure */
8996
+ LWLockAcquire (WALInsertLock , LW_EXCLUSIVE );
8997
+ if (exclusive )
8998
+ {
8999
+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING );
9000
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
9001
+ }
9002
+ LWLockRelease (WALInsertLock );
9003
+ }
9004
+
8946
9005
/*
8947
9006
* do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup()
8948
9007
* function.
@@ -9006,12 +9065,85 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
9006
9065
errmsg ("WAL level not sufficient for making an online backup" ),
9007
9066
errhint ("wal_level must be set to \"archive\" or \"hot_standby\" at server start." )));
9008
9067
9068
+ if (exclusive )
9069
+ {
9070
+ /*
9071
+ * At first, mark that we're now stopping an exclusive backup,
9072
+ * to ensure that there are no other sessions currently running
9073
+ * pg_start_backup() or pg_stop_backup().
9074
+ */
9075
+ LWLockAcquire (WALInsertLock , LW_EXCLUSIVE );
9076
+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS )
9077
+ {
9078
+ LWLockRelease (WALInsertLock );
9079
+ ereport (ERROR ,
9080
+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9081
+ errmsg ("exclusive backup not in progress" )));
9082
+ }
9083
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING ;
9084
+ LWLockRelease (WALInsertLock );
9085
+
9086
+ /*
9087
+ * Remove backup_label. In case of failure, the state for an exclusive
9088
+ * backup is switched back to in-progress.
9089
+ */
9090
+ PG_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum ) BoolGetDatum (exclusive ));
9091
+ {
9092
+ /*
9093
+ * Read the existing label file into memory.
9094
+ */
9095
+ struct stat statbuf ;
9096
+ int r ;
9097
+
9098
+ if (stat (BACKUP_LABEL_FILE , & statbuf ))
9099
+ {
9100
+ if (errno != ENOENT )
9101
+ ereport (ERROR ,
9102
+ (errcode_for_file_access (),
9103
+ errmsg ("could not stat file \"%s\": %m" ,
9104
+ BACKUP_LABEL_FILE )));
9105
+ ereport (ERROR ,
9106
+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9107
+ errmsg ("a backup is not in progress" )));
9108
+ }
9109
+
9110
+ lfp = AllocateFile (BACKUP_LABEL_FILE , "r" );
9111
+ if (!lfp )
9112
+ {
9113
+ ereport (ERROR ,
9114
+ (errcode_for_file_access (),
9115
+ errmsg ("could not read file \"%s\": %m" ,
9116
+ BACKUP_LABEL_FILE )));
9117
+ }
9118
+ labelfile = palloc (statbuf .st_size + 1 );
9119
+ r = fread (labelfile , statbuf .st_size , 1 , lfp );
9120
+ labelfile [statbuf .st_size ] = '\0' ;
9121
+
9122
+ /*
9123
+ * Close and remove the backup label file
9124
+ */
9125
+ if (r != 1 || ferror (lfp ) || FreeFile (lfp ))
9126
+ ereport (ERROR ,
9127
+ (errcode_for_file_access (),
9128
+ errmsg ("could not read file \"%s\": %m" ,
9129
+ BACKUP_LABEL_FILE )));
9130
+ if (unlink (BACKUP_LABEL_FILE ) != 0 )
9131
+ ereport (ERROR ,
9132
+ (errcode_for_file_access (),
9133
+ errmsg ("could not remove file \"%s\": %m" ,
9134
+ BACKUP_LABEL_FILE )));
9135
+ }
9136
+ PG_END_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum ) BoolGetDatum (exclusive ));
9137
+ }
9138
+
9009
9139
/*
9010
9140
* OK to update backup counters and forcePageWrites
9011
9141
*/
9012
9142
LWLockAcquire (WALInsertLock , LW_EXCLUSIVE );
9013
9143
if (exclusive )
9014
- XLogCtl -> Insert .exclusiveBackup = false;
9144
+ {
9145
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
9146
+ }
9015
9147
else
9016
9148
{
9017
9149
/*
@@ -9024,60 +9156,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
9024
9156
XLogCtl -> Insert .nonExclusiveBackups -- ;
9025
9157
}
9026
9158
9027
- if (! XLogCtl -> Insert .exclusiveBackup &&
9159
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
9028
9160
XLogCtl -> Insert .nonExclusiveBackups == 0 )
9029
9161
{
9030
9162
XLogCtl -> Insert .forcePageWrites = false;
9031
9163
}
9032
9164
LWLockRelease (WALInsertLock );
9033
9165
9034
- if (exclusive )
9035
- {
9036
- /*
9037
- * Read the existing label file into memory.
9038
- */
9039
- struct stat statbuf ;
9040
- int r ;
9041
-
9042
- if (stat (BACKUP_LABEL_FILE , & statbuf ))
9043
- {
9044
- if (errno != ENOENT )
9045
- ereport (ERROR ,
9046
- (errcode_for_file_access (),
9047
- errmsg ("could not stat file \"%s\": %m" ,
9048
- BACKUP_LABEL_FILE )));
9049
- ereport (ERROR ,
9050
- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9051
- errmsg ("a backup is not in progress" )));
9052
- }
9053
-
9054
- lfp = AllocateFile (BACKUP_LABEL_FILE , "r" );
9055
- if (!lfp )
9056
- {
9057
- ereport (ERROR ,
9058
- (errcode_for_file_access (),
9059
- errmsg ("could not read file \"%s\": %m" ,
9060
- BACKUP_LABEL_FILE )));
9061
- }
9062
- labelfile = palloc (statbuf .st_size + 1 );
9063
- r = fread (labelfile , statbuf .st_size , 1 , lfp );
9064
- labelfile [statbuf .st_size ] = '\0' ;
9065
-
9066
- /*
9067
- * Close and remove the backup label file
9068
- */
9069
- if (r != 1 || ferror (lfp ) || FreeFile (lfp ))
9070
- ereport (ERROR ,
9071
- (errcode_for_file_access (),
9072
- errmsg ("could not read file \"%s\": %m" ,
9073
- BACKUP_LABEL_FILE )));
9074
- if (unlink (BACKUP_LABEL_FILE ) != 0 )
9075
- ereport (ERROR ,
9076
- (errcode_for_file_access (),
9077
- errmsg ("could not remove file \"%s\": %m" ,
9078
- BACKUP_LABEL_FILE )));
9079
- }
9080
-
9081
9166
/*
9082
9167
* Read and parse the START WAL LOCATION line (this code is pretty crude,
9083
9168
* but we are not expecting any variability in the file format).
@@ -9318,7 +9403,7 @@ do_pg_abort_backup(void)
9318
9403
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
9319
9404
XLogCtl -> Insert .nonExclusiveBackups -- ;
9320
9405
9321
- if (! XLogCtl -> Insert .exclusiveBackup &&
9406
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
9322
9407
XLogCtl -> Insert .nonExclusiveBackups == 0 )
9323
9408
{
9324
9409
XLogCtl -> Insert .forcePageWrites = false;
0 commit comments