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]