@@ -214,6 +214,8 @@ static bool recoveryStopAfter;
214
214
*
215
215
* recoveryTargetTLI: the desired timeline that we want to end in.
216
216
*
217
+ * recoveryTargetIsLatest: was the requested target timeline 'latest'?
218
+ *
217
219
* expectedTLIs: an integer list of recoveryTargetTLI and the TLIs of
218
220
* its known parents, newest first (so recoveryTargetTLI is always the
219
221
* first list member). Only these TLIs are expected to be seen in the WAL
@@ -227,6 +229,7 @@ static bool recoveryStopAfter;
227
229
* to decrease.
228
230
*/
229
231
static TimeLineID recoveryTargetTLI ;
232
+ static bool recoveryTargetIsLatest = false;
230
233
static List * expectedTLIs ;
231
234
static TimeLineID curFileTLI ;
232
235
@@ -637,6 +640,7 @@ static bool ValidXLOGHeader(XLogPageHeader hdr, int emode);
637
640
static XLogRecord * ReadCheckpointRecord (XLogRecPtr RecPtr , int whichChkpt );
638
641
static List * readTimeLineHistory (TimeLineID targetTLI );
639
642
static bool existsTimeLineHistory (TimeLineID probeTLI );
643
+ static bool rescanLatestTimeLine (void );
640
644
static TimeLineID findNewestTimeLine (TimeLineID startTLI );
641
645
static void writeTimeLineHistory (TimeLineID newTLI , TimeLineID parentTLI ,
642
646
TimeLineID endTLI ,
@@ -4253,6 +4257,62 @@ existsTimeLineHistory(TimeLineID probeTLI)
4253
4257
}
4254
4258
}
4255
4259
4260
+ /*
4261
+ * Scan for new timelines that might have appeared in the archive since we
4262
+ * started recovery.
4263
+ *
4264
+ * If there are any, the function changes recovery target TLI to the latest
4265
+ * one and returns 'true'.
4266
+ */
4267
+ static bool
4268
+ rescanLatestTimeLine (void )
4269
+ {
4270
+ TimeLineID newtarget ;
4271
+ newtarget = findNewestTimeLine (recoveryTargetTLI );
4272
+ if (newtarget != recoveryTargetTLI )
4273
+ {
4274
+ /*
4275
+ * Determine the list of expected TLIs for the new TLI
4276
+ */
4277
+ List * newExpectedTLIs ;
4278
+ newExpectedTLIs = readTimeLineHistory (newtarget );
4279
+
4280
+ /*
4281
+ * If the current timeline is not part of the history of the
4282
+ * new timeline, we cannot proceed to it.
4283
+ *
4284
+ * XXX This isn't foolproof: The new timeline might have forked from
4285
+ * the current one, but before the current recovery location. In that
4286
+ * case we will still switch to the new timeline and proceed replaying
4287
+ * from it even though the history doesn't match what we already
4288
+ * replayed. That's not good. We will likely notice at the next online
4289
+ * checkpoint, as the TLI won't match what we expected, but it's
4290
+ * not guaranteed. The admin needs to make sure that doesn't happen.
4291
+ */
4292
+ if (!list_member_int (newExpectedTLIs ,
4293
+ (int ) recoveryTargetTLI ))
4294
+ ereport (LOG ,
4295
+ (errmsg ("new timeline %u is not a child of database system timeline %u" ,
4296
+ newtarget ,
4297
+ ThisTimeLineID )));
4298
+ else
4299
+ {
4300
+ /* Switch target */
4301
+ recoveryTargetTLI = newtarget ;
4302
+ list_free (expectedTLIs );
4303
+ expectedTLIs = newExpectedTLIs ;
4304
+
4305
+ XLogCtl -> RecoveryTargetTLI = recoveryTargetTLI ;
4306
+
4307
+ ereport (LOG ,
4308
+ (errmsg ("new target timeline is %u" ,
4309
+ recoveryTargetTLI )));
4310
+ return true;
4311
+ }
4312
+ }
4313
+ return false;
4314
+ }
4315
+
4256
4316
/*
4257
4317
* Find the newest existing timeline, assuming that startTLI exists.
4258
4318
*
@@ -5327,11 +5387,13 @@ readRecoveryCommandFile(void)
5327
5387
(errmsg ("recovery target timeline %u does not exist" ,
5328
5388
rtli )));
5329
5389
recoveryTargetTLI = rtli ;
5390
+ recoveryTargetIsLatest = false;
5330
5391
}
5331
5392
else
5332
5393
{
5333
5394
/* We start the "latest" search from pg_control's timeline */
5334
5395
recoveryTargetTLI = findNewestTimeLine (recoveryTargetTLI );
5396
+ recoveryTargetIsLatest = true;
5335
5397
}
5336
5398
}
5337
5399
@@ -10032,13 +10094,24 @@ XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt,
10032
10094
{
10033
10095
/*
10034
10096
* We've exhausted all options for retrieving the
10035
- * file. Retry .. .
10097
+ * file. Retry.
10036
10098
*/
10037
10099
failedSources = 0 ;
10038
10100
10039
10101
/*
10040
- * ... but sleep first if it hasn't been long since
10041
- * last attempt.
10102
+ * Before we sleep, re-scan for possible new timelines
10103
+ * if we were requested to recover to the latest
10104
+ * timeline.
10105
+ */
10106
+ if (recoveryTargetIsLatest )
10107
+ {
10108
+ if (rescanLatestTimeLine ())
10109
+ continue ;
10110
+ }
10111
+
10112
+ /*
10113
+ * If it hasn't been long since last attempt, sleep
10114
+ * to avoid busy-waiting.
10042
10115
*/
10043
10116
now = (pg_time_t ) time (NULL );
10044
10117
if ((now - last_fail_time ) < 5 )
0 commit comments