diff --git a/doc/src/sgml/ref/pcp_promote_node.sgml b/doc/src/sgml/ref/pcp_promote_node.sgml
index 927e7104..0cc4d01c 100644
--- a/doc/src/sgml/ref/pcp_promote_node.sgml
+++ b/doc/src/sgml/ref/pcp_promote_node.sgml
@@ -26,22 +26,43 @@ Pgpool-II documentation
optionsnode_idgracefully
+ switchoverDescription
- pcp_promote_node
- promotes the given node as new primary to Pgpool-II. In streaming replication mode only. Please note that this command does not actually promote standby PostgreSQL backend: it just changes the internal status of Pgpool-II and trigger failover and users have to promote standby PostgreSQL outside Pgpool-II.
+ pcp_promote_node promotes the given node as new
+ primary to Pgpool-II. In streaming
+ replication mode only. Please note that this command does not
+ actually promote standby PostgreSQL
+ backend unless switchover option is specified:
+ it just changes the internal status of
+ Pgpool-II and trigger failover and users
+ have to promote standby PostgreSQL outside
+ Pgpool-II.
- pcp_promote_node executes followings. Please be
+ If switchover is specified,
+ Pgpool-II detaches current primary
+ (changes the internal status to down) and execute the , with the new main node argument to
+ be set to the specified node id. Because most failover scripts
+ promote the new main node, the specified node will be the new
+ primary node. The is
+ necessary to be set properly to turn the former primary into
+ standby.
+
+
+
+ pcp_promote_node executes followings if
+ really-promote is not specified. Please be
warned that if is set,
the command will be executed. It is a standard advice that you
- disable before executing
- this command.
+ disable before
+ executing this command.
@@ -49,9 +70,9 @@ Pgpool-II documentation
Change the status of standby
PostgreSQL from standby to
- primary. It just changes the internal status of Pgpool-II and it
- does not actually promote PostgreSQL
- standby server.
+ primary. It just changes the internal status of
+ Pgpool-II and it does not actually
+ promote PostgreSQL standby server.
@@ -59,9 +80,53 @@ Pgpool-II documentation
Change the status of PostgreSQL node
which is not specified by this command's argument to down. It
- just changes the internal status of Pgpool-II and it does not
- actually make PostgreSQL standby
- server down.
+ just changes the internal status of
+ Pgpool-II and it does not actually
+ make PostgreSQL standby server down.
+
+
+
+
+
+ If is set, execute
+ against
+ PostgreSQL.
+
+
+
+
+
+
+
+ pcp_promote_node executes followings if
+ switchover is specified. If is set, the command will be
+ executed. You need to set before executing this command
+ because failover script will create the new primary and other nodes
+ need to be turned into standbys
+
+
+
+
+
+ Change the status of primary
+ PostgreSQL from up to down. This
+ triggers execution, with
+ the new main node argument to be set to the specified node
+ id. Because most failover scripts promote the new main node, the
+ specified node will become the new primary node.
+
+
+
+
+
+ Change the status of standby
+ PostgreSQL node which is not
+ specified by this command's argument to down. It just changes
+ the internal status of Pgpool-II and
+ it does not actually make PostgreSQL
+ standby server down.
@@ -103,6 +168,17 @@ Pgpool-II documentation
+
+
+
+
+
+ Let the specified node to be actually promoted by triggering the failover command.
+ Also change the current primary node status to down.
+
+
+
+
diff --git a/src/include/pcp/libpcp_ext.h b/src/include/pcp/libpcp_ext.h
index 9bcba4ad..df898b0a 100644
--- a/src/include/pcp/libpcp_ext.h
+++ b/src/include/pcp/libpcp_ext.h
@@ -356,8 +356,8 @@ extern PCPResultInfo * pcp_detach_node_gracefully(PCPConnInfo * pcpConn, int nid
extern PCPResultInfo * pcp_attach_node(PCPConnInfo * pcpConn, int nid);
extern PCPResultInfo * pcp_pool_status(PCPConnInfo * pcpConn);
extern PCPResultInfo * pcp_recovery_node(PCPConnInfo * pcpConn, int nid);
-extern PCPResultInfo * pcp_promote_node(PCPConnInfo * pcpConn, int nid);
-extern PCPResultInfo * pcp_promote_node_gracefully(PCPConnInfo * pcpConn, int nid);
+extern PCPResultInfo * pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool promote);
+extern PCPResultInfo * pcp_promote_node_gracefully(PCPConnInfo * pcpConn, int nid, bool promote);
extern PCPResultInfo * pcp_watchdog_info(PCPConnInfo * pcpConn, int nid);
extern PCPResultInfo * pcp_set_backend_parameter(PCPConnInfo * pcpConn, char *parameter_name, char *value);
diff --git a/src/include/pool.h b/src/include/pool.h
index 2950d290..fe19d2d9 100644
--- a/src/include/pool.h
+++ b/src/include/pool.h
@@ -428,6 +428,8 @@ typedef enum
* require majority vote */
#define REQ_DETAIL_UPDATE 0x00000008 /* failover req is just and update
* node status request */
+#define REQ_DETAIL_PROMOTE 0x00000010 /* failover req is actually promoting the specified standby node.
+ * current primary will be detached */
typedef struct
{
diff --git a/src/libs/pcp/pcp.c b/src/libs/pcp/pcp.c
index 052ff1e4..dc3b99cc 100644
--- a/src/libs/pcp/pcp.c
+++ b/src/libs/pcp/pcp.c
@@ -8,7 +8,7 @@
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
*
- * Copyright (c) 2003-2020 PgPool Global Development Group
+ * Copyright (c) 2003-2021 PgPool Global Development Group
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
@@ -56,7 +56,7 @@ static int pcp_authorize(PCPConnInfo * pcpConn, char *username, char *password);
static void pcp_internal_error(PCPConnInfo * pcpConn, const char *fmt,...);
static PCPResultInfo * _pcp_detach_node(PCPConnInfo * pcpConn, int nid, bool gracefully);
-static PCPResultInfo * _pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool gracefully);
+static PCPResultInfo * _pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool gracefully, bool promote);
static PCPResultInfo * process_pcp_response(PCPConnInfo * pcpConn, char sentMsg);
static void setCommandSuccessful(PCPConnInfo * pcpConn);
static void setResultStatus(PCPConnInfo * pcpConn, ResultStateType resultState);
@@ -1441,9 +1441,9 @@ pcp_recovery_node(PCPConnInfo * pcpConn, int nid)
* --------------------------------
*/
PCPResultInfo *
-pcp_promote_node(PCPConnInfo * pcpConn, int nid)
+pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool promote)
{
- return _pcp_promote_node(pcpConn, nid, FALSE);
+ return _pcp_promote_node(pcpConn, nid, FALSE, promote);
}
/* --------------------------------
@@ -1454,17 +1454,18 @@ pcp_promote_node(PCPConnInfo * pcpConn, int nid)
* --------------------------------
*/
PCPResultInfo *
-pcp_promote_node_gracefully(PCPConnInfo * pcpConn, int nid)
+pcp_promote_node_gracefully(PCPConnInfo * pcpConn, int nid, bool switchover)
{
- return _pcp_promote_node(pcpConn, nid, TRUE);
+ return _pcp_promote_node(pcpConn, nid, TRUE, switchover);
}
static PCPResultInfo *
-_pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool gracefully)
+_pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool gracefully, bool switchover)
{
int wsize;
char node_id[16];
char *sendchar;
+ char *switchover_option; /* n: just change node status, s: switchover primary */
if (PCPConnectionStatus(pcpConn) != PCP_CONNECTION_OK)
{
@@ -1472,17 +1473,31 @@ _pcp_promote_node(PCPConnInfo * pcpConn, int nid, bool gracefully)
return NULL;
}
- snprintf(node_id, sizeof(node_id), "%d", nid);
+ snprintf(node_id, sizeof(node_id), "%d ", nid);
if (gracefully)
sendchar = "j";
else
sendchar = "J";
+ if (switchover)
+ switchover_option = "s";
+ else
+ switchover_option = "n";
+
pcp_write(pcpConn->pcpConn, sendchar, 1);
- wsize = htonl(strlen(node_id) + 1 + sizeof(int));
+
+ /* caluculate send buffer size */
+ wsize = sizeof(char); /* protocol. 'j' or 'J' */
+ wsize += strlen(node_id); /* node id + space */
+ wsize += sizeof(char); /* promote option */
+ wsize += sizeof(int); /* buffer length */
+ wsize = htonl(wsize);
+
pcp_write(pcpConn->pcpConn, &wsize, sizeof(int));
pcp_write(pcpConn->pcpConn, node_id, strlen(node_id) + 1);
+ pcp_write(pcpConn->pcpConn, switchover_option, 1);
+
if (PCPFlush(pcpConn) < 0)
return NULL;
if (pcpConn->Pfdebug)
diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c
index d216cb55..ab5f2378 100644
--- a/src/main/pgpool_main.c
+++ b/src/main/pgpool_main.c
@@ -1417,6 +1417,7 @@ failover(void)
int node_count;
unsigned char request_details;
bool search_primary = true;
+ int promote_node = 0;
pool_semaphore_lock(REQUEST_INFO_SEM);
@@ -1459,6 +1460,20 @@ failover(void)
ereport(DEBUG1,
(errmsg("failover handler"),
errdetail("starting to select new main node")));
+
+ /* If this is promoting specified node, new_main_node
+ * should be replaced by the requested node. The requested
+ * node should be REAL_PRIMARY_NODE_ID.
+ */
+ if (request_details & REQ_DETAIL_PROMOTE)
+ {
+ promote_node = node_id_set[0];
+ for (i = 0; i < node_count; i++)
+ {
+ node_id_set[i] = REAL_PRIMARY_NODE_ID;
+ }
+ }
+
node_id = node_id_set[0];
/* failback request? */
@@ -1778,8 +1793,8 @@ failover(void)
}
/*
- * Exec failover_command if needed We do not execute failover when
- * request is quarantine type
+ * Exec failover_command if needed. We do not execute failover when
+ * request is quarantine type.
*/
if (reqkind == NODE_DOWN_REQUEST)
{
@@ -1787,8 +1802,20 @@ failover(void)
{
if (nodes[i])
{
- trigger_failover_command(i, pool_config->failover_command,
- MAIN_NODE_ID, new_main_node, REAL_PRIMARY_NODE_ID);
+ /* If this is prmoting specified node, new_main_node
+ * should be replaced by the requested node. The requested
+ * node should be REAL_PRIMARY_NODE_ID.
+ */
+ if (request_details & REQ_DETAIL_PROMOTE)
+ {
+ trigger_failover_command(i, pool_config->failover_command,
+ MAIN_NODE_ID, promote_node, REAL_PRIMARY_NODE_ID);
+ }
+ else
+ {
+ trigger_failover_command(i, pool_config->failover_command,
+ MAIN_NODE_ID, new_main_node, REAL_PRIMARY_NODE_ID);
+ }
sync_required = true;
}
}
@@ -2794,7 +2821,7 @@ trigger_failover_command(int node, const char *command_line,
break;
case 'r': /* new main node port */
- newmain = pool_get_node_info(get_next_main_node());
+ newmain = pool_get_node_info(new_main_node);
if (newmain)
{
snprintf(port_buf, sizeof(port_buf), "%d", newmain->backend_port);
@@ -2806,7 +2833,7 @@ trigger_failover_command(int node, const char *command_line,
break;
case 'R': /* new main database directory */
- newmain = pool_get_node_info(get_next_main_node());
+ newmain = pool_get_node_info(new_main_node);
if (newmain)
string_append_char(exec_cmd, newmain->backend_data_directory);
else
diff --git a/src/pcp_con/pcp_worker.c b/src/pcp_con/pcp_worker.c
index 201f1104..7afa7455 100644
--- a/src/pcp_con/pcp_worker.c
+++ b/src/pcp_con/pcp_worker.c
@@ -75,7 +75,7 @@ static void send_md5salt(PCP_CONNECTION * frontend, char *salt);
static void pcp_process_command(char tos, char *buf, int buf_len);
-static int pool_detach_node(int node_id, bool gracefully);
+static int pool_detach_node(int node_id, bool gracefully, bool switchover);
static int pool_promote_node(int node_id, bool gracefully);
static void inform_process_count(PCP_CONNECTION * frontend);
static void inform_process_info(PCP_CONNECTION * frontend, char *buf);
@@ -227,8 +227,10 @@ pcp_worker_main(int port)
static void
pcp_process_command(char tos, char *buf, int buf_len)
{
+ /* The request is recovery or pcp shutdown request? */
if (tos == 'O' || tos == 'T')
{
+ /* Prevent those pcp requests while processing failover/failback request */
if (Req_info->switching)
{
if (Req_info->request_queue_tail != Req_info->request_queue_head)
@@ -521,11 +523,16 @@ user_authenticate(char *buf, char *passwd_file, char *salt, int salt_len)
/* Detach a node */
static int
-pool_detach_node(int node_id, bool gracefully)
+pool_detach_node(int node_id, bool gracefully, bool switchover)
{
+ int flag = 0;
+
+ if (switchover)
+ flag = REQ_DETAIL_PROMOTE;
+
if (!gracefully)
{
- degenerate_backend_set_ex(&node_id, 1, REQ_DETAIL_SWITCHOVER | REQ_DETAIL_CONFIRMED, true, false);
+ degenerate_backend_set_ex(&node_id, 1, flag | REQ_DETAIL_SWITCHOVER | REQ_DETAIL_CONFIRMED, true, false);
return 0;
}
@@ -552,7 +559,7 @@ pool_detach_node(int node_id, bool gracefully)
/*
* Now all frontends have gone. Let's do failover.
*/
- degenerate_backend_set_ex(&node_id, 1, REQ_DETAIL_SWITCHOVER | REQ_DETAIL_CONFIRMED, false, false);
+ degenerate_backend_set_ex(&node_id, 1, flag | REQ_DETAIL_SWITCHOVER | REQ_DETAIL_CONFIRMED, false, false);
/*
* Wait for failover completed.
@@ -1071,7 +1078,7 @@ process_detach_node(PCP_CONNECTION * frontend, char *buf, char tos)
(errmsg("PCP: processing detach node"),
errdetail("detaching Node ID %d", node_id)));
- pool_detach_node(node_id, gracefully);
+ pool_detach_node(node_id, gracefully, false);
pcp_write(frontend, "d", 1);
wsize = htonl(sizeof(code) + sizeof(int));
@@ -1214,6 +1221,11 @@ process_status_request(PCP_CONNECTION * frontend)
errdetail("retrieved status information")));
}
+/*
+ * Process promote node request. This function is tricky. If promote option
+ * is sent from client, calls pool_detach_node() so that failover script
+ * actually promote the specified node and detach current primary.
+ */
static void
process_promote_node(PCP_CONNECTION * frontend, char *buf, char tos)
{
@@ -1221,22 +1233,34 @@ process_promote_node(PCP_CONNECTION * frontend, char *buf, char tos)
int wsize;
char code[] = "CommandComplete";
bool gracefully;
+ char node_id_buf[64];
+ char *p;
+ char promote_option;
if (tos == 'J')
gracefully = false;
else
gracefully = true;
- node_id = atoi(buf);
+ p = node_id_buf;
+ while (*buf && *buf != ' ')
+ *p++ = *buf++;
+ *p = '\0';
+ buf += 2;
+ promote_option = *buf;
+
+ node_id = atoi(node_id_buf);
+
if ((node_id < 0) || (node_id >= pool_config->backend_desc->num_backends))
ereport(ERROR,
- (errmsg("could not process recovery request"),
+ (errmsg("could not process promote request"),
errdetail("node id %d is not valid", node_id)));
- /* promoting node is reserved to Streaming Replication */
+
+ /* Promoting node is only possible in Streaming Replication mode */
if (!STREAM)
{
ereport(FATAL,
- (errmsg("invalid pgpool mode for process recovery request"),
+ (errmsg("invalid pgpool mode for process promote request"),
errdetail("not in streaming replication mode, can't promote node id %d", node_id)));
}
@@ -1244,14 +1268,27 @@ process_promote_node(PCP_CONNECTION * frontend, char *buf, char tos)
if (node_id == REAL_PRIMARY_NODE_ID)
{
ereport(FATAL,
- (errmsg("invalid pgpool mode for process recovery request"),
+ (errmsg("invalid promote request"),
errdetail("specified node is already primary node, can't promote node id %d", node_id)));
}
- ereport(DEBUG1,
+
+ ereport(LOG, (errmsg("pcp_promote_node: promote option: %c", promote_option)));
+
+ if (promote_option == 's')
+ {
+ ereport(DEBUG1,
(errmsg("PCP: processing promote node"),
- errdetail("promoting Node ID %d", node_id)));
- pool_promote_node(node_id, gracefully);
+ errdetail("promoting Node ID %d and shutdown primary node %d", node_id, REAL_PRIMARY_NODE_ID)));
+ pool_detach_node(node_id, gracefully, true);
+ }
+ else
+ {
+ ereport(DEBUG1,
+ (errmsg("PCP: processing promote node"),
+ errdetail("promoting Node ID %d", node_id)));
+ pool_promote_node(node_id, gracefully);
+ }
pcp_write(frontend, "d", 1);
wsize = htonl(sizeof(code) + sizeof(int));
diff --git a/src/tools/pcp/pcp_frontend_client.c b/src/tools/pcp/pcp_frontend_client.c
index 8ace0a90..36e369fb 100644
--- a/src/tools/pcp/pcp_frontend_client.c
+++ b/src/tools/pcp/pcp_frontend_client.c
@@ -4,7 +4,7 @@
* pgpool: a language independent connection pool server for PostgreSQL
* written by Tatsuo Ishii
*
- * Copyright (c) 2003-2020 PgPool Global Development Group
+ * Copyright (c) 2003-2021 PgPool Global Development Group
*
* Permission to use, copy, modify, and distribute this software and
* its documentation for any purpose and without fee is hereby
@@ -90,7 +90,7 @@ struct AppTypes AllAppTypes[] =
{"pcp_pool_status", PCP_POOL_STATUS, "h:p:U:wWvd", "display pgpool configuration and status"},
{"pcp_proc_count", PCP_PROC_COUNT, "h:p:U:wWvd", "display the list of pgpool-II child process PIDs"},
{"pcp_proc_info", PCP_PROC_INFO, "h:p:P:U:awWvd", "display a pgpool-II child process' information"},
- {"pcp_promote_node", PCP_PROMOTE_NODE, "n:h:p:U:gwWvd", "promote a node as new main from pgpool-II"},
+ {"pcp_promote_node", PCP_PROMOTE_NODE, "n:h:p:U:gowWvd", "promote a node as new main from pgpool-II"},
{"pcp_recovery_node", PCP_RECOVERY_NODE, "n:h:p:U:wWvd", "recover a node"},
{"pcp_stop_pgpool", PCP_STOP_PGPOOL, "m:h:p:U:s:wWvda", "terminate pgpool-II"},
{"pcp_watchdog_info", PCP_WATCHDOG_INFO, "n:h:p:U:wWvd", "display a pgpool-II watchdog's information"},
@@ -117,6 +117,7 @@ main(int argc, char **argv)
bool debug = false;
bool need_password = true;
bool gracefully = false;
+ bool switchover = false;
bool verbose = false;
PCPConnInfo *pcpConn;
PCPResultInfo *pcpResInfo;
@@ -135,6 +136,7 @@ main(int argc, char **argv)
{"mode", required_argument, NULL, 'm'},
{"scope", required_argument, NULL, 's'},
{"gracefully", no_argument, NULL, 'g'},
+ {"switchover", no_argument, NULL, 'o'},
{"verbose", no_argument, NULL, 'v'},
{"all", no_argument, NULL, 'a'},
{"node-id", required_argument, NULL, 'n'},
@@ -290,6 +292,10 @@ main(int argc, char **argv)
user = strdup(optarg);
break;
+ case 'o':
+ switchover = true;
+ break;
+
case '?':
default:
@@ -417,9 +423,9 @@ main(int argc, char **argv)
else if (current_app_type->app_type == PCP_PROMOTE_NODE)
{
if (gracefully)
- pcpResInfo = pcp_promote_node_gracefully(pcpConn, nodeID);
+ pcpResInfo = pcp_promote_node_gracefully(pcpConn, nodeID, switchover);
else
- pcpResInfo = pcp_promote_node(pcpConn, nodeID);
+ pcpResInfo = pcp_promote_node(pcpConn, nodeID, switchover);
}
else if (current_app_type->app_type == PCP_RECOVERY_NODE)
@@ -869,7 +875,12 @@ usage(void)
if (current_app_type->app_type == PCP_PROMOTE_NODE ||
current_app_type->app_type == PCP_DETACH_NODE)
{
- fprintf(stderr, " -g, --gracefully promote gracefully(optional)\n");
+ fprintf(stderr, " -g, --gracefully promote gracefully (optional)\n");
+ }
+
+ if (current_app_type->app_type == PCP_PROMOTE_NODE)
+ {
+ fprintf(stderr, " -o, --switchover switchover primary to specified node (optional)\n");
}
if (current_app_type->app_type == PCP_WATCHDOG_INFO)