mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Fix bug in cancellation of non-exclusive backup to avoid assertion failure.
Previously an assertion failure occurred when pg_stop_backup() for non-exclusive backup was aborted while it's waiting for WAL files to be archived. This assertion failure happened in do_pg_abort_backup() which was called when a non-exclusive backup was canceled. do_pg_abort_backup() assumes that there is at least one non-exclusive backup running when it's called. But pg_stop_backup() can be canceled even after it marks the end of non-exclusive backup (e.g., during waiting for WAL archiving). This broke the assumption that do_pg_abort_backup() relies on, and which caused an assertion failure. This commit changes do_pg_abort_backup() so that it does nothing when non-exclusive backup has been already marked as completed. That is, the asssumption is also changed, and do_pg_abort_backup() now can handle even the case where it's called when there is no running backup. Backpatch to 9.6 where SQL-callable non-exclusive backup was added. Author: Masahiko Sawada and Michael Paquier Reviewed-By: Robert Haas and Fujii Masao Discussion: https://www.postgresql.org/message-id/CAD21AoD2L1Fu2c==gnVASMyFAAaq3y-AQ2uEVj-zTCGFFjvmDg@mail.gmail.com
This commit is contained in:
		
							parent
							
								
									fd7c0fa732
								
							
						
					
					
						commit
						56a95ee511
					
				| @ -10628,13 +10628,20 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p, | |||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Mark that start phase has correctly finished for an exclusive backup. | 	 * Mark that start phase has correctly finished for an exclusive backup. | ||||||
| 	 * Session-level locks are updated as well to reflect that state. | 	 * Session-level locks are updated as well to reflect that state. | ||||||
|  | 	 * | ||||||
|  | 	 * Note that CHECK_FOR_INTERRUPTS() must not occur while updating | ||||||
|  | 	 * backup counters and session-level lock. Otherwise they can be | ||||||
|  | 	 * updated inconsistently, and which might cause do_pg_abort_backup() | ||||||
|  | 	 * to fail. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (exclusive) | 	if (exclusive) | ||||||
| 	{ | 	{ | ||||||
| 		WALInsertLockAcquireExclusive(); | 		WALInsertLockAcquireExclusive(); | ||||||
| 		XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS; | 		XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS; | ||||||
| 		WALInsertLockRelease(); | 
 | ||||||
|  | 		/* Set session-level lock */ | ||||||
| 		sessionBackupState = SESSION_BACKUP_EXCLUSIVE; | 		sessionBackupState = SESSION_BACKUP_EXCLUSIVE; | ||||||
|  | 		WALInsertLockRelease(); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 		sessionBackupState = SESSION_BACKUP_NON_EXCLUSIVE; | 		sessionBackupState = SESSION_BACKUP_NON_EXCLUSIVE; | ||||||
| @ -10838,7 +10845,11 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * OK to update backup counters and forcePageWrites | 	 * OK to update backup counters, forcePageWrites and session-level lock. | ||||||
|  | 	 * | ||||||
|  | 	 * Note that CHECK_FOR_INTERRUPTS() must not occur while updating them. | ||||||
|  | 	 * Otherwise they can be updated inconsistently, and which might cause | ||||||
|  | 	 * do_pg_abort_backup() to fail. | ||||||
| 	 */ | 	 */ | ||||||
| 	WALInsertLockAcquireExclusive(); | 	WALInsertLockAcquireExclusive(); | ||||||
| 	if (exclusive) | 	if (exclusive) | ||||||
| @ -10862,11 +10873,20 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) | |||||||
| 	{ | 	{ | ||||||
| 		XLogCtl->Insert.forcePageWrites = false; | 		XLogCtl->Insert.forcePageWrites = false; | ||||||
| 	} | 	} | ||||||
| 	WALInsertLockRelease(); |  | ||||||
| 
 | 
 | ||||||
| 	/* Clean up session-level lock */ | 	/*
 | ||||||
|  | 	 * Clean up session-level lock. | ||||||
|  | 	 * | ||||||
|  | 	 * You might think that WALInsertLockRelease() can be called | ||||||
|  | 	 * before cleaning up session-level lock because session-level | ||||||
|  | 	 * lock doesn't need to be protected with WAL insertion lock. | ||||||
|  | 	 * But since CHECK_FOR_INTERRUPTS() can occur in it, | ||||||
|  | 	 * session-level lock must be cleaned up before it. | ||||||
|  | 	 */ | ||||||
| 	sessionBackupState = SESSION_BACKUP_NONE; | 	sessionBackupState = SESSION_BACKUP_NONE; | ||||||
| 
 | 
 | ||||||
|  | 	WALInsertLockRelease(); | ||||||
|  | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Read and parse the START WAL LOCATION line (this code is pretty crude, | 	 * Read and parse the START WAL LOCATION line (this code is pretty crude, | ||||||
| 	 * but we are not expecting any variability in the file format). | 	 * but we are not expecting any variability in the file format). | ||||||
| @ -11104,8 +11124,16 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p) | |||||||
| void | void | ||||||
| do_pg_abort_backup(void) | do_pg_abort_backup(void) | ||||||
| { | { | ||||||
|  | 	/*
 | ||||||
|  | 	 * Quick exit if session is not keeping around a non-exclusive backup | ||||||
|  | 	 * already started. | ||||||
|  | 	 */ | ||||||
|  | 	if (sessionBackupState == SESSION_BACKUP_NONE) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
| 	WALInsertLockAcquireExclusive(); | 	WALInsertLockAcquireExclusive(); | ||||||
| 	Assert(XLogCtl->Insert.nonExclusiveBackups > 0); | 	Assert(XLogCtl->Insert.nonExclusiveBackups > 0); | ||||||
|  | 	Assert(sessionBackupState == SESSION_BACKUP_NON_EXCLUSIVE); | ||||||
| 	XLogCtl->Insert.nonExclusiveBackups--; | 	XLogCtl->Insert.nonExclusiveBackups--; | ||||||
| 
 | 
 | ||||||
| 	if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && | 	if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE && | ||||||
|  | |||||||
| @ -215,7 +215,7 @@ perform_base_backup(basebackup_options *opt) | |||||||
| 	 * Once do_pg_start_backup has been called, ensure that any failure causes | 	 * Once do_pg_start_backup has been called, ensure that any failure causes | ||||||
| 	 * us to abort the backup so we don't "leak" a backup counter. For this | 	 * us to abort the backup so we don't "leak" a backup counter. For this | ||||||
| 	 * reason, *all* functionality between do_pg_start_backup() and | 	 * reason, *all* functionality between do_pg_start_backup() and | ||||||
| 	 * do_pg_stop_backup() should be inside the error cleanup block! | 	 * the end of do_pg_stop_backup() should be inside the error cleanup block! | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
| 	PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); | 	PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); | ||||||
| @ -324,10 +324,11 @@ perform_base_backup(basebackup_options *opt) | |||||||
| 			else | 			else | ||||||
| 				pq_putemptymessage('c');	/* CopyDone */ | 				pq_putemptymessage('c');	/* CopyDone */ | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		endptr = do_pg_stop_backup(labelfile->data, !opt->nowait, &endtli); | ||||||
| 	} | 	} | ||||||
| 	PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); | 	PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); | ||||||
| 
 | 
 | ||||||
| 	endptr = do_pg_stop_backup(labelfile->data, !opt->nowait, &endtli); |  | ||||||
| 
 | 
 | ||||||
| 	if (opt->includewal) | 	if (opt->includewal) | ||||||
| 	{ | 	{ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user