v1-0001-Restrict-pg_subscriber-to-standby-node.patch

application/octet-stream

Filename: v1-0001-Restrict-pg_subscriber-to-standby-node.patch
Type: application/octet-stream
Part: 0
Message: Re: speed up a logical replica setup

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: format-patch
Series: patch v1-0001
Subject: Restrict pg_subscriber to standby node
File+
src/bin/pg_basebackup/pg_subscriber.c 57 2
From 90c03545c09d29e9daf64e9151047bdd2a93348e Mon Sep 17 00:00:00 2001
From: Shlok Kyal <shlok.kyal.oss@gmail.com>
Date: Tue, 9 Jan 2024 20:53:47 +0530
Subject: [PATCH v1] Restrict pg_subscriber to standby node

Earlier pg_subscriber can run on normal backup cluster and the command gets
stuck. With this patch we are restricting pg_subscriber to run only for
standby server. Also added a timeout of 60 seconds so that the process ends
if it get stuck.
---
 src/bin/pg_basebackup/pg_subscriber.c | 59 ++++++++++++++++++++++++++-
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/bin/pg_basebackup/pg_subscriber.c b/src/bin/pg_basebackup/pg_subscriber.c
index b96ce26ed7..25ef10b0e7 100644
--- a/src/bin/pg_basebackup/pg_subscriber.c
+++ b/src/bin/pg_basebackup/pg_subscriber.c
@@ -72,9 +72,13 @@ static void drop_subscription(PGconn *conn, LogicalRepInfo *dbinfo);
 static void set_replication_progress(PGconn *conn, LogicalRepInfo *dbinfo, const char *lsn);
 static void enable_subscription(PGconn *conn, LogicalRepInfo *dbinfo);
 
+#define DEFAULT_WAIT	60
 #define	USEC_PER_SEC	1000000
+#define WAITS_PER_SEC	10		/* should divide USEC_PER_SEC evenly */
 #define	WAIT_INTERVAL	1		/* 1 second */
 
+static int	wait_seconds = DEFAULT_WAIT;
+
 /* Options */
 static const char *progname;
 
@@ -756,6 +760,9 @@ wait_for_end_recovery(const char *conninfo)
 	PGconn	   *conn;
 	PGresult   *res;
 	int			status = POSTMASTER_STILL_STARTING;
+	int			cnt;
+	int			rc;
+	char	   *pg_ctl_cmd;
 
 	pg_log_info("waiting the postmaster to reach the consistent state");
 
@@ -763,7 +770,7 @@ wait_for_end_recovery(const char *conninfo)
 	if (conn == NULL)
 		exit(1);
 
-	for (;;)
+	for (cnt = 0; cnt < wait_seconds * WAITS_PER_SEC; cnt++)
 	{
 		bool		in_recovery;
 
@@ -796,11 +803,25 @@ wait_for_end_recovery(const char *conninfo)
 		}
 
 		/* Keep waiting. */
-		pg_usleep(WAIT_INTERVAL * USEC_PER_SEC);
+		pg_usleep(USEC_PER_SEC / WAITS_PER_SEC);
 	}
 
 	disconnect_database(conn);
 
+	/*
+	 * if timeout is reached exit the pg_subscriber and stop the standby node
+	 */
+	if (cnt >= wait_seconds * WAITS_PER_SEC)
+	{
+		pg_log_error("recovery timed out");
+
+		pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path, subscriber_dir);
+		rc = system(pg_ctl_cmd);
+		pg_ctl_status(pg_ctl_cmd, rc, 0);
+
+		exit(1);
+	}
+
 	if (status == POSTMASTER_STILL_STARTING)
 	{
 		pg_log_error("server did not end recovery");
@@ -1160,6 +1181,7 @@ main(int argc, char **argv)
 	struct stat statbuf;
 
 	PGconn	   *conn;
+	PGresult   *res;
 	char	   *consistent_lsn;
 
 	PQExpBuffer recoveryconfcontents = NULL;
@@ -1167,6 +1189,7 @@ main(int argc, char **argv)
 	char		pidfile[MAXPGPATH];
 
 	int			i;
+	bool		in_recovery;
 
 	pg_logging_init(argv[0]);
 	pg_logging_set_level(PG_LOG_WARNING);
@@ -1340,6 +1363,38 @@ main(int argc, char **argv)
 	/* subscriber PID file. */
 	snprintf(pidfile, MAXPGPATH, "%s/postmaster.pid", subscriber_dir);
 
+	/*
+	 * Exit the pg_subscriber if the node is not a standby server.
+	 */
+	conn = connect_database(dbinfo[0].subconninfo);
+	if (conn == NULL)
+		exit(1);
+
+	res = PQexec(conn, "SELECT pg_catalog.pg_is_in_recovery()");
+
+	if (PQresultStatus(res) != PGRES_TUPLES_OK)
+	{
+		pg_log_error("could not obtain recovery progress");
+		exit(1);
+	}
+
+	if (PQntuples(res) != 1)
+	{
+		pg_log_error("unexpected result from pg_is_in_recovery function");
+		exit(1);
+	}
+
+	in_recovery = (strcmp(PQgetvalue(res, 0, 0), "t") == 0);
+
+	if (!in_recovery)
+	{
+		pg_log_error("pg_subscriber is supported only on standby server");
+		exit(1);
+	}
+
+	PQclear(res);
+	disconnect_database(conn);
+
 	/*
 	 * Stop the subscriber if it is a standby server. Before executing the
 	 * transformation steps, make sure the subscriber is not running because
-- 
2.34.1