If statement timeout is enabled on backend and do_query() sends a
authorTatsuo Ishii <ishii@postgresql.org>
Tue, 24 May 2016 14:41:23 +0000 (23:41 +0900)
committerTatsuo Ishii <ishii@postgresql.org>
Tue, 24 May 2016 14:41:23 +0000 (23:41 +0900)
query to primary node, and all of following user queries are sent to
standby, it is possible that the next command, for example END, could
cause a statement timeout error on the primary, and a kind mismatch
error on pgpool-II is raised.

This fix tries to mitigate the problem by sending sync message instead
of flush message in do_query(), expecting that the sync message reset
the statement timeout timer if we are in an explicit transaction. We
cannot use this technique for implicit transaction case, because the
sync message removes the unnamed portal if there's any.

Plus, pg_stat_statement will no longer show the query issued by
do_query() as "running".

Per bug194.

pool_process_query.c

index 95d101b1951f7f4b50687fd3e4e85a15510bf985..73d5f4162e32891bc734ac5afccbe9e1d7112daa 100644 (file)
@@ -2561,9 +2561,17 @@ POOL_STATUS do_query(POOL_CONNECTION *backend, char *query, POOL_SELECT_RESULT *
                pool_write(backend, prepared_name, pname_len);
 
                /*
-                * Send flush message
+                * Send sync or flush message. If we are in an explicit transaction,
+                * sending "sync" is safe because it will not break unnamed
+                * portal. Also this is desirable because if no user queries are sent
+                * after do_query(), COMMIT command could cause statement timeout,
+                * because flush message does not clear the alarm for statement time
+                * out in the backend which has been set when do_query() issues query.
                 */
-               pool_write(backend, "H", 1);
+               if (backend->tstate == 'T')             /* are we in an explicit transaction? */
+                       pool_write(backend, "S", 1);            /* send "sync" message */
+               else
+                       pool_write(backend, "H", 1);            /* send "flush" message */
                len = htonl(sizeof(len));
                pool_write_and_flush(backend, &len, sizeof(len));
        }
@@ -2658,13 +2666,21 @@ POOL_STATUS do_query(POOL_CONNECTION *backend, char *query, POOL_SELECT_RESULT *
                {
                        case 'Z':       /* Ready for query */
                                pool_debug("do_query: Ready for query");
+
                                if (!doing_extended)
                                        return POOL_CONTINUE;
+
+                               /* If "sync" message was issued, 'Z' is expected. */
+                               if (doing_extended && backend->tstate == 'T')
+                                       state |= COMMAND_COMPLETE_RECEIVED;
                                break;
 
                        case 'C':       /* Command Complete */
                                pool_debug("do_query: Command complete received");
-                               state |= COMMAND_COMPLETE_RECEIVED;
+
+                               /* If "sync" message was issued, 'Z' is expected, else we are done with 'C'. */
+                               if (!doing_extended || backend->tstate != 'T')
+                                       state |= COMMAND_COMPLETE_RECEIVED;
                                /*
                                 * "Comand Complete" implies data row received status
                                 * if the query was SELECT.  If there's no row