1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-12 16:21:30 +03:00
postgres/contrib/test_decoding/specs/skip_snapshot_restore.spec
Masahiko Sawada 068674f4ab Fix possibility of logical decoding partial transaction changes.
When creating and initializing a logical slot, the restart_lsn is set
to the latest WAL insertion point (or the latest replay point on
standbys). Subsequently, WAL records are decoded from that point to
find the start point for extracting changes in the
DecodingContextFindStartpoint() function. Since the initial
restart_lsn could be in the middle of a transaction, the start point
must be a consistent point where we won't see the data for partial
transactions.

Previously, when not building a full snapshot, serialized snapshots
were restored, and the SnapBuild jumps to the consistent state even
while finding the start point. Consequently, the slot's restart_lsn
and confirmed_flush could be set to the middle of a transaction. This
could lead to various unexpected consequences. Specifically, there
were reports of logical decoding decoding partial transactions, and
assertion failures occurred because only subtransactions were decoded
without decoding their top-level transaction until decoding the commit
record.

To resolve this issue, the changes prevent restoring the serialized
snapshot and jumping to the consistent state while finding the start
point.

On v17 and HEAD, a flag indicating whether snapshot restores should be
skipped has been added to the SnapBuild struct, and SNAPBUILD_VERSION
has been bumpded.

On backbranches, the flag is stored in the LogicalDecodingContext
instead, preserving on-disk compatibility.

Backpatch to all supported versions.

Reported-by: Drew Callahan
Reviewed-by: Amit Kapila, Hayato Kuroda
Discussion: https://postgr.es/m/2444AA15-D21B-4CCE-8052-52C7C2DAFE5C%40amazon.com
Backpatch-through: 12
2024-07-11 22:48:21 +09:00

47 lines
2.1 KiB
Ruby

# Test that a slot creation skips to restore serialized snapshot to reach
# the consistent state.
setup
{
DROP TABLE IF EXISTS tbl;
CREATE TABLE tbl (val1 integer);
}
teardown
{
DROP TABLE tbl;
SELECT 'stop' FROM pg_drop_replication_slot('slot0');
SELECT 'stop' FROM pg_drop_replication_slot('slot1');
}
session "s0"
setup { SET synchronous_commit = on; }
step "s0_init" { SELECT 'init' FROM pg_create_logical_replication_slot('slot0', 'test_decoding'); }
step "s0_begin" { BEGIN; }
step "s0_insert1" { INSERT INTO tbl VALUES (1); }
step "s0_insert2" { INSERT INTO tbl VALUES (2); }
step "s0_commit" { COMMIT; }
session "s1"
setup { SET synchronous_commit = on; }
step "s1_init" { SELECT 'init' FROM pg_create_logical_replication_slot('slot1', 'test_decoding'); }
step "s1_get_changes_slot0" { SELECT data FROM pg_logical_slot_get_changes('slot0', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0'); }
step "s1_get_changes_slot1" { SELECT data FROM pg_logical_slot_get_changes('slot1', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0'); }
session "s2"
setup { SET synchronous_commit = on ;}
step "s2_checkpoint" { CHECKPOINT; }
step "s2_get_changes_slot0" { SELECT data FROM pg_logical_slot_get_changes('slot0', NULL, NULL, 'skip-empty-xacts', '1', 'include-xids', '0'); }
# While 'slot1' creation by "s1_init" waits for s0-transaction to commit, the
# RUNNING_XACTS record is written by "s2_checkpoint" and "s2_get_changes_slot1"
# serializes consistent snapshots to the disk at LSNs where are before
# s0-transaction's commit. After s0-transaction commits, "s1_init" resumes but
# must not restore any serialized snapshots and will reach the consistent state
# when decoding a RUNNING_XACT record generated after s0-transaction's commit.
# We check if the get_changes on 'slot1' will not return any s0-transaction's
# changes as its confirmed_flush_lsn will be after the s0-transaction's commit
# record.
permutation "s0_init" "s0_begin" "s0_insert1" "s1_init" "s2_checkpoint" "s2_get_changes_slot0" "s0_insert2" "s0_commit" "s1_get_changes_slot0" "s1_get_changes_slot1"