From 45007620d716acf0831cd780c807eee3e8e59a25 Mon Sep 17 00:00:00 2001 From: Taiki Koshino Date: Sat, 19 Sep 2026 03:22:25 +0900 Subject: [PATCH v2] Preserve dml_adaptive state on ROLLBACK TO SAVEPOINT. dml_adaptive() used is_commit_or_rollback_query() to detect the end of a transaction. However, this function also returns true for ROLLBACK TO SAVEPOINT, which does not end the transaction. Consequently, ROLLBACK TO SAVEPOINT cleared is_in_transaction and the transaction write list. Subsequent SELECTs on previously modified tables could then be load balanced to a standby and return stale data. Exclude ROLLBACK TO SAVEPOINT from the transaction-end branch and keep the accumulated write list until COMMIT or ROLLBACK. Author: zourenli Reviewed-by: Taiki Koshino Discussion: https://github.com/pgpool/pgpool2/issues/173 Backpatch-through: v4.3 --- src/context/pool_query_context.c | 7 +++- .../032.dml_adaptive_load_balance/test.sh | 41 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/context/pool_query_context.c b/src/context/pool_query_context.c index 16ac34825..b9bfe3229 100644 --- a/src/context/pool_query_context.c +++ b/src/context/pool_query_context.c @@ -2003,7 +2003,12 @@ dml_adaptive(Node *node, char *query) session_context->transaction_temp_write_oid_list = NIL; session_context->transaction_temp_write_dboid = 0; } - else if (is_commit_or_rollback_query(node)) + /* + * Only COMMIT and normal ROLLBACK query. + * ROLLBACK TO SAVEPOINT must preserve the transaction state + * and accumulated write list. + */ + else if (is_commit_query(node) || is_rollback_query(node)) { session_context->is_in_transaction = false; diff --git a/src/test/regression/tests/032.dml_adaptive_load_balance/test.sh b/src/test/regression/tests/032.dml_adaptive_load_balance/test.sh index 5692249fc..98885dc5d 100755 --- a/src/test/regression/tests/032.dml_adaptive_load_balance/test.sh +++ b/src/test/regression/tests/032.dml_adaptive_load_balance/test.sh @@ -37,6 +37,7 @@ CREATE TABLE t1(i INTEGER); CREATE TABLE t2(i INTEGER); CREATE TABLE t3(i INTEGER); CREATE TABLE tF(i INTEGER); +CREATE TABLE t_savepoint(i INTEGER); CREATE FUNCTION f1(INTEGER) returns INTEGER AS 'SELECT \$1' LANGUAGE SQL; EOF # wait for the newly created tables replicated @@ -142,6 +143,46 @@ EOF fi echo ok: T2 final read was load balanced. + +# check if ROLLBACK TO SAVEPOINT preserves the transaction write list +$PSQL -X -At -v ON_ERROR_STOP=1 test > result.txt </dev/null 2>&1 + if [ $? != 0 ];then + echo fail: update before SAVEPOINT was not visible. + ./shutdownall + exit 1 + fi + + grep -F "SELECT sum(i), 'QUERY ID T3-1' FROM t_savepoint;" log/pgpool.log |grep "DB node id: 0">/dev/null 2>&1 + if [ $? != 0 ];then + echo fail: read after ROLLBACK TO SAVEPOINT was not sent to primary. + ./shutdownall + exit 1 + fi + + grep -F "SELECT sum(i), 'QUERY ID T3-1' FROM t_savepoint;" log/pgpool.log |grep "DB node id: 1">/dev/null 2>&1 + if [ $? = 0 ];then + echo fail: read after ROLLBACK TO SAVEPOINT was wrongly load balanced. + ./shutdownall + exit 1 + fi + echo ok: ROLLBACK TO SAVEPOINT preserved the transaction write list. + ./shutdownall cd .. done -- 2.52.0