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]