From: Bertrand Drouvot Date: Mon, 8 Jun 2026 07:05:35 +0000 Subject: [PATCH v1 4/4] Add SQL-path test for read_local_xlog_page_guts() timeline race Extend the injection-point test in 035_standby_logical_decoding.pl to also cover the SQL-callable decoding path (pg_logical_slot_peek_changes) which exercises read_local_xlog_page_guts(). The test uses the same promotion and injection point as the walsender test: a background psql session is connected before promotion, then issues pg_logical_slot_peek_changes while startup is paused at the injection point. Author: Bertrand Drouvot Reviewed-by: Discussion: https://postgr.es/m/7daef094-abf3-4672-bc23-3df4763b16a3%40gmail.com --- .../t/035_standby_logical_decoding.pl | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) 100.0% src/test/recovery/t/ diff --git a/src/test/recovery/t/035_standby_logical_decoding.pl b/src/test/recovery/t/035_standby_logical_decoding.pl index ce80123844d..146c33ce39d 100644 --- a/src/test/recovery/t/035_standby_logical_decoding.pl +++ b/src/test/recovery/t/035_standby_logical_decoding.pl @@ -1068,11 +1068,13 @@ is($cascading_stdout, $expected, # timeline in this window. ################################################## -# Create a logical slot on the cascading standby for this test. +# Create logical slots on the cascading standby for this test. $node_cascading_standby->create_logical_slot_on_standby($node_standby, 'race_slot', 'testdb'); +$node_cascading_standby->create_logical_slot_on_standby($node_standby, + 'race_slot_sql', 'testdb'); -# Insert data so the slot has WAL to decode. +# Insert data so the slots have WAL to decode. $node_standby->safe_psql('testdb', qq[INSERT INTO decoding_test(x,y) SELECT s, s::text FROM generate_series(10,13) s;] ); @@ -1082,6 +1084,9 @@ $node_standby->wait_for_replay_catchup($node_cascading_standby); $node_standby->safe_psql('testdb', 'CREATE EXTENSION injection_points;'); $node_standby->wait_for_replay_catchup($node_cascading_standby); +# Open a background psql session BEFORE promotion for the SQL decoding test. +my $decode_session = $node_cascading_standby->background_psql('testdb'); + # Attach injection point to pause startup after WAL segment cleanup # but before RecoveryInProgress() flips to false. $node_cascading_standby->safe_psql('testdb', @@ -1112,6 +1117,14 @@ my $handle2 = IPC::Run::start( '2>' => \$stderr2, IPC::Run::timeout($default_timeout)); +# Issue SQL decoding (read_local_xlog_page_guts path) on the pre-connected +# session. +$decode_session->query_until( + qr/decoding_started/, qq( + \\echo decoding_started + SELECT count(*) FROM pg_logical_slot_peek_changes('race_slot_sql', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); +)); + # Wait for the walsender to acquire the slot. $node_cascading_standby->poll_query_until('testdb', qq[SELECT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'race_slot' AND active_pid IS NOT NULL)] @@ -1121,9 +1134,18 @@ $node_cascading_standby->poll_query_until('testdb', $node_cascading_standby->safe_psql('testdb', "SELECT injection_points_wakeup('promotion-after-wal-segment-cleanup');"); -# Verify pg_recvlogical successfully decodes the data. +# Verify pg_recvlogical successfully decodes the data (walsender path). $pump_timeout = IPC::Run::timer($default_timeout); ok( pump_until($handle2, $pump_timeout, \$stdout2, qr/COMMIT/s), 'pg_recvlogical works during promotion timeline switch'); +# Verify SQL decoding returns a numeric result (read_local_xlog_page_guts path). +my $sql_timeout = IPC::Run::timer($default_timeout); +ok( pump_until( + $decode_session->{run}, $sql_timeout, + \$decode_session->{stdout}, qr/\d+/m), + 'pg_logical_slot_peek_changes works during promotion timeline switch'); + +$decode_session->quit; + done_testing(); -- 2.34.1 --HitlKQoyB8givWgr--