aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam/xlog.c
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2024-07-26 14:50:21 -0400
committerRobert Haas <rhaas@postgresql.org>2024-07-26 15:00:48 -0400
commit8a53539bd603e5fe8fa52bdbb7277f6f49724522 (patch)
tree6142cb65a5945746a2b3b2dbee714e7985d63ede /src/backend/access/transam/xlog.c
parent454aab4b738e53a5dbfca9251a7807a2ad21f87e (diff)
downloadpostgresql-8a53539bd603e5fe8fa52bdbb7277f6f49724522.tar.gz
postgresql-8a53539bd603e5fe8fa52bdbb7277f6f49724522.zip
Wait for WAL summarization to catch up before creating .partial file.
When a standby is promoted, CleanupAfterArchiveRecovery() may decide to rename the final WAL file from the old timeline by adding ".partial" to the name. If WAL summarization is enabled and this file is renamed before its partial contents are summarized, WAL summarization breaks: the summarizer gets stuck at that point in the WAL stream and just errors out. To fix that, first make the startup process wait for WAL summarization to catch up before renaming the file. Generally, this should be quick, and if it's not, the user can shut off summarize_wal and try again. To make this fix work, also teach the WAL summarizer that after a promotion has occurred, no more WAL can appear on the previous timeline: previously, the WAL summarizer wouldn't switch to the new timeline until we actually started writing WAL there, but that meant that when the startup process was waiting for the WAL summarizer, it was waiting for an action that the summarizer wasn't yet prepared to take. In the process of fixing these bugs, I realized that the logic to wait for WAL summarization to catch up was spread out in a way that made it difficult to reuse properly, so this code refactors things to make it easier. Finally, add a test case that would have caught this bug and the previously-fixed bug that WAL summarization sometimes needs to back up when the timeline changes. Discussion: https://postgr.es/m/CA+TgmoZGEsZodXC4f=XZNkAeyuDmWTSkpkjCEOcF19Am0mt_OA@mail.gmail.com
Diffstat (limited to 'src/backend/access/transam/xlog.c')
-rw-r--r--src/backend/access/transam/xlog.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 473a9c5c2f8..f86f4b5c4b7 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -498,6 +498,11 @@ typedef struct XLogCtlData
* If we create a new timeline when the system was started up,
* PrevTimeLineID is the old timeline's ID that we forked off from.
* Otherwise it's equal to InsertTimeLineID.
+ *
+ * We set these fields while holding info_lck. Most that reads these
+ * values knows that recovery is no longer in progress and so can safely
+ * read the value without a lock, but code that could be run either during
+ * or after recovery can take info_lck while reading these values.
*/
TimeLineID InsertTimeLineID;
TimeLineID PrevTimeLineID;
@@ -5315,6 +5320,13 @@ CleanupAfterArchiveRecovery(TimeLineID EndOfLogTLI, XLogRecPtr EndOfLog,
char partialfname[MAXFNAMELEN];
char partialpath[MAXPGPATH];
+ /*
+ * If we're summarizing WAL, we can't rename the partial file
+ * until the summarizer finishes with it, else it will fail.
+ */
+ if (summarize_wal)
+ WaitForWalSummarization(EndOfLog);
+
XLogFilePath(origpath, EndOfLogTLI, endLogSegNo, wal_segment_size);
snprintf(partialfname, MAXFNAMELEN, "%s.partial", origfname);
snprintf(partialpath, MAXPGPATH, "%s.partial", origpath);
@@ -5945,8 +5957,10 @@ StartupXLOG(void)
}
/* Save the selected TimeLineID in shared memory, too */
+ SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->InsertTimeLineID = newTLI;
XLogCtl->PrevTimeLineID = endOfRecoveryInfo->lastRecTLI;
+ SpinLockRelease(&XLogCtl->info_lck);
/*
* Actually, if WAL ended in an incomplete record, skip the parts that
@@ -6482,6 +6496,25 @@ GetWALInsertionTimeLine(void)
}
/*
+ * GetWALInsertionTimeLineIfSet -- If the system is not in recovery, returns
+ * the WAL insertion timeline; else, returns 0. Wherever possible, use
+ * GetWALInsertionTimeLine() instead, since it's cheaper. Note that this
+ * function decides recovery has ended as soon as the insert TLI is set, which
+ * happens before we set XLogCtl->SharedRecoveryState to RECOVERY_STATE_DONE.
+ */
+TimeLineID
+GetWALInsertionTimeLineIfSet(void)
+{
+ TimeLineID insertTLI;
+
+ SpinLockAcquire(&XLogCtl->info_lck);
+ insertTLI = XLogCtl->InsertTimeLineID;
+ SpinLockRelease(&XLogCtl->info_lck);
+
+ return insertTLI;
+}
+
+/*
* GetLastImportantRecPtr -- Returns the LSN of the last important record
* inserted. All records not explicitly marked as unimportant are considered
* important.