fast_failover.v1.patch
application/octet-stream
Filename: fast_failover.v1.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v1
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 125 | 42 |
| src/include/catalog/pg_control.h | 11 | 0 |
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0d494e2..eb28848 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -561,8 +561,8 @@ static bool reachedMinRecoveryPoint = false;
static bool InRedo = false;
-/* Have we launched bgwriter during recovery? */
-static bool bgwriterLaunched = false;
+/* Have we launched checkpointer process during recovery? */
+static bool checkpointerLaunched = false;
/*
* Information logged when we detect a change in one of the parameters
@@ -597,6 +597,8 @@ static void recoveryPausesHere(void);
static void SetLatestXTime(TimestampTz xtime);
static void CheckRequiredParameterValues(void);
static void XLogReportParameters(void);
+static void LogEndOfRecovery(void);
+static void StartHotStandbyImmediately(TransactionId nextXid);
static void LocalSetXLogInsertAllowed(void);
static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
static void KeepLogSeg(XLogRecPtr recptr, uint32 *logId, uint32 *logSeg);
@@ -6382,7 +6384,7 @@ StartupXLOG(void)
PublishStartupProcessInformation();
SetForwardFsyncRequests();
SendPostmasterSignal(PMSIGNAL_RECOVERY_STARTED);
- bgwriterLaunched = true;
+ checkpointerLaunched = true;
}
/*
@@ -6744,17 +6746,19 @@ StartupXLOG(void)
/*
* Perform a checkpoint to update all our recovery activity to disk.
+ * If we have a chckpoint process, we perform the checkpoint later.
*
- * Note that we write a shutdown checkpoint rather than an on-line
- * one. This is not particularly critical, but since we may be
- * assigning a new TLI, using a shutdown checkpoint allows us to have
- * the rule that TLI only changes in shutdown checkpoints, which
- * allows some extra error checking in xlog_redo.
+ * Note that we write a shutdown checkpoint rather than an on-line one.
+ *
+ * We mark end of recovery here, either by a end-of-recovery checkpoint
+ * or by and end-of-recovery WAL record. Either way, this is the only
+ * place the TLI of WAL files is allowed to change.
+ *
+ * We are able to do this because we know that all WAL files are
+ * available locally. XXX perhaps we should actually check that...
*/
- if (bgwriterLaunched)
- RequestCheckpoint(CHECKPOINT_END_OF_RECOVERY |
- CHECKPOINT_IMMEDIATE |
- CHECKPOINT_WAIT);
+ if (checkpointerLaunched)
+ LogEndOfRecovery();
else
CreateCheckPoint(CHECKPOINT_END_OF_RECOVERY | CHECKPOINT_IMMEDIATE);
@@ -6867,6 +6871,19 @@ StartupXLOG(void)
xlogctl->SharedRecoveryInProgress = false;
SpinLockRelease(&xlogctl->info_lck);
}
+
+ /*
+ * If we skipped a shutdown checkpoint earlier, request a checkpoint now.
+ * This is a normal checkpoint now, not a restartpoint or end-of-recovery
+ * checkpoint. This is safe only because we know we have all required
+ * WAL files in the pg_xlog directory if we crash before writing the
+ * final part of this checkpoint. Checkpoint here is immediate, so that we
+ * minimise the time before the next checkpoint ends. Yet we don't wait for
+ * completion of the checkpoint, since that slows down getting normal
+ * users connected in case of a failover.
+ */
+ if (checkpointerLaunched)
+ RequestCheckpoint(CHECKPOINT_IMMEDIATE);
}
/*
@@ -8285,6 +8302,57 @@ XLogReportParameters(void)
}
}
+static void
+LogEndOfRecovery(void)
+{
+ XLogRecData rdata;
+ EndOfRecovery end;
+
+ end.nextXid = ControlFile->checkPointCopy.nextXid;
+ end.nextXidEpoch = ControlFile->checkPointCopy.nextXidEpoch;
+ end.ThisTimeLineID = ThisTimeLineID;
+ end.time = (pg_time_t) time(NULL);
+
+ rdata.buffer = InvalidBuffer;
+ rdata.data = (char *) &end;
+ rdata.len = sizeof(EndOfRecovery);
+ rdata.next = NULL;
+
+ XLogInsert(RM_XLOG_ID, XLOG_END_OF_RECOVERY, &rdata);
+}
+
+static void
+StartHotStandbyImmediately(TransactionId nextXid)
+{
+ TransactionId *xids;
+ int nxids;
+ TransactionId oldestActiveXID;
+ TransactionId latestCompletedXid;
+ RunningTransactionsData running;
+
+ oldestActiveXID = PrescanPreparedTransactions(&xids, &nxids);
+
+ /*
+ * Construct a RunningTransactions snapshot representing a shut
+ * down server, with only prepared transactions still alive. We're
+ * never overflowed at this point because all subxids are listed
+ * with their parent prepared transactions.
+ */
+ running.xcnt = nxids;
+ running.subxid_overflow = false;
+ running.nextXid = nextXid;
+ running.oldestRunningXid = oldestActiveXID;
+ latestCompletedXid = nextXid;
+ TransactionIdRetreat(latestCompletedXid);
+ Assert(TransactionIdIsNormal(latestCompletedXid));
+ running.latestCompletedXid = latestCompletedXid;
+ running.xids = xids;
+
+ ProcArrayApplyRecoveryInfo(&running);
+
+ StandbyRecoverPreparedTransactions(true);
+}
+
/*
* XLOG resource manager's routines
*
@@ -8310,6 +8378,40 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
ShmemVariableCache->oidCount = 0;
}
}
+ else if (info == XLOG_END_OF_RECOVERY)
+ {
+ EndOfRecovery end;
+
+ memcpy(&end, XLogRecGetData(record), sizeof(EndOfRecovery));
+
+ /*
+ * If we see an end of recovery record, we know that no write transactions
+ * are active that will commit. So fake-up an empty running-xacts
+ * record and use that here and now. Recover additional standby state
+ * for prepared transactions.
+ */
+ if (standbyState >= STANDBY_INITIALIZED)
+ StartHotStandbyImmediately(end.nextXid);
+
+ /* ControlFile->checkPointCopy always tracks the latest ckpt XID */
+ ControlFile->checkPointCopy.nextXidEpoch = end.nextXidEpoch;
+ ControlFile->checkPointCopy.nextXid = end.nextXid;
+
+ /*
+ * TLI may change in an end of recovery, but it shouldn't decrease
+ */
+ if (end.ThisTimeLineID != ThisTimeLineID)
+ {
+ if (end.ThisTimeLineID < ThisTimeLineID ||
+ !list_member_int(expectedTLIs,
+ (int) end.ThisTimeLineID))
+ ereport(PANIC,
+ (errmsg("unexpected timeline ID %u (after %u) in end of recovery record",
+ end.ThisTimeLineID, ThisTimeLineID)));
+ /* Following WAL records should be run with new TLI */
+ ThisTimeLineID = end.ThisTimeLineID;
+ }
+ }
else if (info == XLOG_CHECKPOINT_SHUTDOWN)
{
CheckPoint checkPoint;
@@ -8340,35 +8442,7 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
* for prepared transactions.
*/
if (standbyState >= STANDBY_INITIALIZED)
- {
- TransactionId *xids;
- int nxids;
- TransactionId oldestActiveXID;
- TransactionId latestCompletedXid;
- RunningTransactionsData running;
-
- oldestActiveXID = PrescanPreparedTransactions(&xids, &nxids);
-
- /*
- * Construct a RunningTransactions snapshot representing a shut
- * down server, with only prepared transactions still alive. We're
- * never overflowed at this point because all subxids are listed
- * with their parent prepared transactions.
- */
- running.xcnt = nxids;
- running.subxid_overflow = false;
- running.nextXid = checkPoint.nextXid;
- running.oldestRunningXid = oldestActiveXID;
- latestCompletedXid = checkPoint.nextXid;
- TransactionIdRetreat(latestCompletedXid);
- Assert(TransactionIdIsNormal(latestCompletedXid));
- running.latestCompletedXid = latestCompletedXid;
- running.xids = xids;
-
- ProcArrayApplyRecoveryInfo(&running);
-
- StandbyRecoverPreparedTransactions(true);
- }
+ StartHotStandbyImmediately(checkPoint.nextXid);
/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch;
@@ -8524,6 +8598,15 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
checkpoint->oldestActiveXid,
(info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
}
+ else if (info == XLOG_END_OF_RECOVERY)
+ {
+ EndOfRecovery *end = (EndOfRecovery *) rec;
+
+ appendStringInfo(buf, "end of recovery: tli %u; xid %u/%u",
+ end->ThisTimeLineID,
+ end->nextXidEpoch,
+ end->nextXid);
+ }
else if (info == XLOG_NOOP)
{
appendStringInfo(buf, "xlog no-op");
@@ -9563,7 +9646,7 @@ XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt,
* Request a restartpoint if we've replayed too much
* xlog since the last one.
*/
- if (StandbyMode && bgwriterLaunched)
+ if (StandbyMode && checkpointerLaunched)
{
if (XLogCheckpointNeeded(readId, readSeg))
{
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 6688c19..baa7fc2 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -60,7 +60,18 @@ typedef struct CheckPoint
#define XLOG_BACKUP_END 0x50
#define XLOG_PARAMETER_CHANGE 0x60
#define XLOG_RESTORE_POINT 0x70
+#define XLOG_END_OF_RECOVERY 0x80
+/*
+ * Body of EndOfRecovery XLOG records - only some of the fields of a checkpoint.
+ */
+typedef struct EndOfRecovery
+{
+ TimeLineID ThisTimeLineID; /* current TLI */
+ uint32 nextXidEpoch; /* higher-order bits of nextXid */
+ TransactionId nextXid; /* next free XID */
+ pg_time_t time; /* time stamp of checkpoint */
+} EndOfRecovery;
/*
* System status indicator. Note this is stored in pg_control; if you change