From ce7e1eec97dbe7b1648b4f56a1f9825eeba7ebed Mon Sep 17 00:00:00 2001
From: Michael Paquier <mpaquier@vmware.com>
Date: Mon, 20 Oct 2014 14:40:37 +0900
Subject: [PATCH] Mark segment file .done for last WAL position at stream start

When stream connection between a standby and its root node is unstable,
WAL stream errors make this standby node fail with FATAL errors because
of the WAL receiver, forcing it to restart in crash-recovery mode. Now,
when the crash occurs exactly when a switch to a new segment file is
done, it is possible that the WAL receiver restarts from a position
located on the next segment file, while the previous file is let as is,
marked ultimately in .ready state once old WAL files are recycled or
removed. Note that this file should have been marked as .done by the
WAL receiver itself.

With this patch, WAL receiver checks for the presence of the previous
segment file of start position if this WAL position is the last one of
the previous segment file and enforces it to .done, preventing .ready
files from being accumulated on standbys.

This failure can be easily reproducible with the following command:
psql -c 'select pg_switch_xlog()'; pg_ctl restart -m immediate
---
 src/backend/access/transam/xlogarchive.c | 26 ++++++++++++++++++++++++++
 src/backend/replication/walreceiver.c    | 12 ++++++++++++
 src/include/access/xlog_internal.h       |  1 +
 3 files changed, 39 insertions(+)

diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c
index 047efa2..25a153f 100644
--- a/src/backend/access/transam/xlogarchive.c
+++ b/src/backend/access/transam/xlogarchive.c
@@ -553,6 +553,32 @@ XLogArchiveNotifySeg(XLogSegNo segno)
 	XLogArchiveNotify(xlog);
 }
 
+
+/*
+ * XLogArchiveForceDoneIfPresent
+ *
+ * Wrapper of XLogArchiveForceDone, used conditionally based on the presence
+ * of given XLOG segment file.
+ */
+void
+XLogArchiveForceDoneIfPresent(TimeLineID tli, XLogSegNo segno)
+{
+	struct stat	stat_buf;
+	char		xlogfname[MAXFNAMELEN];
+	char		xlogfpath[MAXPGPATH];
+
+	XLogFilePath(xlogfpath, tli, segno);
+
+	/* File is missing, nothing to do */
+	if (stat(xlogfpath, &stat_buf) != 0)
+		return;
+
+	/* All is fine, process... */
+	XLogFileName(xlogfname, tli, segno);
+	XLogArchiveForceDone(xlogfname);
+}
+
+
 /*
  * XLogArchiveForceDone
  *
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index c2d4ed3..6857a05 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -361,6 +361,7 @@ WalReceiverMain(void)
 								  slotname[0] != '\0' ? slotname : NULL))
 		{
 			bool		endofwal = false;
+			XLogSegNo	startPointSegNo;
 
 			if (first_stream)
 				ereport(LOG,
@@ -383,6 +384,17 @@ WalReceiverMain(void)
 			last_recv_timestamp = GetCurrentTimestamp();
 			ping_sent = false;
 
+			/*
+			 * Check if current start point is located exactly at the end of
+			 * a segment file and mark this file as already archived. It is
+			 * possible that if WAL stream connection has been abruptly cut
+			 * exactly during a segment file switch that this file is still
+			 * present but not marked as .done even if it should be.
+			 */
+			XLByteToPrevSeg(startpoint, startPointSegNo);
+			if (!XLByteInSeg(startpoint, startPointSegNo))
+				XLogArchiveForceDoneIfPresent(startpointTLI, startPointSegNo);
+
 			/* Loop until end-of-streaming or error */
 			while (!endofwal)
 			{
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 27b9899..3b1a99e 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -286,6 +286,7 @@ extern void ExecuteRecoveryCommand(char *command, char *commandName,
 extern void KeepFileRestoredFromArchive(char *path, char *xlogfname);
 extern void XLogArchiveNotify(const char *xlog);
 extern void XLogArchiveNotifySeg(XLogSegNo segno);
+extern void XLogArchiveForceDoneIfPresent(TimeLineID tli, XLogSegNo segno);
 extern void XLogArchiveForceDone(const char *xlog);
 extern bool XLogArchiveCheckDone(const char *xlog);
 extern bool XLogArchiveIsBusy(const char *xlog);
-- 
2.1.2

