@@ -459,6 +459,29 @@ typedef union WALInsertLockPadded
459
459
char pad [PG_CACHE_LINE_SIZE ];
460
460
} WALInsertLockPadded ;
461
461
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
+
462
485
/*
463
486
* Shared state data for WAL insertion.
464
487
*/
@@ -500,13 +523,15 @@ typedef struct XLogCtlInsert
500
523
bool fullPageWrites ;
501
524
502
525
/*
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.
508
533
*/
509
- bool exclusiveBackup ;
534
+ ExclusiveBackupState exclusiveBackupState ;
510
535
int nonExclusiveBackups ;
511
536
XLogRecPtr lastBackupStart ;
512
537
@@ -845,6 +870,7 @@ static void xlog_outrec(StringInfo buf, XLogReaderState *record);
845
870
#endif
846
871
static void xlog_outdesc (StringInfo buf , XLogReaderState * record );
847
872
static void pg_start_backup_callback (int code , Datum arg );
873
+ static void pg_stop_backup_callback (int code , Datum arg );
848
874
static bool read_backup_label (XLogRecPtr * checkPointLoc ,
849
875
bool * backupEndRequired , bool * backupFromStandby );
850
876
static bool read_tablespace_map (List * * tablespaces );
@@ -9872,15 +9898,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
9872
9898
WALInsertLockAcquireExclusive ();
9873
9899
if (exclusive )
9874
9900
{
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 )
9876
9907
{
9877
9908
WALInsertLockRelease ();
9878
9909
ereport (ERROR ,
9879
9910
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9880
9911
errmsg ("a backup is already in progress" ),
9881
9912
errhint ("Run pg_stop_backup() and try again." )));
9882
9913
}
9883
- XLogCtl -> Insert .exclusiveBackup = true ;
9914
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING ;
9884
9915
}
9885
9916
else
9886
9917
XLogCtl -> Insert .nonExclusiveBackups ++ ;
@@ -10135,7 +10166,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
10135
10166
{
10136
10167
/*
10137
10168
* 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,
10139
10170
* maybe it would be OK to just unlink any such label file?)
10140
10171
*/
10141
10172
if (stat (BACKUP_LABEL_FILE , & stat_buf ) != 0 )
@@ -10216,6 +10247,16 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
10216
10247
}
10217
10248
PG_END_ENSURE_ERROR_CLEANUP (pg_start_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10218
10249
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
+
10219
10260
/*
10220
10261
* We're done. As a convenience, return the starting WAL location.
10221
10262
*/
@@ -10234,23 +10275,41 @@ pg_start_backup_callback(int code, Datum arg)
10234
10275
WALInsertLockAcquireExclusive ();
10235
10276
if (exclusive )
10236
10277
{
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 ;
10239
10280
}
10240
10281
else
10241
10282
{
10242
10283
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
10243
10284
XLogCtl -> Insert .nonExclusiveBackups -- ;
10244
10285
}
10245
10286
10246
- if (! XLogCtl -> Insert .exclusiveBackup &&
10287
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10247
10288
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10248
10289
{
10249
10290
XLogCtl -> Insert .forcePageWrites = false;
10250
10291
}
10251
10292
WALInsertLockRelease ();
10252
10293
}
10253
10294
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
+
10254
10313
/*
10255
10314
* do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup()
10256
10315
* function.
@@ -10313,20 +10372,91 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
10313
10372
errmsg ("WAL level not sufficient for making an online backup" ),
10314
10373
errhint ("wal_level must be set to \"replica\" or \"logical\" at server start." )));
10315
10374
10316
- /*
10317
- * OK to update backup counters and forcePageWrites
10318
- */
10319
- WALInsertLockAcquireExclusive ();
10320
10375
if (exclusive )
10321
10376
{
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 )
10323
10384
{
10324
10385
WALInsertLockRelease ();
10325
10386
ereport (ERROR ,
10326
10387
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10327
10388
errmsg ("exclusive backup not in progress" )));
10328
10389
}
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 ;
10330
10460
}
10331
10461
else
10332
10462
{
@@ -10340,66 +10470,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
10340
10470
XLogCtl -> Insert .nonExclusiveBackups -- ;
10341
10471
}
10342
10472
10343
- if (! XLogCtl -> Insert .exclusiveBackup &&
10473
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10344
10474
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10345
10475
{
10346
10476
XLogCtl -> Insert .forcePageWrites = false;
10347
10477
}
10348
10478
WALInsertLockRelease ();
10349
10479
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
-
10403
10480
/*
10404
10481
* Read and parse the START WAL LOCATION line (this code is pretty crude,
10405
10482
* but we are not expecting any variability in the file format).
@@ -10636,7 +10713,7 @@ do_pg_abort_backup(void)
10636
10713
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
10637
10714
XLogCtl -> Insert .nonExclusiveBackups -- ;
10638
10715
10639
- if (! XLogCtl -> Insert .exclusiveBackup &&
10716
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10640
10717
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10641
10718
{
10642
10719
XLogCtl -> Insert .forcePageWrites = false;
0 commit comments