Fix pgpool hung when query cache enabled in extended query mode.
authorTatsuo Ishii <ishii@postgresql.org>
Thu, 31 May 2018 02:39:09 +0000 (11:39 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Thu, 31 May 2018 03:21:39 +0000 (12:21 +0900)
If replication delay is too much, load balancing is
disabled. Unfortunately query cache module tried to access the backend
even if the target node was already changed to primary even if load
balancing was disabled. To fix this, the target backend is identified
by using pending message data which reflects the fact that load
balancing is disabled.

Bug reported in [pgpool-general-jp: 1534].

To reproduce the bug, following steps are required.

1) create a 2 node streaming replication setting (just running
   pgpool_setup will do this),

2) set backend weight 0 to 0 to force the load balance node to node 1.

3) break recovery.conf on node 1 (for example modify conninfo)

4) start the whole cluster.

5) run massive DDL. pgbench -i is convenient for this.

6) make sure that replication delay is too much by using "show
   pool_nodes".

7) run following data using pgproto. (you need to adjust lines
including "#" so that # starts beginning of the line)

 # Test for disable_load_balance_on_write feature.
 #

 # Force load balance node to 1.
 ##backend_weight0 = 0
 ##backend_weight1 = 1

 # Start a transaction
'P' "" "BEGIN" 0
'B' "" "" 0 0 0
'E' "" 0

 # Issue SELECT.
'P' "" "SELECT 1"
'B' "" "" 0 0 0
'E' "" 0

 # Issue COMMIT
'P' "" "COMMIT" 0
'B' "" "" 0 0 0
'E' "" 0
'S'
'Y'

 # Issue same SELECT.
'P' "" "SELECT 1"
'B' "" "" 0 0 0
'E' "" 0
'S'
'Y'

'X'

8) next "SELECT 1" will be hung.

src/context/pool_session_context.c
src/include/context/pool_session_context.h
src/query_cache/pool_memqcache.c

index b63b8ca84c757bd05ac5a6a3508a48999ee03fde..79384d3f367bc97dd9678c0da75f8693e180cb7f 100644 (file)
@@ -1499,6 +1499,25 @@ POOL_PENDING_MESSAGE *pool_pending_message_find_lastest_by_query_context(POOL_QU
        return NULL;
 }
 
+/*
+ * Get target backend id from pending message assuming that the destination for
+ * the pending message is one of primary or standby node.
+ */
+int pool_pending_message_get_target_backend_id(POOL_PENDING_MESSAGE *msg)
+{
+       int backend_id = -1;
+
+       if (msg->node_ids[0] != -1)
+               backend_id = msg->node_ids[0];
+       else if (msg->node_ids[1] != -1)
+               backend_id = msg->node_ids[1];
+       else
+               ereport(ERROR,
+                               (errmsg("pool_pending_message_get_target_backend_id: no target backend id found")));
+
+       return backend_id;
+}
+
 /*
  * Dump whole pending message list
  */
index 2956b1d1547ec7592763ee805a73698388615151..b0bec425c81ec98530f856f3a1d4f55cf95ef679 100644 (file)
@@ -6,7 +6,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL 
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2017     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -288,7 +288,7 @@ extern void pool_pending_messages_destroy(void);
 extern POOL_PENDING_MESSAGE *pool_pending_message_create(char kind, int len, char *contents);
 extern void pool_pending_message_free_pending_message(POOL_PENDING_MESSAGE *message);
 extern void pool_pending_message_dest_set(POOL_PENDING_MESSAGE* message, POOL_QUERY_CONTEXT *query_context);
-void pool_pending_message_query_context_dest_set(POOL_PENDING_MESSAGE* message, POOL_QUERY_CONTEXT *query_context);
+extern void pool_pending_message_query_context_dest_set(POOL_PENDING_MESSAGE* message, POOL_QUERY_CONTEXT *query_context);
 extern void pool_pending_message_query_set(POOL_PENDING_MESSAGE* message, POOL_QUERY_CONTEXT *query_context);
 extern void pool_pending_message_add(POOL_PENDING_MESSAGE* message);
 extern POOL_PENDING_MESSAGE *pool_pending_message_head_message(void);
@@ -303,6 +303,7 @@ extern bool pool_pending_message_exists(void);
 extern const char *pool_pending_message_type_to_string(POOL_MESSAGE_TYPE type);
 extern void pool_check_pending_message_and_reply(POOL_MESSAGE_TYPE type, char kind);
 extern POOL_PENDING_MESSAGE *pool_pending_message_find_lastest_by_query_context(POOL_QUERY_CONTEXT *qc);
+extern int pool_pending_message_get_target_backend_id(POOL_PENDING_MESSAGE *msg);
 extern void dump_pending_message(void);
 extern void pool_set_major_version(int major);
 extern void pool_set_minor_version(int minor);
index ce91f2b99798810498ae578499113dd2cfeb44c6..fbf2f655a0dd828e1477cae8139e09b6ece1f411 100644 (file)
@@ -3,7 +3,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2017     PgPool Global Development Group
+ * Copyright (c) 2003-2018     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -3980,7 +3980,16 @@ static void inject_cached_message(POOL_CONNECTION *backend, char *qcache, int qc
        msg = pool_pending_message_find_lastest_by_query_context(query_context);
 
        if (msg)
+       {
+               /*
+                * If pending message found, we should extract target backend from it
+                */
+               int backend_id;
+
+               backend_id = pool_pending_message_get_target_backend_id(msg);
+               backend = CONNECTION(session_context->backend, backend_id);
                timeout = -1;
+       }
        else
                timeout = 0;