From 02d90ab32cc88f12fb8cf3a60a001c115ffc7ae3 Mon Sep 17 00:00:00 2001 From: yuyash Date: Wed, 2 Sep 2026 20:27:19 -0700 Subject: [PATCH v4 2/2] Add test for catalog changes during fast-forward Exercise fast-forwarding over a NEW_CID record generated by an insert into a user catalog table. Keep an older transaction open and use pg_logicalinspect to verify that the catalog-changing transaction's XID is included in the serialized historic snapshot. --- .../expected/logical_inspect.out | 30 +++++++++++++++++++ .../specs/logical_inspect.spec | 17 +++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/contrib/pg_logicalinspect/expected/logical_inspect.out b/contrib/pg_logicalinspect/expected/logical_inspect.out index b343d3ad733..4a0370ae431 100644 --- a/contrib/pg_logicalinspect/expected/logical_inspect.out +++ b/contrib/pg_logicalinspect/expected/logical_inspect.out @@ -30,3 +30,33 @@ t (1 row) step s0_commit: COMMIT; + +starting permutation: s0_init s0_begin s0_get_xid s1_insert_user_catalog s1_checkpoint s1_advance_slot s1_check_fast_forward_snapshot s0_commit +step s0_init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); +?column? +-------- +init +(1 row) + +step s0_begin: BEGIN; +step s0_get_xid: SELECT pg_current_xact_id() IS NOT NULL AS xid_assigned; +xid_assigned +------------ +t +(1 row) + +step s1_insert_user_catalog: INSERT INTO user_cat VALUES (1); +step s1_checkpoint: CHECKPOINT; +step s1_advance_slot: SELECT slot_name FROM pg_replication_slot_advance('isolation_slot', pg_current_wal_lsn()); +slot_name +-------------- +isolation_slot +(1 row) + +step s1_check_fast_forward_snapshot: SELECT count(*) > 0 AS catalog_xid_tracked FROM pg_ls_logicalsnapdir() files, pg_get_logical_snapshot_info(files.name) AS info, user_cat WHERE user_cat.xmin = ANY(info.committed_xip); +catalog_xid_tracked +------------------- +t +(1 row) + +step s0_commit: COMMIT; diff --git a/contrib/pg_logicalinspect/specs/logical_inspect.spec b/contrib/pg_logicalinspect/specs/logical_inspect.spec index 26b2db10f3e..3ee8bcb0db5 100644 --- a/contrib/pg_logicalinspect/specs/logical_inspect.spec +++ b/contrib/pg_logicalinspect/specs/logical_inspect.spec @@ -6,13 +6,15 @@ setup DROP TABLE IF EXISTS tbl1; DROP TABLE IF EXISTS tbl2; CREATE TABLE tbl1 (val1 integer, val2 integer); + CREATE TABLE user_cat (val1 integer) WITH (user_catalog_table = true); CREATE EXTENSION pg_logicalinspect; } teardown { - DROP TABLE tbl1; - DROP TABLE tbl2; + DROP TABLE IF EXISTS tbl1; + DROP TABLE IF EXISTS tbl2; + DROP TABLE IF EXISTS user_cat; SELECT 'stop' FROM pg_drop_replication_slot('isolation_slot'); DROP EXTENSION pg_logicalinspect; } @@ -21,6 +23,7 @@ session "s0" setup { SET synchronous_commit=on; } step "s0_init" { SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); } step "s0_begin" { BEGIN; } +step "s0_get_xid" { SELECT pg_current_xact_id() IS NOT NULL AS xid_assigned; } step "s0_savepoint" { SAVEPOINT sp1; } step "s0_truncate" { TRUNCATE tbl1; } step "s0_commit" { COMMIT; } @@ -29,9 +32,12 @@ session "s1" setup { SET synchronous_commit=on; } step "s1_checkpoint" { CHECKPOINT; } step "s1_create_table" { CREATE TABLE tbl2 (val1 integer, val2 integer); } +step "s1_insert_user_catalog" { INSERT INTO user_cat VALUES (1); } +step "s1_advance_slot" { SELECT slot_name FROM pg_replication_slot_advance('isolation_slot', pg_current_wal_lsn()); } step "s1_get_changes" { SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0'); } step "s1_check_snapshot_meta" { SELECT count(meta.*) > 0 AS has_meta from pg_ls_logicalsnapdir(), pg_get_logical_snapshot_meta(name) as meta; } step "s1_check_snapshot_info" { SELECT count(*) > 0 as has_info FROM pg_ls_logicalsnapdir(), pg_get_logical_snapshot_info(name) AS info where info.catchange_count >= 2 and array_length(info.catchange_xip,1) >= 2 and info.committed_count >= 1 and array_length(info.committed_xip,1) >= 1; } +step "s1_check_fast_forward_snapshot" { SELECT count(*) > 0 AS catalog_xid_tracked FROM pg_ls_logicalsnapdir() files, pg_get_logical_snapshot_info(files.name) AS info, user_cat WHERE user_cat.xmin = ANY(info.committed_xip); } # Both s0 and s1 execute catalog-change transactions. When "s1_get_changes" is @@ -44,3 +50,10 @@ step "s1_check_snapshot_info" { SELECT count(*) > 0 as has_info FROM pg_ls_logic # least the expected number of transactions, accounting for potential # additional catalog changes that may occur due to concurrent autoanalyze. permutation "s0_init" "s0_begin" "s0_savepoint" "s0_truncate" "s1_create_table" "s1_checkpoint" "s1_get_changes" "s1_check_snapshot_info" "s1_check_snapshot_meta" "s0_commit" + +# An INSERT into a user catalog table emits NEW_CID without a cache +# invalidation. Keep an older transaction open so that the INSERT's XID +# remains in the historic snapshot's interesting range. Advancing the slot +# fast-forwards over the NEW_CID and serializes a snapshot at the checkpoint. +# The committed XID must be present in that saved snapshot. +permutation "s0_init" "s0_begin" "s0_get_xid" "s1_insert_user_catalog" "s1_checkpoint" "s1_advance_slot" "s1_check_fast_forward_snapshot" "s0_commit" -- 2.54.0 (Apple Git-157)