pgpool2.git / summary / log / commit / refs
commit 9f977ccbff9d844594ff4cf092fccdc41b2a2fba
Author: Tatsuo Ishii <ishii at sraoss.co.jp>
Date: Wed Jul 22 08:26:56 2009 +0000
Query cancel by statement timeout does not generate "kind mismatch"
error any more.
How to reproduce the test case:
Directory connect to PostgreSQL.
T1: BEGIN;
T1: LOCK TABLE t1;
T2: SET statement_timeout TO 5000;
T2: UPDATE t1 SET ...
UPDATE query aborts and error message comes out on T2:
ERROR: canceling statement due to statement timeout
A side effect of this is, statement timeout works correctly (rather
than statement timeout value * # of node).
Note that to initiate T1 by directory connecting to slave node will not
work (still results in kind mismatch error). But I don't think this could
happend in the real world.
Also refactor error detecting modules (detect_deadlock_error and so on).
pool_process_query.c | 11 ++-
pool_proto_modules.c | 222 ++++++++++++++++++++++++++-------------------------
pool_proto_modules.h | 1 +
3 files changed, 124 insertions(+), 110 deletions(-)
diff --git a/pool_process_query.c b/pool_process_query.c
index 7f15c6629..15ab27c1f 100644
--- a/pool_process_query.c
+++ b/pool_process_query.c
@@ -54,6 +54,7 @@
#define ACTIVE_SQL_TRANSACTION_ERROR_CODE "25001" /* SET TRANSACTION ISOLATION LEVEL must be called before any query */
#define DEADLOCK_ERROR_CODE "40P01"
#define SERIALIZATION_FAIL_ERROR_CODE "40001"
+#define QUERY_CANCEL_ERROR_CODE "57014"
#define ADMIN_SHUTDOWN_ERROR_CODE "57P01"
#define CRASH_SHUTDOWN_ERROR_CODE "57P02"
@@ -3593,7 +3594,7 @@ POOL_STATUS read_kind_from_backend(POOL_CONNECTION *frontend, POOL_CONNECTION_PO
/* initialize degenerate record */
degenerate_node[i] = 0;
- /* kind is singed char.
+ /* kind is signed char.
* We must check negative number.
*/
int id = kind_list[i] + 128;
@@ -4250,6 +4251,14 @@ int detect_serialization_error(POOL_CONNECTION *backend, int major)
return r;
}
+int detect_query_cancel_error(POOL_CONNECTION *backend, int major)
+{
+ int r = detect_error(backend, QUERY_CANCEL_ERROR_CODE, major, 'E', true);
+ if (r == SPECIFIED_ERROR)
+ pool_debug("detect_query_cancel_error: received query cancel error message from backend");
+ return r;
+}
+
/*
* detect_error: Detect specified error from error code.
*/
diff --git a/pool_proto_modules.c b/pool_proto_modules.c
index 592c5aea6..8676ea1ea 100644
--- a/pool_proto_modules.c
+++ b/pool_proto_modules.c
@@ -46,6 +46,7 @@
#include "pool.h"
#include "pool_signal.h"
#include "pool_proto_modules.h"
+#include "parser/pool_string.h"
int force_replication;
int replication_was_enabled; /* replication mode was enabled */
@@ -87,6 +88,9 @@ char query_string_buffer[QUERY_STRING_BUFFER_LEN];
*/
char *parsed_query = NULL;
+static int check_errors(POOL_CONNECTION_POOL *backend, int backend_id);
+static void generate_error_message(char *prefix, int specific_error, char *query);
+
POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
POOL_CONNECTION_POOL *backend)
{
@@ -137,9 +141,7 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
List *parse_tree_list;
Node *node = NULL, *node1;
POOL_STATUS status;
- int serialization_error_detected = 0;
- int deadlock_detected = 0;
- int active_sql_transaction_error = 0;
+ int specific_error;
POOL_MEMORY_POOL *old_context = NULL;
Portal *portal;
@@ -541,68 +543,17 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
return POOL_END;
}
- /*
- * Check dead lock error on the master node and abort
- * transactions on all nodes if so.
- */
- deadlock_detected = detect_deadlock_error(MASTER(backend), MAJOR(backend));
- if (deadlock_detected < 0)
- return POOL_END;
-
- /*
- * Check serialization failure error and abort
- * transactions on all nodes if so. Otherwise we allow
- * data inconsistency among DB nodes. See following
- * scenario: (M:master, S:slave)
- *
- * M:S1:BEGIN;
- * M:S2:BEGIN;
- * S:S1:BEGIN;
- * S:S2:BEGIN;
- * M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * M:S2:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * S:S2:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * M:S1:UPDATE t1 SET i = i + 1;
- * S:S1:UPDATE t1 SET i = i + 1;
- * M:S2:UPDATE t1 SET i = i + 1; <-- blocked
- * S:S1:COMMIT;
- * M:S1:COMMIT;
- * M:S2:ERROR: could not serialize access due to concurrent update
- * S:S2:UPDATE t1 SET i = i + 1; <-- success in UPDATE and data becomes inconsistent!
- */
- serialization_error_detected = detect_serialization_error(MASTER(backend), MAJOR(backend));
- if (serialization_error_detected < 0)
- return POOL_END;
-
- /*
- * check "SET TRANSACTION ISOLATION LEVEL must be called before any query" error.
- * This happens in following scenario:
- *
- * M:S1:BEGIN;
- * S:S1:BEGIN;
- * M:S1:SELECT 1; <-- only sent to MASTER
- * M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * M: <-- error
- * S: <-- ok since no previous SELECT is sent. kind mismatch error occurs!
- */
- active_sql_transaction_error = detect_active_sql_transaction_error(MASTER(backend), MAJOR(backend));
- if (active_sql_transaction_error < 0)
- return POOL_END;
- }
+ /* Check specific errors */
+ specific_error = check_errors(backend, MASTER_NODE_ID);
+ if (specific_error)
+ {
+ /* log error message */
+ generate_error_message("SimpleQuery: ", specific_error, string);
- if (deadlock_detected == SPECIFIED_ERROR || serialization_error_detected == SPECIFIED_ERROR ||
- active_sql_transaction_error == SPECIFIED_ERROR)
- {
- if (deadlock_detected == SPECIFIED_ERROR)
- pool_log("SimpleQuery: received deadlock error message from master node. query: %s", string);
- else if (serialization_error_detected == SPECIFIED_ERROR)
- pool_log("SimpleQuery: received serialization failure error message from master node. query: %s", string);
- else
- pool_log("SimpleQuery: received SET TRANSACTION ISOLATION LEVEL must be called before any query error. query: %s", string);
- string = POOL_ERROR_QUERY;
- len = strlen(string) + 1;
+ /* Set error query to abort transactions on other nodes */
+ string = POOL_ERROR_QUERY;
+ len = strlen(string) + 1;
+ }
}
/* send query to other than master nodes */
@@ -641,7 +592,7 @@ POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend,
if (send_simplequery_message(MASTER(backend), len, string, MAJOR(backend)) != POOL_CONTINUE)
return POOL_END;
- if (wait_for_query_response(frontend, CONNECTION(backend, i), string) != POOL_CONTINUE)
+ if (wait_for_query_response(frontend, MASTER(backend), string) != POOL_CONTINUE)
{
/* Cancel current transaction */
CancelPacket cancel_packet;
@@ -695,10 +646,8 @@ POOL_STATUS Execute(POOL_CONNECTION *frontend,
Portal *portal;
char *string1;
PrepareStmt *p_stmt;
- int deadlock_detected = 0;
- int serialization_error_detected = 0;
- int active_sql_transaction_error = 0;
POOL_STATUS ret;
+ int specific_error = 0;
/* read Execute packet */
if (pool_read(frontend, &len, sizeof(len)) < 0)
@@ -807,39 +756,14 @@ POOL_STATUS Execute(POOL_CONNECTION *frontend,
return POOL_END;
}
- /*
- * Check dead lock error on the master node and abort
- * transactions on all nodes if so.
- */
- deadlock_detected = detect_deadlock_error(MASTER(backend), MAJOR(backend));
- if (deadlock_detected < 0)
- return POOL_END;
-
- /*
- * Check serialization failure error and abort all nodes
- * if so. Otherwise we allow data inconsistency among DB
- * nodes. See following scenario: (M:master, S:slave)
- */
- serialization_error_detected = detect_serialization_error(MASTER(backend), MAJOR(backend));
- if (serialization_error_detected < 0)
- return POOL_END;
-
- /*
- * check "SET TRANSACTION ISOLATION LEVEL must be called before any query" error.
- * This happens in following scenario:
- *
- * M:S1:BEGIN;
- * S:S1:BEGIN;
- * M:S1:SELECT 1; <-- only sent to MASTER
- * M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
- * M: <-- error
- * S: <-- ok since no previous SELECT is sent. kind mismatch error occurs!
- */
- active_sql_transaction_error = detect_active_sql_transaction_error(MASTER(backend), MAJOR(backend));
- if (active_sql_transaction_error < 0)
- return POOL_END;
+ /* Check specific errors */
+ specific_error = check_errors(backend, MASTER_NODE_ID);
+ if (specific_error)
+ {
+ /* log error message */
+ generate_error_message("Execute: ", specific_error, string);
+ }
}
/* send query to other nodes */
@@ -847,19 +771,12 @@ POOL_STATUS Execute(POOL_CONNECTION *frontend,
{
if (!VALID_BACKEND(i) || IS_MASTER_NODE_ID(i))
continue;
- if (deadlock_detected == SPECIFIED_ERROR || serialization_error_detected == SPECIFIED_ERROR ||
- active_sql_transaction_error == SPECIFIED_ERROR)
+
+ if (specific_error)
{
char msg[1024] = "pgpoool_error_portal"; /* large enough */
int len = strlen(msg);
- if (deadlock_detected == SPECIFIED_ERROR)
- pool_log("Execute: received deadlock error message from master node. query: %s", string);
- else if (serialization_error_detected == SPECIFIED_ERROR)
- pool_log("SimpleQuery: received serialization failure error message from master node. query: %s", string);
- else
- pool_log("SimpleQuery: received SET TRANSACTION ISOLATION LEVEL must be called before any query error. query: %s", string);
-
memset(msg + len, 0, sizeof(int));
if (send_execute_message(backend, i, len + 5, msg))
return POOL_END;
@@ -2369,3 +2286,90 @@ POOL_STATUS EmptyQueryResponse(POOL_CONNECTION *frontend,
pool_write(frontend, "I", 1);
return pool_write_and_flush(frontend, "", 1);
}
+
+/*
+ * Check various errors from backend. return values: 0: no error 1:
+ * deadlock detected 2: serialization error detected 3: query cancel
+ * detected: 4
+ */
+static int check_errors(POOL_CONNECTION_POOL *backend, int backend_id)
+{
+
+ /*
+ * Check dead lock error on the master node and abort
+ * transactions on all nodes if so.
+ */
+ if (detect_deadlock_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
+ return 1;
+
+ /*
+ * Check serialization failure error and abort
+ * transactions on all nodes if so. Otherwise we allow
+ * data inconsistency among DB nodes. See following
+ * scenario: (M:master, S:slave)
+ *
+ * M:S1:BEGIN;
+ * M:S2:BEGIN;
+ * S:S1:BEGIN;
+ * S:S2:BEGIN;
+ * M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ * M:S2:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ * S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ * S:S2:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ * M:S1:UPDATE t1 SET i = i + 1;
+ * S:S1:UPDATE t1 SET i = i + 1;
+ * M:S2:UPDATE t1 SET i = i + 1; <-- blocked
+ * S:S1:COMMIT;
+ * M:S1:COMMIT;
+ * M:S2:ERROR: could not serialize access due to concurrent update
+ * S:S2:UPDATE t1 SET i = i + 1; <-- success in UPDATE and data becomes inconsistent!
+ */
+ if (detect_serialization_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
+ return 2;
+
+ /*
+ * check "SET TRANSACTION ISOLATION LEVEL must be called before any query" error.
+ * This happens in following scenario:
+ *
+ * M:S1:BEGIN;
+ * S:S1:BEGIN;
+ * M:S1:SELECT 1; <-- only sent to MASTER
+ * M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ * S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
+ * M: <-- error
+ * S: <-- ok since no previous SELECT is sent. kind mismatch error occurs!
+ */
+ if (detect_active_sql_transaction_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
+ return 3;
+
+ /* check query cancel error */
+ if (detect_query_cancel_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
+ return 4;
+
+ return 0;
+}
+
+static void generate_error_message(char *prefix, int specific_error, char *query)
+{
+ static char *error_messages[] = {
+ "received deadlock error message from master node. query: %s",
+ "received serialization failure error message from master node. query: %s",
+ "received SET TRANSACTION ISOLATION LEVEL must be called before any query error. query: %s",
+ "received query cancel error message from master node. query: %s"
+ };
+
+ String *msg;
+
+ if (specific_error < 1 || specific_error > sizeof(error_messages)/sizeof(char *))
+ {
+ pool_error("generate_error_message: invalid specific_error: %d", specific_error);
+ return;
+ }
+
+ specific_error--;
+
+ msg = init_string(prefix);
+ string_append_char(msg, error_messages[specific_error]);
+ pool_error(msg->data, query);
+ free_string(msg);
+}
diff --git a/pool_proto_modules.h b/pool_proto_modules.h
index 2e444e98b..9f9f2456d 100644
--- a/pool_proto_modules.h
+++ b/pool_proto_modules.h
@@ -164,6 +164,7 @@ extern POOL_STATUS end_internal_transaction(POOL_CONNECTION_POOL *backend);
extern int detect_deadlock_error(POOL_CONNECTION *master, int major);
extern int detect_serialization_error(POOL_CONNECTION *master, int major);
extern int detect_active_sql_transaction_error(POOL_CONNECTION *backend, int major);
+extern int detect_query_cancel_error(POOL_CONNECTION *backend, int major);
extern bool is_partition_table(POOL_CONNECTION_POOL *backend, Node *node);
extern POOL_STATUS pool_discard_packet(POOL_CONNECTION_POOL *cp);
[parent: 1c18c6f2c791]