pgpool2.git / summary / log / commit / refs
commit aaec285ed8b9f684db0e87921e00ac97776fcfa6
Author: Tatsuo Ishii <ishii at sraoss.co.jp>
Date: Sat Dec 13 08:13:16 2008 +0000
Avoid kind mismatch error caused by
"SET TRANSACTION ISOLATION LEVEL must be called before any query".
This could happen 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!
Fix ReadyForQuery() by calling pool_unread if backend after
do_error_command executed not returns read for query packet.
Fix comments
pool_process_query.c | 71 +++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 57 insertions(+), 14 deletions(-)
diff --git a/pool_process_query.c b/pool_process_query.c
index bd7aa8794..1247c4074 100644
--- a/pool_process_query.c
+++ b/pool_process_query.c
@@ -55,6 +55,7 @@
#define INIT_STATEMENT_LIST_SIZE 8
+#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 ADMIN_SHUTDOWN_ERROR_CODE "57P01"
@@ -232,6 +233,7 @@ static int detect_error(POOL_CONNECTION *master, char *error_code, int major, bo
static int detect_deadlock_error(POOL_CONNECTION *master, int major);
static int detect_serialization_error(POOL_CONNECTION *master, int major);
static int detect_postmaster_down_error(POOL_CONNECTION *master, int major);
+static int detect_active_sql_transaction_error(POOL_CONNECTION *backend, int major);
static bool is_partition_table(POOL_CONNECTION_POOL *backend, Node *node);
static POOL_STATUS pool_discard_packet(POOL_CONNECTION_POOL *cp);
@@ -410,6 +412,9 @@ POOL_STATUS pool_process_query(POOL_CONNECTION *frontend,
{
if (VALID_BACKEND(i) && FD_ISSET(CONNECTION(backend, i)->fd, &readmask))
{
+ /*
+ * admin shutdown postmaster or postmaster goes down
+ */
if (detect_postmaster_down_error(CONNECTION(backend, i), MAJOR(backend)) == SPECIFIED_ERROR)
{
/* detach backend node. */
@@ -420,7 +425,6 @@ POOL_STATUS pool_process_query(POOL_CONNECTION *frontend,
sleep(5);
break;
}
-
status = read_kind_from_backend(frontend, backend, &kind);
if (status != POOL_CONTINUE)
return status;
@@ -935,6 +939,7 @@ static POOL_STATUS SimpleQuery(POOL_CONNECTION *frontend,
POOL_STATUS status;
int serialization_error_detected = 0;
int deadlock_detected = 0;
+ int active_sql_transaction_error = 0;
force_replication = 0;
if (query == NULL) /* need to read query from frontend? */
@@ -1292,19 +1297,18 @@ static POOL_STATUS SimpleQuery(POOL_CONNECTION *frontend,
return POOL_END;
/*
- * We must check deadlock error because a aborted transaction
- * by detecting deadlock isn't same on all nodes.
- * If a transaction is aborted on master node, pgpool send a
- * error query to another nodes.
+ * 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)
+ * 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;
@@ -1325,14 +1329,33 @@ static POOL_STATUS SimpleQuery(POOL_CONNECTION *frontend,
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;
}
- if (deadlock_detected == SPECIFIED_ERROR || serialization_error_detected == SPECIFIED_ERROR)
+
+ 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
+ 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;
}
@@ -1927,8 +1950,8 @@ static POOL_STATUS Sync(POOL_CONNECTION *frontend,
/*
* Process ReadyForQuery('Z') message.
*
- * - if the global error status "mismatch_ntuples" is set, send a error query
- * to all DB nodes
+ * - if the global error status "mismatch_ntuples" is set, send an error query
+ * to all DB nodes to abort transaction.
* - internal transaction is closed
*/
static POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
@@ -1981,6 +2004,16 @@ static POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
if (kind == 'Z')
break;
+ /* put the message back to read buffer */
+ for (i=0;i<NUM_BACKENDS;i++)
+ {
+ if (VALID_BACKEND(i))
+ {
+ pool_unread(CONNECTION(backend,i), &kind, 1);
+ }
+ }
+
+ /* discard rest of the packet */
if (pool_discard_packet(backend) != POOL_CONTINUE)
{
pool_error("ReadyForQuery: pool_discard_packet failed");
@@ -5886,6 +5919,16 @@ static int detect_postmaster_down_error(POOL_CONNECTION *backend, int major)
return r;
}
+static int detect_active_sql_transaction_error(POOL_CONNECTION *backend, int major)
+{
+ int r = detect_error(backend, ACTIVE_SQL_TRANSACTION_ERROR_CODE, major, true);
+ if (r == SPECIFIED_ERROR)
+ {
+ pool_debug("detect_active_sql_transaction_error: receive SET TRANSACTION ISOLATION LEVEL must be called before any query error from a node.");
+ }
+ return r;
+}
+
static int detect_deadlock_error(POOL_CONNECTION *backend, int major)
{
int r = detect_error(backend, DEADLOCK_ERROR_CODE, major, true);
@@ -5923,7 +5966,7 @@ static int detect_error(POOL_CONNECTION *backend, char *error_code, int major, b
/* ErrorResponse or NoticeResponse message? */
if (kind == 'E' || kind == 'N')
{
- /* read actual query */
+ /* read actual message */
if (major == PROTO_MAJOR_V3)
{
char *e;
[parent: 18ed79f98b47]