pg_basebackup_rep_slot.v4.patch

application/octet-stream

Filename: pg_basebackup_rep_slot.v4.patch
Type: application/octet-stream
Part: 0
Message: pg_basebackup behavior on non-existent slot

Patch

Format: unified
Series: patch v4
File+
src/bin/pg_basebackup/pg_basebackup.c 136 84
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 669aa207a3..4cf875de45 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -173,6 +173,7 @@ static int	bgpipe[2] = {-1, -1};
 
 /* Handle to child process */
 static pid_t bgchild = -1;
+static bool bgchild_exited = false;
 static bool in_log_streamer = false;
 
 /* End position for xlog streaming, empty string if unknown yet */
@@ -211,6 +212,7 @@ static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
 
 static const char *get_tablespace_mapping(const char *dir);
 static void tablespace_list_append(const char *arg);
+static void WaitForBGChild(const char *xlogend);
 
 
 static void
@@ -967,6 +969,22 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
 		int			r;
 		char	   *copybuf;
 
+		if (bgchild > 0)
+		{
+			/* Check to see if our background process is still running fine. */
+			int			status;
+			pid_t		p;
+
+			p = waitpid(bgchild, &status, WNOHANG);
+
+			/* if our child process exited unexpectedly, we stop */
+			if (WIFEXITED(p))
+			{
+				bgchild_exited = true;
+				break;
+			}
+		}
+
 		r = PQgetCopyData(conn, &copybuf, 0);
 		if (r == -1)
 		{
@@ -1167,6 +1185,11 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
 
 	ReceiveCopyData(conn, ReceiveTarCopyChunk, &state);
 
+	if (bgchild_exited)
+	{
+		return;
+	}
+
 	/*
 	 * End of copy data. If requested, and this is the base tablespace, write
 	 * configuration file into the tarfile. When done, close the file (but not
@@ -1531,6 +1554,11 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
 	if (state.file)
 		fclose(state.file);
 
+	if (bgchild_exited)
+	{
+		return;
+	}
+
 	progress_report(rownum, state.filename, true, false);
 
 	if (state.file != NULL)
@@ -1796,6 +1824,95 @@ ReceiveBackupManifestInMemoryChunk(size_t r, char *copybuf,
 	appendPQExpBuffer(buf, copybuf, r);
 }
 
+static void
+WaitForBGChild(const char *xlogend)
+{
+	if (bgchild > 0)
+	{
+#ifndef WIN32
+		int			status;
+		pid_t		r;
+#else
+		DWORD		status;
+
+		/*
+		 * get a pointer sized version of bgchild to avoid warnings about
+		 * casting to a different size on WIN64.
+		 */
+		intptr_t	bgchild_handle = bgchild;
+		uint32		hi,
+					lo;
+#endif
+
+		if (verbose)
+			pg_log_info("waiting for background process to finish streaming ...");
+
+#ifndef WIN32
+		if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
+		{
+			pg_log_info("could not send command to background pipe: %m");
+			exit(1);
+		}
+
+		/* Just wait for the background process to exit */
+		r = waitpid(bgchild, &status, 0);
+		if (r == (pid_t) -1)
+		{
+			pg_log_error("could not wait for child process: %m");
+			exit(1);
+		}
+		if (r != bgchild)
+		{
+			pg_log_error("child %d died, expected %d", (int) r, (int) bgchild);
+			exit(1);
+		}
+		if (status != 0)
+		{
+			pg_log_error("%s", wait_result_to_str(status));
+			exit(1);
+		}
+		/* Exited normally, we're happy! */
+#else							/* WIN32 */
+
+		/*
+		 * On Windows, since we are in the same process, we can just store the
+		 * value directly in the variable, and then set the flag that says
+		 * it's there.
+		 */
+		if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
+		{
+			pg_log_error("could not parse write-ahead log location \"%s\"",
+						 xlogend);
+			exit(1);
+		}
+		xlogendptr = ((uint64) hi) << 32 | lo;
+		InterlockedIncrement(&has_xlogendptr);
+
+		/* First wait for the thread to exit */
+		if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) !=
+			WAIT_OBJECT_0)
+		{
+			_dosmaperr(GetLastError());
+			pg_log_error("could not wait for child thread: %m");
+			exit(1);
+		}
+		if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
+		{
+			_dosmaperr(GetLastError());
+			pg_log_error("could not get child thread exit status: %m");
+			exit(1);
+		}
+		if (status != 0)
+		{
+			pg_log_error("child thread exited with error %u",
+						 (unsigned int) status);
+			exit(1);
+		}
+		/* Exited normally, we're happy */
+#endif
+	}
+}
+
 static void
 BaseBackup(void)
 {
@@ -2020,8 +2137,26 @@ BaseBackup(void)
 			ReceiveTarFile(conn, res, i);
 		else
 			ReceiveAndUnpackTarFile(conn, res, i);
+
+		if (bgchild_exited)
+			break;
 	}							/* Loop over all tablespaces */
 
+
+	/* if bgchild exited early, we need to stop now. */
+	if (bgchild_exited)
+	{
+		PQclear(res);
+		PQfinish(conn);
+		conn = NULL;
+
+		destroyPQExpBuffer(recoveryconfcontents);
+
+		/* wait for the child process to be finished entirely */
+		(void) WaitForBGChild(xlogend);
+		exit(1);
+	}
+
 	/*
 	 * Now receive backup manifest, if appropriate.
 	 *
@@ -2079,90 +2214,7 @@ BaseBackup(void)
 		exit(1);
 	}
 
-	if (bgchild > 0)
-	{
-#ifndef WIN32
-		int			status;
-		pid_t		r;
-#else
-		DWORD		status;
-
-		/*
-		 * get a pointer sized version of bgchild to avoid warnings about
-		 * casting to a different size on WIN64.
-		 */
-		intptr_t	bgchild_handle = bgchild;
-		uint32		hi,
-					lo;
-#endif
-
-		if (verbose)
-			pg_log_info("waiting for background process to finish streaming ...");
-
-#ifndef WIN32
-		if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
-		{
-			pg_log_info("could not send command to background pipe: %m");
-			exit(1);
-		}
-
-		/* Just wait for the background process to exit */
-		r = waitpid(bgchild, &status, 0);
-		if (r == (pid_t) -1)
-		{
-			pg_log_error("could not wait for child process: %m");
-			exit(1);
-		}
-		if (r != bgchild)
-		{
-			pg_log_error("child %d died, expected %d", (int) r, (int) bgchild);
-			exit(1);
-		}
-		if (status != 0)
-		{
-			pg_log_error("%s", wait_result_to_str(status));
-			exit(1);
-		}
-		/* Exited normally, we're happy! */
-#else							/* WIN32 */
-
-		/*
-		 * On Windows, since we are in the same process, we can just store the
-		 * value directly in the variable, and then set the flag that says
-		 * it's there.
-		 */
-		if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-		{
-			pg_log_error("could not parse write-ahead log location \"%s\"",
-						 xlogend);
-			exit(1);
-		}
-		xlogendptr = ((uint64) hi) << 32 | lo;
-		InterlockedIncrement(&has_xlogendptr);
-
-		/* First wait for the thread to exit */
-		if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) !=
-			WAIT_OBJECT_0)
-		{
-			_dosmaperr(GetLastError());
-			pg_log_error("could not wait for child thread: %m");
-			exit(1);
-		}
-		if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
-		{
-			_dosmaperr(GetLastError());
-			pg_log_error("could not get child thread exit status: %m");
-			exit(1);
-		}
-		if (status != 0)
-		{
-			pg_log_error("child thread exited with error %u",
-						 (unsigned int) status);
-			exit(1);
-		}
-		/* Exited normally, we're happy */
-#endif
-	}
+	(void) WaitForBGChild(xlogend);
 
 	/* Free the configuration file contents */
 	destroyPQExpBuffer(recoveryconfcontents);