pgpool2.git / summary / log / commit / refs

commit    d8d85e10d71d0eb3b774dbe06a4f3dc5b3b80b82
Author:   Toshihiro Kitagawa <kitagawa at sraoss.co.jp>
Date:     Thu Apr 21 01:02:51 2011 +0000

    Add pcp_promote_node command.
    Patch contributed by Gilles Darold.


doc/pgpool-en.html | 13 ++++ main.c | 49 ++++++++++++- pcp/Makefile.am | 4 +- pcp/Makefile.in | 17 ++++- pcp/pcp.c | 98 ++++++++++++++++++++++++++ pcp/pcp.h | 2 + pcp/pcp_promote_node.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++ pcp_child.c | 126 +++++++++++++++++++++++++++++++++ pool.h | 4 +- 9 files changed, 490 insertions(+), 8 deletions(-) diff --git a/doc/pgpool-en.html b/doc/pgpool-en.html index 4f600cc10..4cbd23d41 100644 --- a/doc/pgpool-en.html +++ b/doc/pgpool-en.html @@ -3060,6 +3060,7 @@ invalidate old query cache automatically when the data is updated.</p> * pcp_systemdb_info - retrieves the System DB information * pcp_detach_node - detaches a node from pgpool-II * pcp_attach_node - attaches a node to pgpool-II +* pcp_promote_node - promote a new master node to pgpool-II * pcp_stop_pgpool - stops pgpool-II </pre> </p> @@ -3294,6 +3295,18 @@ Attaches the given node to pgpool-II. </pre> </p> +<h3>pcp_promote_node</h3> +<p> +<pre> +Format: +pcp_promote_node [-g] _timeout_ _host_ _port_ _userid_ _passwd_ _nodeid_ + +Promotes the given node as new master to pgpool-II. In master/slave streaming replication only. +If -g is given, wait until all clients are disconnected (unless +client_idle_limit_in_recovery is -1 or recovery_timeout is expired). +</pre> +</p> + <h3>pcp_stop_pgpool</h3> <pre> Format: diff --git a/main.c b/main.c index b56aa402b..d4b4805de 100644 --- a/main.c +++ b/main.c @@ -1285,6 +1285,24 @@ void degenerate_backend_set(int *node_id_set, int count) pool_semaphore_unlock(REQUEST_INFO_SEM); } +/* send promote node request using SIGUSR1 */ +void promote_backend(int node_id) +{ + pid_t parent = getppid(); + + if (pool_config->parallel_mode) + { + return; + } + + pool_semaphore_lock(REQUEST_INFO_SEM); + Req_info->kind = PROMOTE_NODE_REQUEST; + Req_info->node_id[0] = node_id; + pool_log("promote_backend: %d promote node request from pid %d", node_id, getpid()); + kill(parent, SIGUSR1); + pool_semaphore_unlock(REQUEST_INFO_SEM); +} + /* send failback request using SIGUSR1 */ void send_failback_request(int node_id) { @@ -1491,6 +1509,24 @@ static void failover(void) trigger_failover_command(node_id, pool_config->failback_command, MASTER_NODE_ID, get_next_master_node(), PRIMARY_NODE_ID); } + else if (Req_info->kind == PROMOTE_NODE_REQUEST) + { + if (Req_info->node_id[0] != -1 && + VALID_BACKEND(Req_info->node_id[0])) + { + pool_log("starting promotion. promotion host %s(%d)", + BACKEND_INFO(Req_info->node_id[0]).backend_hostname, + BACKEND_INFO(Req_info->node_id[0]).backend_port); + } + else + { + pool_log("failover: no backends are promoted"); + pool_semaphore_unlock(REQUEST_INFO_SEM); + kill(pcp_pid, SIGUSR2); + switching = 0; + return; + } + } else { int cnt = 0; @@ -1595,17 +1631,24 @@ static void failover(void) MASTER_NODE_ID, new_master, PRIMARY_NODE_ID); } + if (Req_info->kind == PROMOTE_NODE_REQUEST && + VALID_BACKEND(Req_info->node_id[0])) + new_primary = Req_info->node_id[0]; + else + new_primary = find_primary_node(); + /* * In master/slave streaming replication we start degenerating * all backends as they are not replicated anymore */ int follow_cnt = 0; - new_primary = find_primary_node(); if (MASTER_SLAVE && !strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP)) { /* only if the failover is against the current primary */ - if ((Req_info->kind == NODE_DOWN_REQUEST) && - (nodes[Req_info->primary_node_id])) { + if (((Req_info->kind == NODE_DOWN_REQUEST) && + (nodes[Req_info->primary_node_id])) || + ((Req_info->kind == PROMOTE_NODE_REQUEST) && + (VALID_BACKEND(Req_info->node_id[0])))) { for (i = 0; i < pool_config->backend_desc->num_backends; i++) { diff --git a/pcp/Makefile.am b/pcp/Makefile.am index bf2ca5892..f9680bcd6 100644 --- a/pcp/Makefile.am +++ b/pcp/Makefile.am @@ -11,7 +11,7 @@ md5.h: ../md5.h rm -f $@ && ln -s $< . bin_PROGRAMS = pcp_stop_pgpool pcp_node_count pcp_node_info pcp_proc_count pcp_proc_info \ - pcp_systemdb_info pcp_detach_node pcp_attach_node pcp_recovery_node + pcp_systemdb_info pcp_detach_node pcp_attach_node pcp_recovery_node pcp_promote_node pcp_stop_pgpool_SOURCES = pcp_stop_pgpool.c pcp.h pcp_stop_pgpool_LDADD = libpcp.la pcp_stop_pgpool_LDFLAGS = @@ -31,3 +31,5 @@ pcp_attach_node_SOURCES = pcp_attach_node.c pcp.h pcp_attach_node_LDADD = libpcp.la pcp_recovery_node_SOURCES = pcp_recovery_node.c pcp.h pcp_recovery_node_LDADD = libpcp.la +pcp_promote_node_SOURCES = pcp_promote_node.c pcp.h +pcp_promote_node_LDADD = libpcp.la diff --git a/pcp/Makefile.in b/pcp/Makefile.in index d8e859b9e..2b2de093a 100644 --- a/pcp/Makefile.in +++ b/pcp/Makefile.in @@ -38,7 +38,7 @@ bin_PROGRAMS = pcp_stop_pgpool$(EXEEXT) pcp_node_count$(EXEEXT) \ pcp_node_info$(EXEEXT) pcp_proc_count$(EXEEXT) \ pcp_proc_info$(EXEEXT) pcp_systemdb_info$(EXEEXT) \ pcp_detach_node$(EXEEXT) pcp_attach_node$(EXEEXT) \ - pcp_recovery_node$(EXEEXT) + pcp_recovery_node$(EXEEXT) pcp_promote_node$(EXEEXT) subdir = pcp DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in @@ -88,6 +88,9 @@ pcp_proc_info_DEPENDENCIES = libpcp.la am_pcp_recovery_node_OBJECTS = pcp_recovery_node.$(OBJEXT) pcp_recovery_node_OBJECTS = $(am_pcp_recovery_node_OBJECTS) pcp_recovery_node_DEPENDENCIES = libpcp.la +am_pcp_promote_node_OBJECTS = pcp_promote_node.$(OBJEXT) +pcp_promote_node_OBJECTS = $(am_pcp_promote_node_OBJECTS) +pcp_promote_node_DEPENDENCIES = libpcp.la am_pcp_stop_pgpool_OBJECTS = pcp_stop_pgpool.$(OBJEXT) pcp_stop_pgpool_OBJECTS = $(am_pcp_stop_pgpool_OBJECTS) pcp_stop_pgpool_DEPENDENCIES = libpcp.la @@ -113,12 +116,14 @@ SOURCES = $(libpcp_la_SOURCES) $(pcp_attach_node_SOURCES) \ $(pcp_detach_node_SOURCES) $(pcp_node_count_SOURCES) \ $(pcp_node_info_SOURCES) $(pcp_proc_count_SOURCES) \ $(pcp_proc_info_SOURCES) $(pcp_recovery_node_SOURCES) \ - $(pcp_stop_pgpool_SOURCES) $(pcp_systemdb_info_SOURCES) + $(pcp_stop_pgpool_SOURCES) $(pcp_systemdb_info_SOURCES) \ + $(pcp_promote_node_SOURCES) DIST_SOURCES = $(libpcp_la_SOURCES) $(pcp_attach_node_SOURCES) \ $(pcp_detach_node_SOURCES) $(pcp_node_count_SOURCES) \ $(pcp_node_info_SOURCES) $(pcp_proc_count_SOURCES) \ $(pcp_proc_info_SOURCES) $(pcp_recovery_node_SOURCES) \ - $(pcp_stop_pgpool_SOURCES) $(pcp_systemdb_info_SOURCES) + $(pcp_stop_pgpool_SOURCES) $(pcp_systemdb_info_SOURCES) \ + $(pcp_promote_node_SOURCES) includeHEADERS_INSTALL = $(INSTALL_HEADER) HEADERS = $(include_HEADERS) ETAGS = etags @@ -266,6 +271,8 @@ pcp_attach_node_SOURCES = pcp_attach_node.c pcp.h pcp_attach_node_LDADD = libpcp.la pcp_recovery_node_SOURCES = pcp_recovery_node.c pcp.h pcp_recovery_node_LDADD = libpcp.la +pcp_promote_node_SOURCES = pcp_promote_node.c pcp.h +pcp_promote_node_LDADD = libpcp.la all: all-am .SUFFIXES: @@ -383,6 +390,9 @@ pcp_stop_pgpool$(EXEEXT): $(pcp_stop_pgpool_OBJECTS) $(pcp_stop_pgpool_DEPENDENC pcp_systemdb_info$(EXEEXT): $(pcp_systemdb_info_OBJECTS) $(pcp_systemdb_info_DEPENDENCIES) @rm -f pcp_systemdb_info$(EXEEXT) $(LINK) $(pcp_systemdb_info_OBJECTS) $(pcp_systemdb_info_LDADD) $(LIBS) +pcp_promote_node$(EXEEXT): $(pcp_promote_node_OBJECTS) $(pcp_promote_node_DEPENDENCIES) + @rm -f pcp_promote_node$(EXEEXT) + $(LINK) $(pcp_promote_node_OBJECTS) $(pcp_promote_node_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -403,6 +413,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_stop_pgpool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_stream.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_systemdb_info.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pcp_promote_node.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/pcp/pcp.c b/pcp/pcp.c index 3bf7502c3..c2f26f423 100644 --- a/pcp/pcp.c +++ b/pcp/pcp.c @@ -50,6 +50,7 @@ static int debug = 0; static int pcp_authorize(char *username, char *password); static int _pcp_detach_node(int nid, bool gracefully); +static int _pcp_promote_node(int nid, bool gracefully); /* -------------------------------- * pcp_connect - open connection to pgpool using given arguments @@ -1331,3 +1332,100 @@ pcp_disable_debug(void) { debug = 0; } + +/* -------------------------------- + * pcp_promote_node - promote a node given by the argument as new pgpool's master + * + * return 0 on success, -1 otherwise + * -------------------------------- + */ +int +pcp_promote_node(int nid) +{ + return _pcp_promote_node(nid, FALSE); +} + +/* -------------------------------- + + * and promote a node given by the argument as new pgpool's master + * + * return 0 on success, -1 otherwise + * -------------------------------- + */ +int +pcp_promote_node_gracefully(int nid) +{ + return _pcp_promote_node(nid, TRUE); +} + +static int _pcp_promote_node(int nid, bool gracefully) +{ + int wsize; + char node_id[16]; + char tos; + char *buf = NULL; + int rsize; + char *sendchar; + + if (pc == NULL) + { + if (debug) fprintf(stderr, "DEBUG: connection does not exist\n"); + errorcode = NOCONNERR; + return -1; + } + + snprintf(node_id, sizeof(node_id), "%d", nid); + + if (gracefully) + sendchar = "j"; + else + sendchar = "J"; + + pcp_write(pc, sendchar, 1); + wsize = htonl(strlen(node_id)+1 + sizeof(int)); + pcp_write(pc, &wsize, sizeof(int)); + pcp_write(pc, node_id, strlen(node_id)+1); + if (pcp_flush(pc) < 0) + { + if (debug) fprintf(stderr, "DEBUG: could not send data to backend\n"); + return -1; + } + if (debug) fprintf(stderr, "DEBUG: send: tos=\"E\", len=%d\n", ntohl(wsize)); + + if (pcp_read(pc, &tos, 1)) + return -1; + if (pcp_read(pc, &rsize, sizeof(int))) + return -1; + rsize = ntohl(rsize); + buf = (char *)malloc(rsize); + if (buf == NULL) + { + errorcode = NOMEMERR; + return -1; + } + if (pcp_read(pc, buf, rsize - sizeof(int))) + { + free(buf); + return -1; + } + if (debug) fprintf(stderr, "DEBUG: recv: tos=\"%c\", len=%d, data=%s\n", tos, rsize, buf); + + if (tos == 'e') + { + if (debug) fprintf(stderr, "DEBUG: command failed. reason=%s\n", buf); + errorcode = BACKENDERR; + } + else if (tos == 'd') + { + /* strcmp() for success message, or fail */ + if(strcmp(buf, "CommandComplete") == 0) + { + free(buf); + return 0; + } + } + + free(buf); + return -1; +} + diff --git a/pcp/pcp.h b/pcp/pcp.h index e18f08383..164aa6746 100644 --- a/pcp/pcp.h +++ b/pcp/pcp.h @@ -65,6 +65,8 @@ extern void pcp_set_timeout(long sec); extern int pcp_recovery_node(int nid); extern void pcp_enable_debug(void); extern void pcp_disable_debug(void); +extern int pcp_promote_node(int nid); +extern int pcp_promote_node_gracefully(int nid); /* ------------------------------ * pcp_error.c diff --git a/pcp/pcp_promote_node.c b/pcp/pcp_promote_node.c new file mode 100644 index 000000000..9a09376ce --- /dev/null +++ b/pcp/pcp_promote_node.c @@ -0,0 +1,185 @@ +/* + * $Header$ + * + * pgpool: a language independent connection pool server for PostgreSQL + * written by Tatsuo Ishii + * + * Copyright (c) 2003-2011 PgPool Global Development Group + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of the + * author not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. The author makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * Client program to send "promote node" command. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#else +#include "getopt_long.h" +#endif + +#include "pcp.h" + +static void usage(void); +static void myexit(ErrorCode e); + +int +main(int argc, char **argv) +{ + long timeout; + char host[MAX_DB_HOST_NAMELEN]; + int port; + char user[MAX_USER_PASSWD_LEN]; + char pass[MAX_USER_PASSWD_LEN]; + int nodeID; + int ch; + int optindex; + bool gracefully = false; + int sts; + + static struct option long_options[] = { + {"debug", no_argument, NULL, 'd'}, + {"gracefully", no_argument, NULL, 'g'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + while ((ch = getopt_long(argc, argv, "hdg", long_options, &optindex)) != -1) { + switch (ch) { + case 'd': + pcp_enable_debug(); + break; + + case 'g': + gracefully = true; + break; + + case 'h': + case '?': + default: + usage(); + exit(0); + } + } + argc -= optind; + argv += optind; + + if (argc != 6) + { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + + timeout = atol(argv[0]); + if (timeout < 0) { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + + if (strlen(argv[1]) >= MAX_DB_HOST_NAMELEN) + { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + strcpy(host, argv[1]); + + port = atoi(argv[2]); + if (port <= 1024 || port > 65535) + { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + + if (strlen(argv[3]) >= MAX_USER_PASSWD_LEN) + { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + strcpy(user, argv[3]); + + if (strlen(argv[4]) >= MAX_USER_PASSWD_LEN) + { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + strcpy(pass, argv[4]); + + nodeID = atoi(argv[5]); + if (nodeID < 0 || nodeID > MAX_NUM_BACKENDS) + { + errorcode = INVALERR; + pcp_errorstr(errorcode); + myexit(errorcode); + } + + pcp_set_timeout(timeout); + + if (pcp_connect(host, port, user, pass)) + { + pcp_errorstr(errorcode); + myexit(errorcode); + } + + if (gracefully) + sts = pcp_promote_node_gracefully(nodeID); + else + sts = pcp_promote_node(nodeID); + + if (sts) + { + pcp_errorstr(errorcode); + pcp_disconnect(); + myexit(errorcode); + } + + pcp_disconnect(); + + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "pcp_promote_node - promote a node as new master from pgpool-II\n\n"); + fprintf(stderr, "Usage: pcp_promote_node [-d][-g] timeout hostname port# username password nodeID\n"); + fprintf(stderr, "Usage: pcp_promote_node -h\n\n"); + fprintf(stderr, " -d, --debug : enable debug message (optional)\n"); + fprintf(stderr, " -g, --gracefully : promote gracefully(optional)\n"); + fprintf(stderr, " timeout : connection timeout value in seconds. command exits on timeout\n"); + fprintf(stderr, " hostname : pgpool-II hostname\n"); + fprintf(stderr, " port# : PCP port number\n"); + fprintf(stderr, " username : username for PCP authentication\n"); + fprintf(stderr, " password : password for PCP authentication\n"); + fprintf(stderr, " nodeID : ID of a node to be promoted\n"); + fprintf(stderr, " -h, --help : print this help\n"); +} + +static void +myexit(ErrorCode e) +{ + if (e == INVALERR) + { + usage(); + exit(e); + } + + exit(e); +} diff --git a/pcp_child.c b/pcp_child.c index 768a2213b..58ae7e54e 100644 --- a/pcp_child.c +++ b/pcp_child.c @@ -69,6 +69,7 @@ static int user_authenticate(char *buf, char *passwd_file, char *salt, int salt_ static RETSIGTYPE wakeup_handler(int sig); static RETSIGTYPE reload_config_handler(int sig); static int pool_detach_node(int node_id, bool gracefully); +static int pool_promote_node(int node_id, bool gracefully); extern int myargc; extern char **myargv; @@ -826,6 +827,83 @@ pcp_do_child(int unix_fd, int inet_fd, char *pcp_conf_file) } break; + case 'J': /* promote node */ + case 'j': /* promote node gracefully */ + { + int node_id; + int wsize; + char code[] = "CommandComplete"; + bool gracefully; + + if (tos == 'J') + gracefully = false; + else + gracefully = true; + + node_id = atoi(buf); + if ( (node_id < 0) || (node_id >= pool_config->backend_desc->num_backends) ) + { + char code[] = "NodeIdOutOfRange"; + pool_error("pcp_child: node id %d is not valid", node_id); + pcp_write(frontend, "e", 1); + wsize = htonl(sizeof(code) + sizeof(int)); + pcp_write(frontend, &wsize, sizeof(int)); + pcp_write(frontend, code, sizeof(code)); + if (pcp_flush(frontend) < 0) + { + pool_error("pcp_child: pcp_flush() failed. reason: %s", strerror(errno)); + exit(1); + } + exit(1); + } + /* promoting node is reserved to Streaming Replication */ + if (!MASTER_SLAVE || (strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP) != 0)) + { + char code[] = "NotInStreamingReplication"; + pool_error("pcp_child: not in streaming replication mode, can't promote node id %d", node_id); + pcp_write(frontend, "e", 1); + wsize = htonl(sizeof(code) + sizeof(int)); + pcp_write(frontend, &wsize, sizeof(int)); + pcp_write(frontend, code, sizeof(code)); + if (pcp_flush(frontend) < 0) + { + pool_error("pcp_child: pcp_flush() failed. reason: %s", strerror(errno)); + exit(1); + } + exit(1); + } + + if (node_id == PRIMARY_NODE_ID) + { + char code[] = "NodeIdAlreadyPrimary"; + pool_error("pcp_child: specified node is already primary node, can't promote node id %d", node_id); + pcp_write(frontend, "e", 1); + wsize = htonl(sizeof(code) + sizeof(int)); + pcp_write(frontend, &wsize, sizeof(int)); + pcp_write(frontend, code, sizeof(code)); + if (pcp_flush(frontend) < 0) + { + pool_error("pcp_child: pcp_flush() failed. reason: %s", strerror(errno)); + exit(1); + } + exit(1); + } + + pool_debug("pcp_child: promoting Node ID %d", node_id); + pool_promote_node(node_id, gracefully); + + pcp_write(frontend, "d", 1); + wsize = htonl(sizeof(code) + sizeof(int)); + pcp_write(frontend, &wsize, sizeof(int)); + pcp_write(frontend, code, sizeof(code)); + if (pcp_flush(frontend) < 0) + { + pool_error("pcp_child: pcp_flush() failed. reason: %s", strerror(errno)); + exit(1); + } + break; + } + case 'F': pool_debug("pcp_child: stop online recovery"); break; @@ -1146,3 +1224,51 @@ static int pool_detach_node(int node_id, bool gracefully) return 0; } + +/* Promote a node */ +static int pool_promote_node(int node_id, bool gracefully) +{ + if (!gracefully) + { + promote_backend(node_id); /* send promote request */ + return 0; + } + + /* + * Wait until all frontends exit + */ + *InRecovery = 1; /* This wiil ensure that new incoming + * connection requests are blocked */ + + if (wait_connection_closed()) + { + /* wait timed out */ + finish_recovery(); + return -1; + } + + /* + * Now all frontends have gone. Let's do failover. + */ + promote_backend(node_id); /* send promote request */ + + /* + * Wait for failover completed. + */ + pcp_wakeup_request = 0; + + while (!pcp_wakeup_request) + { + struct timeval t = {1, 0}; + select(0, NULL, NULL, NULL, &t); + } + pcp_wakeup_request = 0; + + /* + * Start to accept incoming connections and send SIGUSR2 to pgpool + * parent to distribute SIGUSR2 all pgpool children. + */ + finish_recovery(); + + return 0; +} diff --git a/pool.h b/pool.h index 01f7982a3..c04b5ce0f 100644 --- a/pool.h +++ b/pool.h @@ -349,7 +349,8 @@ typedef enum { NODE_UP_REQUEST = 0, NODE_DOWN_REQUEST, NODE_RECOVERY_REQUEST, - CLOSE_IDLE_REQUEST + CLOSE_IDLE_REQUEST, + PROMOTE_NODE_REQUEST } POOL_REQUEST_KIND; typedef struct { @@ -448,6 +449,7 @@ extern POOL_STATUS NoticeResponse(POOL_CONNECTION *frontend, extern void notice_backend_error(int node_id); extern void degenerate_backend_set(int *node_id_set, int count); +extern void promote_backend(int node_id); extern void send_failback_request(int node_id); [parent: 52f3e7204875]