Hi,
Here is the new patch to allow slaves to follow master after a failover.
Following our discussion this feature will only be used in master/slave
and streaming replication mode. I also join a patch to the
documentation. Here is the explanation of the follow master feature:
"After a master failover in master/slave streaming replication, I mean
at failover_command completion, pgpool degenerate all nodes excepted the
new master and starts new child processes to be ready again to accept
connections from clients. After this, pgpool run the command set into
the 'follow_master_command' for each degenerated nodes. Typically the
command should be used to recover the slave from the new master by call
the pcp_recovery_node command for example."
Regards,
--
Gilles Darold
http://dalibo.com - http://dalibo.org
diff -rNu pgpool-II-current/main.c pgpool-II-follow/main.c
--- pgpool-II-current/main.c 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/main.c 2011-02-27 19:05:24.000000000 +0100
@@ -106,6 +106,7 @@
static int pool_pause(struct timeval *timeout);
static void kill_all_children(int sig);
static int get_next_master_node(void);
+static pid_t fork_follow_child(void);
static RETSIGTYPE exit_handler(int sig);
static RETSIGTYPE reap_handler(int sig);
@@ -137,6 +138,7 @@
static int unix_fd; /* unix domain socket fd */
static int inet_fd; /* inet domain socket fd */
+static int follow_pid; /* pid for child process handling follow command */
static int pcp_pid; /* pid for child process handling PCP */
static int pcp_unix_fd; /* unix domain socket fd for PCP (not used) */
static int pcp_inet_fd; /* inet domain socket fd for PCP */
@@ -1597,6 +1599,37 @@
Req_info->master_node_id = new_master;
}
+ /*
+ In master/slaver streaming replication we start degenerating
+ all backends as they are not replicated anymore
+ */
+ int follow_cnt = 0;
+ if (MASTER_SLAVE && !strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP))
+ {
+ /* Only if the failover is against the current master */
+ if ((Req_info->kind == NODE_DOWN_REQUEST) && (node_id == Req_info->master_node_id)) {
+ for (i = 0; i < pool_config->backend_desc->num_backends; i++)
+ {
+ /* do not degenerate the new master */
+ if (i != new_master) {
+ BackendInfo *bkinfo;
+ bkinfo = pool_get_node_info(i);
+ pool_log("starting follow degeneration. shutdown host %s(%d)",
+ bkinfo->backend_hostname,
+ bkinfo->backend_port);
+ bkinfo->backend_status = CON_DOWN; /* set down status */
+ follow_cnt++;
+ }
+ }
+ if (follow_cnt == 0)
+ {
+ pool_log("failover: no follow backends are degenerated");
+ } else {
+ pool_log("failover: %d follow backends have been degenerated", follow_cnt);
+ }
+ }
+ }
+
/* no need to wait since it will be done in reap_handler */
#ifdef NOT_USED
while (wait(NULL) > 0)
@@ -1609,6 +1642,17 @@
memset(Req_info->node_id, -1, sizeof(int) * MAX_NUM_BACKENDS);
pool_semaphore_unlock(REQUEST_INFO_SEM);
+ /* Save primary node id */
+ if (follow_cnt > 0)
+ {
+ Req_info->primary_node_id = new_master;
+ } else {
+ Req_info->primary_node_id = find_primary_node();
+ }
+ Req_info->master_node_id = Req_info->primary_node_id;
+ pool_log("Primary node id saved: %d", Req_info->primary_node_id);
+ Req_info->prefered_primary_node_id = -1;
+
/* fork the children */
for (i=0;i<pool_config->num_init_children;i++)
{
@@ -1629,15 +1673,19 @@
BACKEND_INFO(node_id).backend_port);
}
- /* Save primary node id */
- Req_info->primary_node_id = find_primary_node();
-
switching = 0;
/* kick wakeup_handler in pcp_child to notice that
- * faiover/failback done
+ * failover/failback done
*/
kill(pcp_pid, SIGUSR2);
+
+ /* exec follow_master_command */
+ if ( (follow_cnt > 0) && (pool_config->follow_master_command) )
+ {
+ follow_pid = fork_follow_child();
+ }
+
}
/*
@@ -2344,7 +2392,7 @@
return -1;
}
- status = do_query(con, "SELECT pg_is_in_recovery() AND pgpool_walrecrunning()",
+ status = do_query(con, "SELECT not pg_is_in_recovery() AND not pgpool_walrecrunning()",
&res, PROTO_MAJOR_V3);
if (res->numrows <= 0)
{
@@ -2358,7 +2406,7 @@
{
pool_log("find_primary_node: do_query returns NULL");
}
- if (res->data[0] && !strcmp(res->data[0], "t"))
+ if (res->data[0] && !strcmp(res->data[0], "f"))
{
is_standby = true;
}
@@ -2387,3 +2435,34 @@
pool_log("find_primary_node: primary node id is %d", i);
return i;
}
+
+/*
+* fork a follow child
+*/
+pid_t fork_follow_child(void)
+{
+ pid_t pid;
+ int i;
+
+ pid = fork();
+
+ if (pid == 0)
+ {
+ for (i = 0; i < pool_config->backend_desc->num_backends; i++)
+ {
+ BackendInfo *bkinfo;
+ bkinfo = pool_get_node_info(i);
+ pool_log("start triggering follow command.");
+ if (bkinfo->backend_status == CON_DOWN)
+ trigger_failover_command(i, pool_config->follow_master_command);
+ }
+ exit(0);
+ }
+ else if (pid == -1)
+ {
+ pool_error("follow fork() failed. reason: %s", strerror(errno));
+ exit(1);
+ }
+ return pid;
+}
+
diff -rNu pgpool-II-current/pgpool.conf.sample pgpool-II-follow/pgpool.conf.sample
--- pgpool-II-current/pgpool.conf.sample 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pgpool.conf.sample 2011-02-27 17:48:58.000000000 +0100
@@ -278,6 +278,17 @@
# %M = old master node id
# %P = old primary node id
# %% = '%' character
+follow_master_command = '' # Executes this command after master failover.
+ # Special values:
+ # %d = node id
+ # %h = host name
+ # %p = port number
+ # %D = database cluster path
+ # %m = new master node id
+ # %H = hostname of the new master node
+ # %M = old master node id
+ # %P = old primary node id
+ # %% = '%' character
fail_over_on_backend_error = on # Initiates failover when writing to the
# backend communication socket fails
diff -rNu pgpool-II-current/pgpool.conf.sample-master-slave pgpool-II-follow/pgpool.conf.sample-master-slave
--- pgpool-II-current/pgpool.conf.sample-master-slave 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pgpool.conf.sample-master-slave 2011-02-27 17:48:52.000000000 +0100
@@ -278,6 +278,17 @@
# %M = old master node id
# %P = old primary node id
# %% = '%' character
+follow_master_command = '' # Executes this command after master failover.
+ # Special values:
+ # %d = node id
+ # %h = host name
+ # %p = port number
+ # %D = database cluster path
+ # %m = new master node id
+ # %H = hostname of the new master node
+ # %M = old master node id
+ # %P = old primary node id
+ # %% = '%' character
fail_over_on_backend_error = on # Initiates failover when writing to the
# backend communication socket fails
diff -rNu pgpool-II-current/pgpool.conf.sample-replication pgpool-II-follow/pgpool.conf.sample-replication
--- pgpool-II-current/pgpool.conf.sample-replication 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pgpool.conf.sample-replication 2011-02-27 17:49:05.000000000 +0100
@@ -278,6 +278,17 @@
# %M = old master node id
# %P = old primary node id
# %% = '%' character
+follow_master_command = '' # Executes this command after master failover.
+ # Special values:
+ # %d = node id
+ # %h = host name
+ # %p = port number
+ # %D = database cluster path
+ # %m = new master node id
+ # %H = hostname of the new master node
+ # %M = old master node id
+ # %P = old primary node id
+ # %% = '%' character
fail_over_on_backend_error = on # Initiates failover when writing to the
# backend communication socket fails
diff -rNu pgpool-II-current/pgpool.conf.sample-stream pgpool-II-follow/pgpool.conf.sample-stream
--- pgpool-II-current/pgpool.conf.sample-stream 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pgpool.conf.sample-stream 2011-02-27 17:49:10.000000000 +0100
@@ -279,6 +279,17 @@
# %M = old master node id
# %P = old primary node id
# %% = '%' character
+follow_master_command = '' # Executes this command after master failover.
+ # Special values:
+ # %d = node id
+ # %h = host name
+ # %p = port number
+ # %D = database cluster path
+ # %m = new master node id
+ # %H = hostname of the new master node
+ # %M = old master node id
+ # %P = old primary node id
+ # %% = '%' character
fail_over_on_backend_error = on # Initiates failover when writing to the
# backend communication socket fails
diff -rNu pgpool-II-current/pool_config.c pgpool-II-follow/pool_config.c
--- pgpool-II-current/pool_config.c 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pool_config.c 2011-02-27 17:47:48.000000000 +0100
@@ -1904,6 +1904,7 @@
pool_config->health_check_period = 0;
pool_config->health_check_user = "nobody";
pool_config->failover_command = "";
+ pool_config->follow_master_command = "";
pool_config->failback_command = "";
pool_config->fail_over_on_backend_error = 1;
pool_config->insert_lock = 1;
@@ -2811,6 +2812,26 @@
pool_config->failover_command = str;
}
+ else if (!strcmp(key, "follow_master_command") &&
+ CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
+ {
+ char *str;
+
+ if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY)
+ {
+ PARSE_ERROR();
+ fclose(fd);
+ return(-1);
+ }
+ str = extract_string(yytext, token);
+ if (str == NULL)
+ {
+ fclose(fd);
+ return(-1);
+ }
+ pool_config->follow_master_command = str;
+ }
+
else if (!strcmp(key, "failback_command") &&
CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
{
diff -rNu pgpool-II-current/pool_config.h pgpool-II-follow/pool_config.h
--- pgpool-II-current/pool_config.h 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pool_config.h 2011-02-27 17:47:48.000000000 +0100
@@ -108,6 +108,7 @@
int health_check_period; /* health check period */
char *health_check_user; /* PostgreSQL user name for health check */
char *failover_command; /* execute command when failover happens */
+ char *follow_master_command; /* execute command when failover is ended */
char *failback_command; /* execute command when failback happens */
/*
diff -rNu pgpool-II-current/pool_config.l pgpool-II-follow/pool_config.l
--- pgpool-II-current/pool_config.l 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pool_config.l 2011-02-27 17:47:48.000000000 +0100
@@ -179,6 +179,7 @@
pool_config->health_check_period = 0;
pool_config->health_check_user = "nobody";
pool_config->failover_command = "";
+ pool_config->follow_master_command = "";
pool_config->failback_command = "";
pool_config->fail_over_on_backend_error = 1;
pool_config->insert_lock = 1;
@@ -1086,6 +1087,26 @@
pool_config->failover_command = str;
}
+ else if (!strcmp(key, "follow_master_command") &&
+ CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
+ {
+ char *str;
+
+ if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY)
+ {
+ PARSE_ERROR();
+ fclose(fd);
+ return(-1);
+ }
+ str = extract_string(yytext, token);
+ if (str == NULL)
+ {
+ fclose(fd);
+ return(-1);
+ }
+ pool_config->follow_master_command = str;
+ }
+
else if (!strcmp(key, "failback_command") &&
CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context))
{
diff -rNu pgpool-II-current/pool_process_reporting.c pgpool-II-follow/pool_process_reporting.c
--- pgpool-II-current/pool_process_reporting.c 2011-02-27 17:36:25.000000000 +0100
+++ pgpool-II-follow/pool_process_reporting.c 2011-02-27 17:47:48.000000000 +0100
@@ -321,6 +321,11 @@
strncpy(status[i].desc, "failover command", POOLCONFIG_MAXDESCLEN);
i++;
+ strncpy(status[i].name, "follow_master_command", POOLCONFIG_MAXNAMELEN);
+ snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%s", pool_config->follow_master_command);
+ strncpy(status[i].desc, "follow master command", POOLCONFIG_MAXDESCLEN);
+ i++;
+
strncpy(status[i].name, "failback_command", POOLCONFIG_MAXNAMELEN);
snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%s", pool_config->failback_command);
strncpy(status[i].desc, "failback command", POOLCONFIG_MAXDESCLEN);
612c612
< <dt>failover_command
---
> <dt>failover_command</dt>
646c646
< <dt>failback_command
---
> <dt>failback_command</td>
670a671,702
> <dt>follow_master_command</dt>
> <dd>
> <p>
> This parameter specifies a command to run in master/slave streaming replication mode only after a master failover.
> pgpool-II replaces the following special characters with backend specific information.
> </p>
>
> <blockquote>
> <table border>
> <tr><td>Special character</td><td>Description</td></tr>
> <tr><td>%d</td><td>Backend ID of a detached node.</td></tr>
> <tr><td>%h</td><td>Hostname of a detached node.</td></tr>
> <tr><td>%p</td><td>Port number of a detached node.</td></tr>
> <tr><td>%D</td><td>Database cluster directory of a detached node.</td></tr>
> <tr><td>%M</td><td>Old master node ID.</td></tr>
> <tr><td>%m</td><td>New master node ID.</td></tr>
> <tr><td>%H</td><td>Hostname of the new master node.</td></tr>
> <tr><td>%P</td><td>Old primary node ID.</td></tr>
> <tr><td>%%</td><td>'%' character</td></tr>
> </table>
> </blockquote>
> <p>
> You need to reload pgpool.conf if you change follow_master_command.
> </p>
>
> <p>
> When a master failover is completed in master/slave streaming replication, pgpool degenerate all
> nodes excepted the new master and starts new child processes to be ready again to accept connections from clients.
> After this, pgpool run the command set into the 'follow_master_command' for each degenerated nodes. Typically the
> command should be used to recover the slave from the new master by call the pcp_recovery_node command for example.
> </p>
>
_______________________________________________
Pgpool-hackers mailing list
[email protected]
http://pgfoundry.org/mailman/listinfo/pgpool-hackers