@@ -462,6 +462,29 @@ typedef union WALInsertLockPadded
462
462
char pad [PG_CACHE_LINE_SIZE ];
463
463
} WALInsertLockPadded ;
464
464
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
+
465
488
/*
466
489
* Shared state data for WAL insertion.
467
490
*/
@@ -503,13 +526,15 @@ typedef struct XLogCtlInsert
503
526
bool fullPageWrites ;
504
527
505
528
/*
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.
511
536
*/
512
- bool exclusiveBackup ;
537
+ ExclusiveBackupState exclusiveBackupState ;
513
538
int nonExclusiveBackups ;
514
539
XLogRecPtr lastBackupStart ;
515
540
@@ -847,6 +872,7 @@ static void xlog_outrec(StringInfo buf, XLogReaderState *record);
847
872
#endif
848
873
static void xlog_outdesc (StringInfo buf , XLogReaderState * record );
849
874
static void pg_start_backup_callback (int code , Datum arg );
875
+ static void pg_stop_backup_callback (int code , Datum arg );
850
876
static bool read_backup_label (XLogRecPtr * checkPointLoc ,
851
877
bool * backupEndRequired , bool * backupFromStandby );
852
878
static bool read_tablespace_map (List * * tablespaces );
@@ -9845,15 +9871,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
9845
9871
WALInsertLockAcquireExclusive ();
9846
9872
if (exclusive )
9847
9873
{
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 )
9849
9880
{
9850
9881
WALInsertLockRelease ();
9851
9882
ereport (ERROR ,
9852
9883
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9853
9884
errmsg ("a backup is already in progress" ),
9854
9885
errhint ("Run pg_stop_backup() and try again." )));
9855
9886
}
9856
- XLogCtl -> Insert .exclusiveBackup = true ;
9887
+ XLogCtl -> Insert .exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING ;
9857
9888
}
9858
9889
else
9859
9890
XLogCtl -> Insert .nonExclusiveBackups ++ ;
@@ -10110,7 +10141,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
10110
10141
{
10111
10142
/*
10112
10143
* Check for existing backup label --- implies a backup is already
10113
- * running. (XXX given that we checked exclusiveBackup above,
10144
+ * running. (XXX given that we checked exclusiveBackupState above,
10114
10145
* maybe it would be OK to just unlink any such label file?)
10115
10146
*/
10116
10147
if (stat (BACKUP_LABEL_FILE , & stat_buf ) != 0 )
@@ -10191,6 +10222,16 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
10191
10222
}
10192
10223
PG_END_ENSURE_ERROR_CLEANUP (pg_start_backup_callback , (Datum ) BoolGetDatum (exclusive ));
10193
10224
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
+
10194
10235
/*
10195
10236
* We're done. As a convenience, return the starting WAL location.
10196
10237
*/
@@ -10209,16 +10250,16 @@ pg_start_backup_callback(int code, Datum arg)
10209
10250
WALInsertLockAcquireExclusive ();
10210
10251
if (exclusive )
10211
10252
{
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 ;
10214
10255
}
10215
10256
else
10216
10257
{
10217
10258
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
10218
10259
XLogCtl -> Insert .nonExclusiveBackups -- ;
10219
10260
}
10220
10261
10221
- if (! XLogCtl -> Insert .exclusiveBackup &&
10262
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10222
10263
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10223
10264
{
10224
10265
XLogCtl -> Insert .forcePageWrites = false;
@@ -10228,6 +10269,24 @@ pg_start_backup_callback(int code, Datum arg)
10228
10269
cfs_control_gc (SavedGCState ); /* Restore CFS GC activity */
10229
10270
}
10230
10271
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
+
10231
10290
/*
10232
10291
* do_pg_stop_backup is the workhorse of the user-visible pg_stop_backup()
10233
10292
* function.
@@ -10290,20 +10349,91 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
10290
10349
errmsg ("WAL level not sufficient for making an online backup" ),
10291
10350
errhint ("wal_level must be set to \"replica\" or \"logical\" at server start." )));
10292
10351
10293
- /*
10294
- * OK to update backup counters and forcePageWrites
10295
- */
10296
- WALInsertLockAcquireExclusive ();
10297
10352
if (exclusive )
10298
10353
{
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 )
10300
10361
{
10301
10362
WALInsertLockRelease ();
10302
10363
ereport (ERROR ,
10303
10364
(errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
10304
10365
errmsg ("exclusive backup not in progress" )));
10305
10366
}
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 ;
10307
10437
}
10308
10438
else
10309
10439
{
@@ -10317,66 +10447,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
10317
10447
XLogCtl -> Insert .nonExclusiveBackups -- ;
10318
10448
}
10319
10449
10320
- if (! XLogCtl -> Insert .exclusiveBackup &&
10450
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10321
10451
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10322
10452
{
10323
10453
XLogCtl -> Insert .forcePageWrites = false;
10324
10454
}
10325
10455
WALInsertLockRelease ();
10326
10456
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
-
10380
10457
/*
10381
10458
* Read and parse the START WAL LOCATION line (this code is pretty crude,
10382
10459
* but we are not expecting any variability in the file format).
@@ -10615,7 +10692,7 @@ do_pg_abort_backup(void)
10615
10692
Assert (XLogCtl -> Insert .nonExclusiveBackups > 0 );
10616
10693
XLogCtl -> Insert .nonExclusiveBackups -- ;
10617
10694
10618
- if (! XLogCtl -> Insert .exclusiveBackup &&
10695
+ if (XLogCtl -> Insert .exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
10619
10696
XLogCtl -> Insert .nonExclusiveBackups == 0 )
10620
10697
{
10621
10698
XLogCtl -> Insert .forcePageWrites = false;
0 commit comments