Fix query cache hang when used by node.js.
authorTatsuo Ishii <ishii@postgresql.org>
Wed, 31 May 2017 01:45:55 +0000 (10:45 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Wed, 31 May 2017 01:53:18 +0000 (10:53 +0900)
node.js sends a query in following pattern:

Parse
Bind
Describe
Execute
Flush
Sync

Notice the "Flush" message. This is unnecessary message and Pgpool-II
did not prepare for it. Since Pgpool-I supposed that next message to
Execute is "Sync" in pool_fetch_from_memory_cache(), it actually read
the "Flush" message and forwarded to backend, then discarded
subsequent "Ready for query" message, which was actually a "Sync"
message. That results in no "ready for query" message from backend.

Fix is, do not have any assumption regarding messages after Execute,
instead returns to the message processing loop. This way, whatever
messages coming after Execute should be properly processed.

Following is the test data for pgproto.

'Q' "DROP TABLE IF EXISTS pgproto_test1"
'Y'
'Q' "CREATE TABLE pgproto_test1(i INT)"
'Y'
'Q' "INSERT INTO pgproto_test1 VALUES(1)"
'Y'

'P' "S2" "SELECT 1 FROM pgproto_test1" 0
'B' "" "S2" 0 0 0
'D' 'S' "S2"
'E' "" 0
'H'
'C' 'S' "S2"
'S'
'Y'

'P' "S2" "SELECT 1 FROM pgproto_test1" 0
'B' "" "S2" 0 0 0
'D' 'S' "S2"
'E' "" 0
'H'
'C' 'S' "S2"
'S'
'Y'
'X'

Discussion: http://www.pgpool.net/pipermail/pgpool-general/2017-May/005569.html

pool_memqcache.c

index db05cb3d1e33349ecfa89d83bbcd3b5874c0650c..fe0c02cc17546c73da62a5e2316f09b1061ad5ea 100644 (file)
@@ -3,7 +3,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2016     PgPool Global Development Group
+ * Copyright (c) 2003-2017     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -582,41 +582,9 @@ POOL_STATUS pool_fetch_from_memory_cache(POOL_CONNECTION *frontend,
                free(qcache);
 
                /*
-                * If we are doing extended query, forward sync message from frontend to
-                * backend. This is necessary to prevent receiving Sync message after
-                * Sending Ready for query.
+                * Send a "READY FOR QUERY" if not in extended query.
                 */
-               if (pool_is_doing_extended_query_message())
-               {
-                       char kind;
-                       int32 len;
-                       POOL_SESSION_CONTEXT *session_context;
-                       POOL_CONNECTION *target_backend;
-                       char buf[5];
-
-                       if (pool_flush(frontend))
-                               return POOL_END;
-                       if (pool_read(frontend, &kind, 1))
-                               return POOL_END;
-                       pool_debug("pool_fetch_from_memory_cache: expecting sync: %c", kind);
-                       if (pool_read(frontend, &len, sizeof(len)))
-                               return POOL_END;
-
-                       /* Forward "Sync" message to backend */
-                       session_context = pool_get_session_context();
-                       target_backend = CONNECTION(backend, session_context->load_balance_node_id);
-                       pool_write(target_backend, &kind, 1);
-                       pool_write_and_flush(target_backend, &len, sizeof(len));
-
-                       /* Read and discard "Ready for query" message from backend */
-                       pool_read(target_backend, &kind, 1);
-                       pool_read(target_backend, buf, sizeof(buf));
-               }
-
-               /*
-                * send a "READY FOR QUERY"
-                */
-               if (MAJOR(backend) == PROTO_MAJOR_V3)
+               if (!pool_is_doing_extended_query_message() && MAJOR(backend) == PROTO_MAJOR_V3)
                {
                        signed char state;
 
@@ -626,10 +594,7 @@ POOL_STATUS pool_fetch_from_memory_cache(POOL_CONNECTION *frontend,
                        state = MASTER(backend)->tstate;
                        send_message(frontend, 'Z', 5, (char *)&state);
                }
-               else
-               {
-                       pool_write(frontend, "Z", 1);
-               }
+
                if (pool_flush(frontend))
                {
                        return POOL_END;