Greetings,

Among the changes made to PG's recovery in v12 was to set
recovery_target_timeline to be 'latest' by default.  That's handy when
you're flipping back and forth between replicas and want to have
everyone follow that game, but it's made doing some basic things like
restoring from a backup problematic.

Specifically, if you take a backup off a primary and, while that backup
is going on, some replica is promoted and drops a .history file into the
WAL repo, that backup is no longer able to be restored with the new
recovery_target_timeline default.  What happens is that the restore
process will happily follow the timeline change- even though it happened
before we reached consistency, and then it'll never find the needed
end-of-backup WAL point that would allow us to reach consistency.

Naturally, a primary isn't ever going to do a TL switch, and we already
throw an error during an online backup from a replica if that replica
did a TL switch during the backup, to indicate that the backup isn't
valid.

Attached is an initial draft of a patch to at least give a somewhat
clearer error message when we detect that the user has asked us to
follow a timeline switch to a new timeline before we've reached
consistency (though I had to hack in a check to see if pg_rewind is
being used, since apparently it actually depends on PG following a
timeline switch before reaching consistency...).

Thoughts?

Thanks,

Stephen
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
new file mode 100644
index fd93bcf..5dd777f
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
*************** static void SetCurrentChunkStartTime(Tim
*** 896,902 ****
  static void CheckRequiredParameterValues(void);
  static void XLogReportParameters(void);
  static void checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI,
! 								TimeLineID prevTLI);
  static void LocalSetXLogInsertAllowed(void);
  static void CreateEndOfRecoveryRecord(void);
  static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
--- 896,902 ----
  static void CheckRequiredParameterValues(void);
  static void XLogReportParameters(void);
  static void checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI,
! 								TimeLineID prevTLI, bool pgrewind_replay);
  static void LocalSetXLogInsertAllowed(void);
  static void CreateEndOfRecoveryRecord(void);
  static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
*************** static void xlog_outdesc(StringInfo buf,
*** 946,952 ****
  static void pg_start_backup_callback(int code, Datum arg);
  static void pg_stop_backup_callback(int code, Datum arg);
  static bool read_backup_label(XLogRecPtr *checkPointLoc,
! 							  bool *backupEndRequired, bool *backupFromStandby);
  static bool read_tablespace_map(List **tablespaces);
  
  static void rm_redo_error_callback(void *arg);
--- 946,952 ----
  static void pg_start_backup_callback(int code, Datum arg);
  static void pg_stop_backup_callback(int code, Datum arg);
  static bool read_backup_label(XLogRecPtr *checkPointLoc,
! 							  bool *backupEndRequired, bool *backupFromStandby, bool *pgrewind_replay);
  static bool read_tablespace_map(List **tablespaces);
  
  static void rm_redo_error_callback(void *arg);
*************** StartupXLOG(void)
*** 6319,6324 ****
--- 6319,6325 ----
  	TransactionId oldestActiveXID;
  	bool		backupEndRequired = false;
  	bool		backupFromStandby = false;
+ 	bool		pgrewind_replay = false;
  	DBState		dbstate_at_startup;
  	XLogReaderState *xlogreader;
  	XLogPageReadPrivate private;
*************** StartupXLOG(void)
*** 6506,6512 ****
  	master_image_masked = (char *) palloc(BLCKSZ);
  
  	if (read_backup_label(&checkPointLoc, &backupEndRequired,
! 						  &backupFromStandby))
  	{
  		List	   *tablespaces = NIL;
  
--- 6507,6513 ----
  	master_image_masked = (char *) palloc(BLCKSZ);
  
  	if (read_backup_label(&checkPointLoc, &backupEndRequired,
! 						  &backupFromStandby, &pgrewind_replay))
  	{
  		List	   *tablespaces = NIL;
  
*************** StartupXLOG(void)
*** 7297,7303 ****
  					if (newTLI != ThisTimeLineID)
  					{
  						/* Check that it's OK to switch to this TLI */
! 						checkTimeLineSwitch(EndRecPtr, newTLI, prevTLI);
  
  						/* Following WAL records should be run with new TLI */
  						ThisTimeLineID = newTLI;
--- 7298,7304 ----
  					if (newTLI != ThisTimeLineID)
  					{
  						/* Check that it's OK to switch to this TLI */
! 						checkTimeLineSwitch(EndRecPtr, newTLI, prevTLI, pgrewind_replay);
  
  						/* Following WAL records should be run with new TLI */
  						ThisTimeLineID = newTLI;
*************** UpdateFullPageWrites(void)
*** 9841,9847 ****
   * replay. (Currently, timeline can only change at a shutdown checkpoint).
   */
  static void
! checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, TimeLineID prevTLI)
  {
  	/* Check that the record agrees on what the current (old) timeline is */
  	if (prevTLI != ThisTimeLineID)
--- 9842,9848 ----
   * replay. (Currently, timeline can only change at a shutdown checkpoint).
   */
  static void
! checkTimeLineSwitch(XLogRecPtr lsn, TimeLineID newTLI, TimeLineID prevTLI, bool pgrewind_replay)
  {
  	/* Check that the record agrees on what the current (old) timeline is */
  	if (prevTLI != ThisTimeLineID)
*************** checkTimeLineSwitch(XLogRecPtr lsn, Time
*** 9877,9882 ****
--- 9878,9894 ----
  						(uint32) minRecoveryPoint,
  						minRecoveryPointTLI)));
  
+ 	/*
+ 	 * If we have not yet found the end of a backup that had been performed
+ 	 * then switching to a new timeline is similairly trouble, as there's
+ 	 * zero chance we'll find the end-of-backup WAL record on the new timeline.
+ 	 */
+ 	if (InArchiveRecovery && !reachedConsistency && !pgrewind_replay)
+ 		ereport(PANIC,
+ 				(errmsg("unexpected timeline ID %u in checkpoint record, before finding end-of-backup WAL record on timeline %u",
+ 						newTLI,
+ 						ThisTimeLineID)));
+ 
  	/* Looks good */
  }
  
*************** GetOldestRestartPoint(XLogRecPtr *oldrec
*** 11541,11547 ****
   */
  static bool
  read_backup_label(XLogRecPtr *checkPointLoc, bool *backupEndRequired,
! 				  bool *backupFromStandby)
  {
  	char		startxlogfilename[MAXFNAMELEN];
  	TimeLineID	tli_from_walseg,
--- 11553,11559 ----
   */
  static bool
  read_backup_label(XLogRecPtr *checkPointLoc, bool *backupEndRequired,
! 				  bool *backupFromStandby, bool *pgrewind_replay)
  {
  	char		startxlogfilename[MAXFNAMELEN];
  	TimeLineID	tli_from_walseg,
*************** read_backup_label(XLogRecPtr *checkPoint
*** 11599,11604 ****
--- 11611,11618 ----
  	{
  		if (strcmp(backuptype, "streamed") == 0)
  			*backupEndRequired = true;
+ 		if (strcmp(backuptype, "pg_rewind") == 0)
+ 			*pgrewind_replay = true;
  	}
  
  	if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1)

Attachment: signature.asc
Description: PGP signature

Reply via email to