pgpool2.git / summary / log / commit / refs

commit    5ae03c3e1e1cae2fe77ba2d661d46cd16cb83db7
Author:   Tatsuo Ishii <ishii at sraoss.co.jp>
Date:     Wed Oct 20 01:08:55 2010 +0000

    Fix bug when primary node is not the youngest. Before pgpool-II
    unconditionally assumed the master node(the youngest live node) is a
    primary server, which is apprently wrong assumption. To fix the
    problem, now pgpool-II have info on shmem(Req_info) to know which is
    the primary. The info is set when 1)pgpool-II started 2)failover
    occurs. The work is done by new function find_primary_node(main.c). It
    does SELECT pg_is_in_recovery() AND pgpool_walrecrunning() to know the
    node in question is actually standby. pgpool_walrecrunning() is new
    PostgreSQL C function, to check if walreceiver is in running
    status. This is neccessary when failover occurs. Unfortunately
    pg_is_in_recovery() returns true even if standby is promoting because
    it found a trigger file. Installing pgpool_walrecrunning() is annoying
    but no way.  Also where_to_send() is modified to use PRIMARY_NODE_ID,
    rather than MASTER_NODE_ID.  CVS:
    ----------------------------------------------------------------------
    main.c pool.h pool_query_context.c pool_worker_child.c CVS: Added
    Files: CVS: sql/pgpool-walrecvrunning/Makefile CVS:
    sql/pgpool-walrecvrunning/pgpool-walrecrunning.c CVS:
    sql/pgpool-walrecvrunning/pgpool-walrecrunning.sql.in CVS:
    ----------------------------------------------------------------------


child.c | 16 ++++ main.c | 99 ++++++++++++++++++++++ pool.h | 17 ++++ pool_query_context.c | 14 +-- pool_worker_child.c | 8 +- sql/pgpool-walrecvrunning/Makefile | 20 +++++ sql/pgpool-walrecvrunning/pgpool-walrecrunning.c | 53 ++++++++++++ .../pgpool-walrecrunning.sql.in | 4 + 8 files changed, 220 insertions(+), 11 deletions(-) diff --git a/child.c b/child.c index 91aaea9e7..a9300d8bc 100644 --- a/child.c +++ b/child.c @@ -1465,6 +1465,22 @@ POOL_CONNECTION_POOL_SLOT *make_persistent_db_connection( return cp; } +/* + * Discard connection and memroy allocated by + * make_persistent_db_connection(). + */ +void discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT *cp) +{ + if(cp == NULL) + return; + + pool_close(cp->con); + free(cp->sp->startup_packet); + free(cp->sp->database); + free(cp->sp->user); + free(cp); +} + /* * do authentication for cp */ diff --git a/main.c b/main.c index b3cfa7a62..34b0a0f7e 100644 --- a/main.c +++ b/main.c @@ -120,6 +120,8 @@ static void stop_me(void); static int trigger_failover_command(int node, const char *command_line); +static int find_primary_node(void); + static struct sockaddr_un un_addr; /* unix domain socket path */ static struct sockaddr_un pcp_un_addr; /* unix domain socket path for PCP */ @@ -549,6 +551,9 @@ int main(int argc, char **argv) retrycnt = 0; /* reset health check retry counter */ sys_retrycnt = 0; /* reset SystemDB health check retry counter */ + /* Save primary node id */ + Req_info->primary_node_id = find_primary_node(); + /* * This is the main loop */ @@ -1608,6 +1613,9 @@ static void failover(void) 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 @@ -2240,3 +2248,94 @@ static int trigger_failover_command(int node, const char *command_line) return r; } + +/* + * Find the primary node (i.e. not standby node) and returns its node + * id. If no primary node is found, returns -1. + */ +static int find_primary_node(void) +{ + BackendInfo *bkinfo; + POOL_CONNECTION_POOL_SLOT *s; + POOL_CONNECTION *con; + POOL_STATUS status; + POOL_SELECT_RESULT *res; + bool is_standby; + int i; + + /* Streaming replication mode? */ + if (pool_config->master_slave_mode == 0 || + strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP)) + { + /* No point to look for primary node if not in streaming + * replication mode. + */ + pool_debug("find_primary_node: not in streaming replication mode"); + return -1; + } + + for(i=0;i<NUM_BACKENDS;i++) + { + if (!VALID_BACKEND(i)) + continue; + /* + * Check to see if this is a standby node or not + */ + is_standby = false; + + bkinfo = pool_get_node_info(i); + s = make_persistent_db_connection(bkinfo->backend_hostname, + bkinfo->backend_port, + "postgres", + pool_config->health_check_user, + ""); + if (!s) + { + pool_error("find_primary_node: make_persistent_connetcion failed"); + break; + } + con = s->con; + + status = do_query(con, "SELECT pg_is_in_recovery() AND pgpool_walrecrunning()", + &res, PROTO_MAJOR_V3); + if (res->numrows <= 0) + { + pool_log("find_primary_node: do_query returns no rows"); + } + if (res->data[0] == NULL) + { + pool_log("find_primary_node: do_query returns no data"); + } + if (res->nullflags[0] == -1) + { + pool_log("find_primary_node: do_query returns NULL"); + } + if (!strcmp(res->data[0], "t")) + { + is_standby = true; + } + free_select_result(res); + discard_persistent_db_connection(s); + + /* + * If this is a standby, we continue to look for primary node. + */ + if (is_standby) + { + pool_log("find_primary_node: %d node is standby", i); + } + else + { + break; + } + } + + if (i == NUM_BACKENDS) + { + pool_log("find_primary_node: no primary node found"); + return -1; + } + + pool_log("find_primary_node: primary node id is %d", i); + return i; +} diff --git a/pool.h b/pool.h index 4862b8cde..95cdc71e2 100644 --- a/pool.h +++ b/pool.h @@ -275,6 +275,20 @@ extern int pool_virtual_master_db_node_id(void); */ #define REAL_MASTER_NODE_ID (Req_info->master_node_id) +/* + * The primary node id in streaming replication mode. If not in the + * mode or there's no primary node, this macro returns + * REAL_MASTER_NODE_ID. + */ +#define PRIMARY_NODE_ID (Req_info->primary_node_id >=0?\ + Req_info->primary_node_id:REAL_MASTER_NODE_ID) + +/* + * Real primary node id. If not in the mode or there's no primary + * node, this macro returns -1. + */ +#define REAL_PRIMARY_NODE_ID (Req_info->primary_node_id) + /* * "Virtual" master node id. It's same as REAL_MASTER_NODE_ID if not * in load balance mode. If in load balance, it's the first load @@ -335,6 +349,7 @@ typedef struct { POOL_REQUEST_KIND kind; /* request kind */ int node_id[MAX_NUM_BACKENDS]; /* request node id */ int master_node_id; /* the youngest node id which is not in down status */ + int primary_node_id; /* the primary node id in streaming replication mode */ int conn_counter; } POOL_REQUEST_INFO; @@ -496,8 +511,10 @@ extern ProcessInfo *pool_get_process_info(pid_t pid); extern SystemDBInfo *pool_get_system_db_info(void); extern POOL_STATUS OneNode_do_command(POOL_CONNECTION *frontend, POOL_CONNECTION *backend, char *query, char *database); +/* child.c */ extern POOL_CONNECTION_POOL_SLOT *make_persistent_db_connection( char *hostname, int port, char *dbname, char *user, char *password); +extern void discard_persistent_db_connection(POOL_CONNECTION_POOL_SLOT *cp); /* define pool_system.c */ extern POOL_CONNECTION_POOL_SLOT *pool_system_db_connection(void); diff --git a/pool_query_context.c b/pool_query_context.c index e6c7c660d..be30a85ad 100644 --- a/pool_query_context.c +++ b/pool_query_context.c @@ -323,7 +323,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no /* Should be sent to primary only? */ if (dest == POOL_PRIMARY) { - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* Should be sent to both primary and standby? */ else if (dest == POOL_BOTH) @@ -364,7 +364,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no pool_config->delay_threshold && bkinfo->standby_delay > pool_config->delay_threshold) { - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* @@ -373,7 +373,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no */ else if (pool_has_function_call(node)) { - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* @@ -390,7 +390,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no */ else if (pool_has_system_catalog(node)) { - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* @@ -399,7 +399,7 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no */ else if (pool_has_temp_table(node)) { - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } else @@ -411,13 +411,13 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no else { /* Send to the primary only */ - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } } else { /* Send to the primary only */ - pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } } diff --git a/pool_worker_child.c b/pool_worker_child.c index 57edfb2af..916042f62 100644 --- a/pool_worker_child.c +++ b/pool_worker_child.c @@ -206,7 +206,7 @@ static void check_replication_time_lag(void) return; } - if (REAL_MASTER_NODE_ID == i) + if (PRIMARY_NODE_ID == i) { query = "SELECT pg_current_xlog_location()"; } @@ -260,9 +260,9 @@ static void check_replication_time_lag(void) /* Set standby delay value */ bkinfo = pool_get_node_info(i); - lag = lsn[REAL_MASTER_NODE_ID] - lsn[i]; + lag = lsn[PRIMARY_NODE_ID] - lsn[i]; - if (REAL_MASTER_NODE_ID == i) + if (PRIMARY_NODE_ID == i) { bkinfo->standby_delay = 0; } @@ -276,7 +276,7 @@ static void check_replication_time_lag(void) !strcmp(pool_config->log_standby_delay, "if_over_threshold") && lag > pool_config->delay_threshold)) { - pool_log("Replication of node:%d is behind %lld bytes from the primary server (node:%d)", i, lsn[REAL_MASTER_NODE_ID] - lsn[i], REAL_MASTER_NODE_ID); + pool_log("Replication of node:%d is behind %lld bytes from the primary server (node:%d)", i, lsn[PRIMARY_NODE_ID] - lsn[i], PRIMARY_NODE_ID); } } } diff --git a/sql/pgpool-walrecvrunning/Makefile b/sql/pgpool-walrecvrunning/Makefile new file mode 100644 index 000000000..65e479e23 --- /dev/null +++ b/sql/pgpool-walrecvrunning/Makefile @@ -0,0 +1,20 @@ +MODULES = pgpool-walrecrunning +DATA_built = pgpool-walrecrunning.sql + +# if you are using PostgreSQL 8.0 or later, +# using pg_config is recommended. +# if you are not, comment out following line and... +USE_PGXS = true +# set top_builddir to the PostgreSQL build source tree top. +# (for example /usr/local/src/postgresql-8.4) +top_builddir = ../.. + +ifdef USE_PGXS +PGXS = $(shell pg_config --pgxs) +include $(PGXS) +else +subdir = contrib/pgpool-walrecrunning +#top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/sql/pgpool-walrecvrunning/pgpool-walrecrunning.c b/sql/pgpool-walrecvrunning/pgpool-walrecrunning.c new file mode 100644 index 000000000..607ad3bf7 --- /dev/null +++ b/sql/pgpool-walrecvrunning/pgpool-walrecrunning.c @@ -0,0 +1,53 @@ +/* -*-pgsql-c-*- */ +/* + * $Header$ + * + * Copyright (c) 2003-2010 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. + * + * pgpool-walrecvrunning checks the walreceiver state to it's running. + * pgpool needs to know if the backend is running as a standby by + * using pg_is_in_recover(). Problem is, it returns true even if + * walreceiver is going to shutdown. This function checks the internal + * variable of walreciver to know if it is going to shutdown. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "postgres.h" +#include "utils/builtins.h" +#include "utils/elog.h" +#include "fmgr.h" +#include "funcapi.h" +#include "replication/walreceiver.h" +#include <stdlib.h> + +#ifdef PG_MODULE_MAGIC +PG_MODULE_MAGIC; +#endif + +PG_FUNCTION_INFO_V1(pgpool_walrecrunning); +extern Datum pgpool_walrecrunning(PG_FUNCTION_ARGS); + +Datum +pgpool_walrecrunning(PG_FUNCTION_ARGS) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile WalRcvData *walrcv = WalRcv; + + if (walrcv->walRcvState == WALRCV_RUNNING) + PG_RETURN_BOOL(true); + + PG_RETURN_BOOL(false); +} diff --git a/sql/pgpool-walrecvrunning/pgpool-walrecrunning.sql.in b/sql/pgpool-walrecvrunning/pgpool-walrecrunning.sql.in new file mode 100644 index 000000000..32f58de37 --- /dev/null +++ b/sql/pgpool-walrecvrunning/pgpool-walrecrunning.sql.in @@ -0,0 +1,4 @@ +CREATE OR REPLACE FUNCTION pgpool_walrecrunning() +RETURNS bool +AS 'MODULE_PATHNAME', 'pgpool_walrecrunning' +LANGUAGE C VOLATILE STRICT; [parent: d057e4a23a70]