pgpool2.git / summary / log / commit / refs
commit 6f35af7485732bebec33c174ca7276fe5a62fc06
Author: Tatsuo Ishii <ishii@sraoss.co.jp>
Date: Mon Jun 22 10:59:01 2020 +0000
Allow to SELECT load balance in snapshot isolation mode.
- In an read only explicit transaction
- If outside transaction, transaction mode is read only
- In other case, check if the query is really read only SELECT.
Also modify 001.load_balance test to check snapshot isolation mode.
src/context/pool_query_context.c | 115 +++++++++++++++++----
src/context/pool_session_context.c | 3 +
src/include/context/pool_session_context.h | 3 +
src/include/protocol/pool_pg_utils.h | 1 +
src/protocol/pool_pg_utils.c | 11 ++
src/protocol/pool_proto_modules.c | 27 +++--
src/test/regression/tests/001.load_balance/test.sh | 2 +-
7 files changed, 134 insertions(+), 28 deletions(-)
diff --git a/src/context/pool_query_context.c b/src/context/pool_query_context.c
index b5546d1a6..e2d9941b8 100644
--- a/src/context/pool_query_context.c
+++ b/src/context/pool_query_context.c
@@ -59,6 +59,8 @@ typedef enum
static POOL_DEST send_to_where(Node *node, char *query);
static void where_to_send_deallocate(POOL_QUERY_CONTEXT * query_context, Node *node);
static char *remove_read_write(int len, const char *contents, int *rewritten_len);
+static void set_virtual_master_node(POOL_QUERY_CONTEXT *query_context);
+static void set_load_balance_info(POOL_QUERY_CONTEXT *query_context);
/*
* Create and initialize per query session context
@@ -646,6 +648,61 @@ pool_where_to_send(POOL_QUERY_CONTEXT * query_context, char *query, Node *node)
is_select_query(node, query) &&
MAJOR(backend) == PROTO_MAJOR_V3)
{
+ /*
+ * In snapshot isolation mode, we always load balance if current
+ * transaction is read only unless load balance mode is off.
+ */
+ if (pool_config->backend_clustering_mode == CM_SNAPSHOT_ISOLATION &&
+ pool_config->load_balance_mode)
+ {
+ if (TSTATE(backend, MASTER_NODE_ID) == 'T')
+ {
+ /*
+ * We are in an explicit transaction. If the transaction is
+ * read only, we can load balance.
+ */
+ if (session_context->transaction_read_only)
+ {
+ set_load_balance_info(query_context);
+ }
+
+ /* Set virtual master node according to the where_to_send map. */
+ set_virtual_master_node(query_context);
+
+ return;
+ }
+ else if (TSTATE(backend, MASTER_NODE_ID) == 'I')
+ {
+ /*
+ * We are out side transaction. If default transaction is read only,
+ * we can load balance.
+ */
+ static char *si_query = "SELECT current_setting('transaction_read_only')";
+ POOL_SELECT_RESULT *res;
+ bool load_balance = false;
+
+ do_query(CONNECTION(backend, MASTER_NODE_ID), si_query, &res, MAJOR(backend));
+ if (res)
+ {
+ if (res->data[0] && !strcmp(res->data[0], "on"))
+ {
+ load_balance = true;
+ }
+ free_select_result(res);
+ }
+
+ per_node_statement_log(backend, MASTER_NODE_ID, si_query);
+
+ if (load_balance)
+ {
+ /* Ok, we can load balance. We are don! */
+ set_load_balance_info(query_context);
+ set_virtual_master_node(query_context);
+ return;
+ }
+ }
+ }
+
/*
* If a writing function call is used or replicate_select is true,
* we prefer to send to all nodes.
@@ -666,17 +723,7 @@ pool_where_to_send(POOL_QUERY_CONTEXT * query_context, char *query, Node *node)
!pool_is_failed_transaction() &&
pool_get_transaction_isolation() != POOL_SERIALIZABLE))
{
-
-
-
- /* load balance */
- if (pool_config->statement_level_load_balance)
- session_context->load_balance_node_id = select_load_balancing_node();
-
- session_context->query_context->load_balance_node_id = session_context->load_balance_node_id;
-
- pool_set_node_to_be_sent(query_context,
- session_context->query_context->load_balance_node_id);
+ set_load_balance_info(query_context);
}
else
{
@@ -729,14 +776,8 @@ pool_where_to_send(POOL_QUERY_CONTEXT * query_context, char *query, Node *node)
where_to_send_deallocate(query_context, node);
}
- for (i = 0; i < NUM_BACKENDS; i++)
- {
- if (query_context->where_to_send[i])
- {
- query_context->virtual_master_node_id = i;
- break;
- }
- }
+ /* Set virtual master node according to the where_to_send map. */
+ set_virtual_master_node(query_context);
return;
}
@@ -2010,3 +2051,39 @@ pool_is_transaction_read_only(Node *node)
}
return ret;
}
+
+/*
+ * Set virtual master node according to the where_to_send map.
+ */
+static void
+set_virtual_master_node(POOL_QUERY_CONTEXT *query_context)
+{
+ int i;
+
+ for (i = 0; i < NUM_BACKENDS; i++)
+ {
+ if (query_context->where_to_send[i])
+ {
+ query_context->virtual_master_node_id = i;
+ break;
+ }
+ }
+}
+
+/*
+ * Set load balance info.
+ */
+static void
+set_load_balance_info(POOL_QUERY_CONTEXT *query_context)
+{
+ POOL_SESSION_CONTEXT *session_context;
+ session_context = pool_get_session_context(false);
+
+ if (pool_config->statement_level_load_balance)
+ session_context->load_balance_node_id = select_load_balancing_node();
+
+ session_context->query_context->load_balance_node_id = session_context->load_balance_node_id;
+
+ pool_set_node_to_be_sent(query_context,
+ query_context->load_balance_node_id);
+}
diff --git a/src/context/pool_session_context.c b/src/context/pool_session_context.c
index 2db6267ce..24b83081b 100644
--- a/src/context/pool_session_context.c
+++ b/src/context/pool_session_context.c
@@ -173,6 +173,9 @@ pool_init_session_context(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * bac
/* Snapshot isolation state */
session_context->si_state = SI_NO_SNAPSHOT;
+
+ /* Transaction read only */
+ session_context->transaction_read_only = false;
}
/*
diff --git a/src/include/context/pool_session_context.h b/src/include/context/pool_session_context.h
index b6c0deb30..63dd28b80 100644
--- a/src/include/context/pool_session_context.h
+++ b/src/include/context/pool_session_context.h
@@ -292,6 +292,9 @@ typedef struct
/* Whether snapshot is aquired in this transaction. Only used by Snapshot Isolation mode. */
SI_STATE si_state;
+ /* Whether transaction is read only. Only used by Snapshot Isolation mode. */
+ SI_STATE transaction_read_only;
+
} POOL_SESSION_CONTEXT;
extern void pool_init_session_context(POOL_CONNECTION * frontend, POOL_CONNECTION_POOL * backend);
diff --git a/src/include/protocol/pool_pg_utils.h b/src/include/protocol/pool_pg_utils.h
index 765f8212a..1f82d5a14 100644
--- a/src/include/protocol/pool_pg_utils.h
+++ b/src/include/protocol/pool_pg_utils.h
@@ -55,6 +55,7 @@ extern PGVersion *Pgversion(POOL_CONNECTION_POOL * backend);
/* pool_pg_utils.c */
extern bool si_snapshot_prepared(void);
+extern bool si_snapshot_aquire_command(Node *node);
extern void si_aquire_snapshot(void);
extern void si_snapshot_aquired(void);
extern void si_commit_request(void);
diff --git a/src/protocol/pool_pg_utils.c b/src/protocol/pool_pg_utils.c
index d8719ca65..50bcd4111 100644
--- a/src/protocol/pool_pg_utils.c
+++ b/src/protocol/pool_pg_utils.c
@@ -724,6 +724,17 @@ si_snapshot_prepared(void)
return session->si_state == SI_SNAPSHOT_PREPARED;
}
+/*
+ * Reurns true if the command will aquire snapshot.
+ */
+bool
+si_snapshot_aquire_command(Node *node)
+{
+ return !is_start_transaction_query(node) &&
+ !IsA(node, VariableSetStmt) &&
+ !IsA(node, VariableShowStmt);
+}
+
/*
* Aquire snapshot
*/
diff --git a/src/protocol/pool_proto_modules.c b/src/protocol/pool_proto_modules.c
index 0ee1d1d74..2842fb69f 100644
--- a/src/protocol/pool_proto_modules.c
+++ b/src/protocol/pool_proto_modules.c
@@ -505,12 +505,14 @@ SimpleQuery(POOL_CONNECTION * frontend,
* From now on it is possible that query is actually sent to backend.
* So we need to acquire snapshot while there's no committing backend
* in snapshot isolation mode except while processing reset queries.
- * For this purpose, we send dummy "SELECT 1" to all the backend.
- * Sending actualy user's query is not possible because it might cause
- * rw-conflict, which in turn causes a deadlock.
+ * For this purpose, we send a query to know whether the transaction
+ * is READ ONLY or not. Sending actual user's query is not possible
+ * because it might cause rw-conflict, which in turn causes a
+ * deadlock.
*/
if (pool_config->backend_clustering_mode == CM_SNAPSHOT_ISOLATION &&
- !is_start_transaction_query(node) &&
+ TSTATE(backend, MASTER_NODE_ID) == 'T' &&
+ si_snapshot_aquire_command(node) &&
!si_snapshot_prepared() &&
frontend && frontend->no_forward == 0)
{
@@ -520,13 +522,19 @@ SimpleQuery(POOL_CONNECTION * frontend,
for (i = 0; i < NUM_BACKENDS; i++)
{
- static char *dummy_query = "SELECT 1";
+ static char *si_query = "SELECT current_setting('transaction_read_only')";
POOL_SELECT_RESULT *res;
- do_query(CONNECTION(backend, i), dummy_query, &res, MAJOR(backend));
+ do_query(CONNECTION(backend, i), si_query, &res, MAJOR(backend));
if (res)
+ {
+ if (res->data[0] && !strcmp(res->data[0], "on"))
+ session_context->transaction_read_only = true;
+ else
+ session_context->transaction_read_only = false;
free_select_result(res);
- per_node_statement_log(backend, i, dummy_query);
+ }
+ per_node_statement_log(backend, i, si_query);
}
si_snapshot_aquired();
@@ -712,7 +720,7 @@ SimpleQuery(POOL_CONNECTION * frontend,
* response first.
*/
if (pool_config->num_init_children == 1 &&
- pool_config->backend_clustering_mode == CM_SNAPSHOT_ISOLATION)
+ pool_config->backend_clustering_mode != CM_SNAPSHOT_ISOLATION)
{
/* Send query to all DB nodes at once */
status = pool_send_and_wait(query_context, 0, 0);
@@ -778,6 +786,7 @@ SimpleQuery(POOL_CONNECTION * frontend,
if (pool_config->backend_clustering_mode == CM_SNAPSHOT_ISOLATION)
{
si_commit_done();
+ session_context->transaction_read_only = false;
}
}
@@ -2019,6 +2028,8 @@ ReadyForQuery(POOL_CONNECTION * frontend,
internal_transaction_started)
{
si_commit_done();
+ /* reset transaction readonly flag */
+ session_context->transaction_read_only = false;
}
}
diff --git a/src/test/regression/tests/001.load_balance/test.sh b/src/test/regression/tests/001.load_balance/test.sh
index d2d3f627d..932744105 100755
--- a/src/test/regression/tests/001.load_balance/test.sh
+++ b/src/test/regression/tests/001.load_balance/test.sh
@@ -9,7 +9,7 @@ PSQL=$PGBIN/psql
# sleep time after reload in seconds
st=10
-for mode in s r
+for mode in s r i
do
rm -fr $TESTDIR
mkdir $TESTDIR
[parent: 32c63704a279]