@@ -460,6 +460,29 @@ typedef union WALInsertLockPadded
460
460
char pad [PG_CACHE_LINE_SIZE ];
461
461
} WALInsertLockPadded ;
462
462
463
+ /*
464
+ * State of an exclusive backup, necessary to control concurrent activities
465
+ * across sessions when working on exclusive backups.
466
+ *
467
+ * EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually
468
+ * running, to be more precise pg_start_backup() is not being executed for
469
+ * an exclusive backup and there is no exclusive backup in progress.
470
+ * EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an
471
+ * exclusive backup.
472
+ * EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished
473
+ * running and an exclusive backup is in progress. pg_stop_backup() is
474
+ * needed to finish it.
475
+ * EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an
476
+ * exclusive backup.
477
+ */
478
+ typedef enum ExclusiveBackupState
479
+ {
480
+ EXCLUSIVE_BACKUP_NONE = 0 ,
481
+ EXCLUSIVE_BACKUP_STARTING ,
482
+ EXCLUSIVE_BACKUP_IN_PROGRESS ,
483
+ EXCLUSIVE_BACKUP_STOPPING
484
+ } ExclusiveBackupState ;
485
+
463
486
/*
464
487
* Shared state data for WAL insertion.
465
488
*/
@@ -501,13 +524,15 @@ typedef struct XLogCtlInsert
501
524
bool fullPageWrites ;
502
525
503
526
/*
504
- * exclusiveBackup is true if a backup started with pg_start_backup() is
505
- * in progress, and nonExclusiveBackups is a counter indicating the number
506
- * of streaming base backups currently in progress. forcePageWrites is set
507
- * to true when either of these is non-zero. lastBackupStart is the latest
508
- * checkpoint redo location used as a starting point for an online backup.
527
+ * exclusiveBackupState indicates the state of an exclusive backup
528
+ * (see comments of ExclusiveBackupState for more details).
529
+ * nonExclusiveBackups is a counter indicating the number of streaming
530
+ * base backups currently in progress. forcePageWrites is set to true
531
+ * when either of these is non-zero. lastBackupStart is the latest
532
+ * checkpoint redo location used as a starting point for an online
533
+ * backup.
509
534
*/
510
- bool exclusiveBackup ;
535
+ ExclusiveBackupState exclusiveBackupState ;
511
536
int nonExclusiveBackups ;
512
537
XLogRecPtr lastBackupStart ;
513
538
@@ -846,6 +871,7 @@ static void xlog_outrec(StringInfo buf, XLogReaderState *record);
846
871
#endif
847
872
static void xlog_outdesc (StringInfo buf , XLogReaderState * record );
848
873
static void pg_start_backup_callback (int code , Datum arg );
874
+ static void pg_stop_backup_callback (int code , Datum arg );
849
875
static bool read_backup_label (XLogRecPtr * checkPointLoc ,
850
876
bool * backupEndRequired , bool * backupFromStandby );
851
877
static bool read_tablespace_map (List * * tablespaces );
@@ -9890,15 +9916,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
9890
9916
WALInsertLockAcquireExclusive ();
9891
9917
if (exclusive )
9892
9918
{
9893
- if (XLogCtl -> Insert .exclusiveBackup )
9919
+ /*
9920
+ * At first, mark that we're now starting an exclusive backup,
9921
+ * to ensure that there are no other sessions currently running
9922
+ * pg_start_backup() or pg_stop_backup().
9923
+ */
9924
+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_NONE )
9894
9925
{
9895
9926
WALInsertLockRelease ();
9896
9927
ereport (ERROR ,
9897
9928
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9898
9929
errmsg ("a backup is already in progress" ),
9899
9930
errhint ("Run pg_stop_backup() and try again." )));
9900
9931
}
9901
- XLogCtl -> Insert .exclusiveBackup = true ;
9932
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING ;
9902
9933
}
9903
9934
else
9904
9935
XLogCtl -> Insert .nonExclusiveBackups ++ ;
@@ -10153,7 +10184,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
10153
10184
{
10154
10185
/*
10155
10186
* Check for existing backup label --- implies a backup is already
10156
- * running. (XXX given that we checked exclusiveBackup above,
10187
+ * running. (XXX given that we checked exclusiveBackupState above,
10157
10188
* maybe it would be OK to just unlink any such label file?)
10158
10189
*/
10159
10190
if (stat (BACKUP_LABEL_FILE , & stat_buf ) != 0 )
@@ -10234,6 +10265,16 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
10234
10265
}
10235
10266
PG_END_ENSURE_ERROR_CLEANUP (pg_start_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10236
10267
10268
+ /*
10269
+ * Mark that start phase has correctly finished for an exclusive backup.
10270
+ */
10271
+ if (exclusive )
10272
+ {
10273
+ WALInsertLockAcquireExclusive ();
10274
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
10275
+ WALInsertLockRelease ();
10276
+ }
10277
+
10237
10278
/*
10238
10279
* We're done. As a convenience, return the starting WAL location.
10239
10280
*/
@@ -10252,23 +10293,41 @@ pg_start_backup_callback(int code, Datum arg)
10252
10293
WALInsertLockAcquireExclusive ();
10253
10294
if (exclusive )
10254
10295
{
10255
- Assert (XLogCtl -> Insert .exclusiveBackup );
10256
- XLogCtl -> Insert .exclusiveBackup = false ;
10296
+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING );
10297
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
10257
10298
}
10258
10299
else
10259
10300
{
10260
10301
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
10261
10302
XLogCtl -> Insert .nonExclusiveBackups -- ;
10262
10303
}
10263
10304
10264
- if (! XLogCtl -> Insert .exclusiveBackup &&
10305
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10265
10306
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10266
10307
{
10267
10308
XLogCtl -> Insert .forcePageWrites = false;
10268
10309
}
10269
10310
WALInsertLockRelease ();
10270
10311
}
10271
10312
10313
+ /*
10314
+ * Error cleanup callback for pg_stop_backup
10315
+ */
10316
+ static void
10317
+ pg_stop_backup_callback (int code , Datum arg )
10318
+ {
10319
+ bool exclusive = DatumGetBool (arg );
10320
+
10321
+ /* Update backup status on failure */
10322
+ WALInsertLockAcquireExclusive ();
10323
+ if (exclusive )
10324
+ {
10325
+ Assert (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING );
10326
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS ;
10327
+ }
10328
+ WALInsertLockRelease ();
10329
+ }
10330
+
10272
10331
/*
10273
10332
* do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup()
10274
10333
* function.
@@ -10331,20 +10390,91 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
10331
10390
errmsg ("WAL level not sufficient for making an online backup" ),
10332
10391
errhint ("wal_level must be set to \"replica\" or \"logical\" at server start." )));
10333
10392
10334
- /*
10335
- * OK to update backup counters and forcePageWrites
10336
- */
10337
- WALInsertLockAcquireExclusive ();
10338
10393
if (exclusive )
10339
10394
{
10340
- if (!XLogCtl -> Insert .exclusiveBackup )
10395
+ /*
10396
+ * At first, mark that we're now stopping an exclusive backup,
10397
+ * to ensure that there are no other sessions currently running
10398
+ * pg_start_backup() or pg_stop_backup().
10399
+ */
10400
+ WALInsertLockAcquireExclusive ();
10401
+ if (XLogCtl -> Insert .exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS )
10341
10402
{
10342
10403
WALInsertLockRelease ();
10343
10404
ereport (ERROR ,
10344
10405
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10345
10406
errmsg ("exclusive backup not in progress" )));
10346
10407
}
10347
- XLogCtl -> Insert .exclusiveBackup = false;
10408
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING ;
10409
+ WALInsertLockRelease ();
10410
+
10411
+ /*
10412
+ * Remove backup_label. In case of failure, the state for an exclusive
10413
+ * backup is switched back to in-progress.
10414
+ */
10415
+ PG_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10416
+ {
10417
+ /*
10418
+ * Read the existing label file into memory.
10419
+ */
10420
+ struct stat statbuf ;
10421
+ int r ;
10422
+
10423
+ if (stat (BACKUP_LABEL_FILE , & statbuf ))
10424
+ {
10425
+ /* should not happen per the upper checks */
10426
+ if (errno != ENOENT )
10427
+ ereport (ERROR ,
10428
+ (errcode_for_file_access (),
10429
+ errmsg ("could not stat file \"%s\": %m" ,
10430
+ BACKUP_LABEL_FILE )));
10431
+ ereport (ERROR ,
10432
+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10433
+ errmsg ("a backup is not in progress" )));
10434
+ }
10435
+
10436
+ lfp = AllocateFile (BACKUP_LABEL_FILE , "r" );
10437
+ if (!lfp )
10438
+ {
10439
+ ereport (ERROR ,
10440
+ (errcode_for_file_access (),
10441
+ errmsg ("could not read file \"%s\": %m" ,
10442
+ BACKUP_LABEL_FILE )));
10443
+ }
10444
+ labelfile = palloc (statbuf .st_size + 1 );
10445
+ r = fread (labelfile , statbuf .st_size , 1 , lfp );
10446
+ labelfile [statbuf .st_size ] = '\0' ;
10447
+
10448
+ /*
10449
+ * Close and remove the backup label file
10450
+ */
10451
+ if (r != 1 || ferror (lfp ) || FreeFile (lfp ))
10452
+ ereport (ERROR ,
10453
+ (errcode_for_file_access (),
10454
+ errmsg ("could not read file \"%s\": %m" ,
10455
+ BACKUP_LABEL_FILE )));
10456
+ if (unlink (BACKUP_LABEL_FILE ) != 0 )
10457
+ ereport (ERROR ,
10458
+ (errcode_for_file_access (),
10459
+ errmsg ("could not remove file \"%s\": %m" ,
10460
+ BACKUP_LABEL_FILE )));
10461
+
10462
+ /*
10463
+ * Remove tablespace_map file if present, it is created only if there
10464
+ * are tablespaces.
10465
+ */
10466
+ unlink (TABLESPACE_MAP );
10467
+ }
10468
+ PG_END_ENSURE_ERROR_CLEANUP (pg_stop_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10469
+ }
10470
+
10471
+ /*
10472
+ * OK to update backup counters and forcePageWrites
10473
+ */
10474
+ WALInsertLockAcquireExclusive ();
10475
+ if (exclusive )
10476
+ {
10477
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_NONE ;
10348
10478
}
10349
10479
else
10350
10480
{
@@ -10358,66 +10488,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
10358
10488
XLogCtl -> Insert .nonExclusiveBackups -- ;
10359
10489
}
10360
10490
10361
- if (! XLogCtl -> Insert .exclusiveBackup &&
10491
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10362
10492
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10363
10493
{
10364
10494
XLogCtl -> Insert .forcePageWrites = false;
10365
10495
}
10366
10496
WALInsertLockRelease ();
10367
10497
10368
- if (exclusive )
10369
- {
10370
- /*
10371
- * Read the existing label file into memory.
10372
- */
10373
- struct stat statbuf ;
10374
- int r ;
10375
-
10376
- if (stat (BACKUP_LABEL_FILE , & statbuf ))
10377
- {
10378
- if (errno != ENOENT )
10379
- ereport (ERROR ,
10380
- (errcode_for_file_access (),
10381
- errmsg ("could not stat file \"%s\": %m" ,
10382
- BACKUP_LABEL_FILE )));
10383
- ereport (ERROR ,
10384
- (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10385
- errmsg ("a backup is not in progress" )));
10386
- }
10387
-
10388
- lfp = AllocateFile (BACKUP_LABEL_FILE , "r" );
10389
- if (!lfp )
10390
- {
10391
- ereport (ERROR ,
10392
- (errcode_for_file_access (),
10393
- errmsg ("could not read file \"%s\": %m" ,
10394
- BACKUP_LABEL_FILE )));
10395
- }
10396
- labelfile = palloc (statbuf .st_size + 1 );
10397
- r = fread (labelfile , statbuf .st_size , 1 , lfp );
10398
- labelfile [statbuf .st_size ] = '\0' ;
10399
-
10400
- /*
10401
- * Close and remove the backup label file
10402
- */
10403
- if (r != 1 || ferror (lfp ) || FreeFile (lfp ))
10404
- ereport (ERROR ,
10405
- (errcode_for_file_access (),
10406
- errmsg ("could not read file \"%s\": %m" ,
10407
- BACKUP_LABEL_FILE )));
10408
- if (unlink (BACKUP_LABEL_FILE ) != 0 )
10409
- ereport (ERROR ,
10410
- (errcode_for_file_access (),
10411
- errmsg ("could not remove file \"%s\": %m" ,
10412
- BACKUP_LABEL_FILE )));
10413
-
10414
- /*
10415
- * Remove tablespace_map file if present, it is created only if there
10416
- * are tablespaces.
10417
- */
10418
- unlink (TABLESPACE_MAP );
10419
- }
10420
-
10421
10498
/*
10422
10499
* Read and parse the START WAL LOCATION line (this code is pretty crude,
10423
10500
* but we are not expecting any variability in the file format).
@@ -10654,7 +10731,7 @@ do_pg_abort_backup(void)
10654
10731
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
10655
10732
XLogCtl -> Insert .nonExclusiveBackups -- ;
10656
10733
10657
- if (! XLogCtl -> Insert .exclusiveBackup &&
10734
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10658
10735
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10659
10736
{
10660
10737
XLogCtl -> Insert .forcePageWrites = false;
0 commit comments