Thread
Commits
-
Fix second race with timeline selection during promotion
- d9b49e5b4fc2 16 (unreleased) landed
- ab5334d8bfa2 17 (unreleased) landed
- 4bff3aa51c19 18 (unreleased) landed
- a8ee70bd5e00 19 (unreleased) landed
-
Remove TAP test for timeline lookup race with logical decoding on standbys
- c011d5b65457 17 (unreleased) landed
-
Fix race with timeline selection in logical decoding during promotion
- 8cd687c44b62 16 (unreleased) landed
- 16b89ff04839 17 (unreleased) landed
- b4bd1385043c 18 (unreleased) landed
- eb4e7224a1c6 19 (unreleased) landed
-
Backport GetWALInsertionTimeLineIfSet()
- 2a04624daa32 16 (unreleased) landed
-
TAP test for logical decoding on standby
- fcd77d53217b 16.0 cited
-
t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Alexander Law <exclusion@gmail.com> — 2026-06-06T09:00:00Z
Hello hackers, I'd like to present a recent failure of 035_standby_logical_decoding on buildfarm [1]: ### Promoting node "standby" # Running: pg_ctl --pgdata /home/bf/bf-build/scorpion/HEAD/pgsql.build/testrun/recovery/035_standby_logical_decoding/data/t_035_standby_logical_decoding_standby_data/pgdata --log /home/bf/bf-build/scorpion/HEAD/pgsql.build/testrun/recovery/035_standby_logical_decoding/log/035_standby_logical_decoding_standby.log promote waiting for server to promote.... done server promoted Waiting for replication conn cascading_standby's replay_lsn to pass 0/0648A290 on standby done [10:40:16.996](0.446s) ok 84 - got expected output from SQL decoding session on promoted standby [10:40:16.997](0.001s) # pump_until: process terminated unexpectedly when searching for "(?^s:^.*COMMIT.*COMMIT$)" with stream: "" [10:40:16.997](0.000s) not ok 85 - got 2 COMMIT from pg_recvlogical output [10:40:16.997](0.000s) # Failed test 'got 2 COMMIT from pg_recvlogical output' # at /home/bf/bf-build/scorpion/HEAD/pgsql/src/test/recovery/t/035_standby_logical_decoding.pl line 1036. [10:40:16.997](0.000s) not ok 86 - got same expected output from pg_recvlogical decoding session [10:40:16.997](0.000s) # Failed test 'got same expected output from pg_recvlogical decoding session' # at /home/bf/bf-build/scorpion/HEAD/pgsql/src/test/recovery/t/035_standby_logical_decoding.pl line 1040. [10:40:16.998](0.000s) # got: '' # expected: 'BEGIN # table public.decoding_test: INSERT: x[integer]:1 y[text]:'1' # table public.decoding_test: INSERT: x[integer]:2 y[text]:'2' # table public.decoding_test: INSERT: x[integer]:3 y[text]:'3' # table public.decoding_test: INSERT: x[integer]:4 y[text]:'4' # COMMIT # BEGIN # table public.decoding_test: INSERT: x[integer]:5 y[text]:'5' # table public.decoding_test: INSERT: x[integer]:6 y[text]:'6' # table public.decoding_test: INSERT: x[integer]:7 y[text]:'7' # COMMIT' [10:40:17.009](0.011s) ok 87 - got expected output from SQL decoding session on cascading standby [10:40:17.009](0.000s) ok 88 - got 2 COMMIT from pg_recvlogical output 035_standby_logical_decoding_standby.log contains: 2026-06-03 10:40:16.745 CEST [3955206][startup][29/0:0] LOG: received promote request ... 2026-06-03 10:40:16.784 CEST [3955206][startup][29/0:0] LOG: archive recovery complete 2026-06-03 10:40:16.862 CEST [3963805][walsender][25/0:0] LOG: starting logical decoding for slot "promotion_activeslot" 2026-06-03 10:40:16.862 CEST [3963805][walsender][25/0:0] DETAIL: Streaming transactions committing after 0/06487A20, reading WAL from 0/064879E8. 2026-06-03 10:40:16.862 CEST [3963805][walsender][25/0:0] STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-03 10:40:16.862 CEST [3963805][walsender][25/0:0] ERROR: requested WAL segment 000000010000000000000006 has already been removed 2026-06-03 10:40:16.862 CEST [3963805][walsender][25/0:0] STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-03 10:40:16.862 CEST [3963805][walsender][25/0:0] LOG: released logical replication slot "promotion_activeslot" 2026-06-03 10:40:16.862 CEST [3963805][walsender][:0] LOG: disconnection: session time: 0:00:00.212 user=bf database=testdb host=[local] ... That is, walsender requested WAL segment for timeline 1, while in a successful run, it reads WAL for timeline 2. I've managed to reproduce this failure with: --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1502,2 +1502,3 @@ StartLogicalReplication(StartReplicationCmd *cmd) ReplicationSlotAcquire(cmd->slotname, true, true); +pg_usleep(200000); Being executed in a loop, the test fails for me within 100 iterations (within 10 on a slower VM): ITERATION 23 # +++ tap check in src/test/recovery +++ t/035_standby_logical_decoding.pl .. 84/? # pump_until: process terminated unexpectedly when searching for "(?^s:^.*COMMIT.*COMMIT$)" with stream: "" # Failed test 'got 2 COMMIT from pg_recvlogical output' # at t/035_standby_logical_decoding.pl line 1038. # Failed test 'got same expected output from pg_recvlogical decoding session' # at t/035_standby_logical_decoding.pl line 1042. # got: '' # expected: 'BEGIN # table public.decoding_test: INSERT: x[integer]:1 y[text]:'1' ... As far as I can see, the timeline is chosen in logical_read_xlog_page() depending on the recovery state: am_cascading_walsender = RecoveryInProgress(); if (am_cascading_walsender) GetXLogReplayRecPtr(&currTLI); else currTLI = GetWALInsertionTimeLine(); With diagnostic logging added here, a failed run shows: 2026-06-06 07:23:01.558 UTC walsender[165403] 035_standby_logical_decoding.pl LOG: starting logical decoding for slot "promotion_activeslot" 2026-06-06 07:23:01.558 UTC walsender[165403] 035_standby_logical_decoding.pl DETAIL: Streaming transactions committing after 0/0647C7E8, reading WAL from 0/0647C7B0. 2026-06-06 07:23:01.558 UTC walsender[165403] 035_standby_logical_decoding.pl STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-06 07:23:01.559 UTC walsender[165403] 035_standby_logical_decoding.pl LOG: !!!logical_read_xlog_page| am_cascading_walsender: 1, currTLI: 1 2026-06-06 07:23:01.559 UTC walsender[165403] 035_standby_logical_decoding.pl STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-06 07:23:01.559 UTC walsender[165403] 035_standby_logical_decoding.pl LOG: !!!WalSndSegmentOpen| nextSegNo: 6, path: pg_wal/000000010000000000000006 2026-06-06 07:23:01.559 UTC walsender[165403] 035_standby_logical_decoding.pl STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-06 07:23:01.559 UTC walsender[165403] 035_standby_logical_decoding.pl ERROR: requested WAL segment 000000010000000000000006 has already been removed In a successful run, am_cascading_walsender/RecoveryInProgress() == false and currTLI == 2: 2026-06-06 07:28:38.498 UTC walsender[169751] 035_standby_logical_decoding.pl LOG: starting logical decoding for slot "promotion_activeslot" 2026-06-06 07:28:38.498 UTC walsender[169751] 035_standby_logical_decoding.pl DETAIL: Streaming transactions committing after 0/0647C7E8, reading WAL from 0/0647C7B0. 2026-06-06 07:28:38.498 UTC walsender[169751] 035_standby_logical_decoding.pl STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-06 07:28:38.498 UTC walsender[169751] 035_standby_logical_decoding.pl LOG: !!!logical_read_xlog_page| am_cascading_walsender: 0, currTLI: 2 2026-06-06 07:28:38.498 UTC walsender[169751] 035_standby_logical_decoding.pl STATEMENT: START_REPLICATION SLOT "promotion_activeslot" LOGICAL 0/00000000 ("include-xids" '0', "skip-empty-xacts" '1') 2026-06-06 07:28:38.498 UTC walsender[169751] 035_standby_logical_decoding.pl LOG: !!!WalSndSegmentOpen| nextSegNo: 6, path: pg_wal/000000020000000000000006 Reproduced at the commit fcd77d532, which added the test. [1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=scorpion&dt=2026-06-03%2008%3A31%3A43 Best regards, Alexander -
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-06T11:07:11Z
Hi Alexander, On Sat, Jun 06, 2026 at 12:00:00PM +0300, Alexander Lakhin wrote: > Hello hackers, > > That is, walsender requested WAL segment for timeline 1, while in a > successful run, it reads WAL for timeline 2. > > I've managed to reproduce this failure with: Thanks for the report and the repro! > As far as I can see, the timeline is chosen in logical_read_xlog_page() > depending on the recovery state: > am_cascading_walsender = RecoveryInProgress(); > > if (am_cascading_walsender) > GetXLogReplayRecPtr(&currTLI); > else > currTLI = GetWALInsertionTimeLine(); Yeah, it looks like there is a race condition here. I think we should check if the insertion timeline has already been set (like the walsummarizer is doing). I'll work on a fix early next week. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-06T12:56:10Z
Hi Bertrand, On Sat, Jun 6, 2026 at 7:07 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > Hi Alexander, > > On Sat, Jun 06, 2026 at 12:00:00PM +0300, Alexander Lakhin wrote: > > Hello hackers, > > > > That is, walsender requested WAL segment for timeline 1, while in a > > successful run, it reads WAL for timeline 2. > > > > I've managed to reproduce this failure with: > > Thanks for the report and the repro! > > > As far as I can see, the timeline is chosen in logical_read_xlog_page() > > depending on the recovery state: > > am_cascading_walsender = RecoveryInProgress(); > > > > if (am_cascading_walsender) > > GetXLogReplayRecPtr(&currTLI); > > else > > currTLI = GetWALInsertionTimeLine(); > > Yeah, it looks like there is a race condition here. I think we should check if > the insertion timeline has already been set (like the walsummarizer is doing). > > I'll work on a fix early next week. This looks like the right direction to fix. We may want to apply similar logic to read_local_xlog_page_guts as well. Although the failure is reported in walsender, SQL logical decoding uses the local WAL reader and has the same recovery/TLI pattern. -- Regards, Xuneng Zhou HighGo Software Co., Ltd.
-
RE: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-06-08T04:25:45Z
Hi Alexander, Bertrand, Xuneng, Thanks for seeing the failure. Our team also recognized but could not find the reason. > Yeah, it looks like there is a race condition here. I think we should check if > the insertion timeline has already been set (like the walsummarizer is doing). Sorry for stupid question; I tried to reproduce the failure but could not, see attached. IIUC, the issue can happen if the walsender must read the WAL record generated after the promotion but the timeline could not be updated. However, I think logical_read_xlog_page() is called after the new WAL records are generated, i.e., am_cascading_walsender has already been false at that time. So not sure where is the race? Best regards, Hayato Kuroda FUJITSU LIMITED
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-08T08:31:39Z
Hi Kuroda-san, On Mon, Jun 8, 2026 at 12:25 PM Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> wrote: > > Hi Alexander, Bertrand, Xuneng, > > Thanks for seeing the failure. Our team also recognized but could not find the reason. > > > Yeah, it looks like there is a race condition here. I think we should check if > > the insertion timeline has already been set (like the walsummarizer is doing). > > Sorry for stupid question; I tried to reproduce the failure but could not, see attached. > > IIUC, the issue can happen if the walsender must read the WAL record generated > after the promotion but the timeline could not be updated. I think the race is that the logical walsender is trying to read the pre-promotion wal from the slot's restart_lsn. 1) In 035_standby_logical_decoding.pl, the active slot / pg_recvlogical process is started before: INSERT rows 1..4 # before promotion promote standby INSERT rows 5..7 # after promotion 2) Alexander’s reproducer sleeps immediately after: ReplicationSlotAcquire(cmd->slotname, true, true); pg_usleep(200000); So with the delay: slot is active but walsender has not yet create decoding context and called xlogbeginread(); 3) Then the test continues and promotes the standby while the walsender still sleeping, therefore promotion could happen before the walsender starts its actual wal read. 4) The logical walsender is trying to read the pre-promotion wal from the slot's restart_lsn. The failing log implies this: Streaming transactions committing after 0/06487A20, reading WAL from 0/064879E8. This line is emitted from: errdetail("Streaming transactions committing after %X/%08X, reading WAL from %X/%08X.", slot->data.confirmed_flush, slot->data.restart_lsn) Function StartLogicalReplication() initiates the xlogreader at the restart lsn: XLogBeginRead(logical_decoding_ctx->reader, MyReplicationSlot->data.restart_lsn); Because the slot was created and acquired before promotion, its restart_lsn is from the standby-era state which points to wal required for decoding the pre-promotion stream. In the test, the first expected output is rows 1..4, inserted before promotion. The failing output was empty: got: '' expected: BEGIN rows 1..4 COMMIT BEGIN rows 5..7 COMMIT Empty output suggests that the walsender died before emitting even the first pre-promotion transaction. 5) The walsender try to read wal but failed Failed run: LOG: !!!WalSndSegmentOpen| nextSegNo: 6, path: pg_wal/000000010000000000000006 ERROR: requested WAL segment 000000010000000000000006 has already been removed Passed run LOG: !!!WalSndSegmentOpen| nextSegNo: 6, path: pg_wal/000000020000000000000006 As the log suggested, the walsender of the successful run readed the wal from timeline 2 but failed to do so for timeline1. This comment in xlogutils.is almost exactly the issue: We care about timelines ... reading xlog generated prior to a promotion ... The server copied the segment to the new timeline ... there's no guarantee the old segment will still exist. It may have been deleted or renamed with a .partial suffix So the need for tli1 does not always imply that the wal was generated after promotion. It could be bytes from before the switchpoint, but they now be available through the new tli segment file. 6) XLogReadDetermineTimeline() needs the current system-wide TLI. The current code in does roughly: am_cascading_walsender = RecoveryInProgress(); if (am_cascading_walsender) GetXLogReplayRecPtr(&currTLI); else currTLI = GetWALInsertionTimeLine(); During promotion, we set the wal insertion timeline before marking recovery as fully done. So there is a window where: - InsertTimeLineID is already the new timeline, e.g. tli 2. - RecoveryInProgress() can still return true in another backend. - A logical walsender that started before promotion can wake up and choose its read timeline using the old recovery-state test. In the race, replay tli 1 is chosen even though the promotion has created the new tli 2 wal segment copy. Then WalSndSegmentOpen() tries to open 000000010000000000000006, which may already be gone or renamed, while the correct file is 000000020000000000000006. 7) Bertrand's proposal to fix this Recognizing the intermediate promotion state -- if recovery still shows progress but the insertion tli is already advanced, use that insertion tli for the timeline to be chosen. > However, I think logical_read_xlog_page() is called after the new WAL records > are generated, i.e., am_cascading_walsender has already been false at that time. > So not sure where is the race? This function is not only called after new post-promotion records are generated. When START_REPLICATION begins, CreateDecodingContext() / XLogBeginRead() starts from the slot's restart_lsn, which can be well before promotion. -- Regards, Xuneng Zhou HighGo Software Co., Ltd. -
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-08T08:47:48Z
Hi, On Mon, Jun 08, 2026 at 04:25:45AM +0000, Hayato Kuroda (Fujitsu) wrote: > Hi Alexander, Bertrand, Xuneng, > > Thanks for seeing the failure. Our team also recognized but could not find the reason. > > > Yeah, it looks like there is a race condition here. I think we should check if > > the insertion timeline has already been set (like the walsummarizer is doing). > > IIUC, the issue can happen if the walsender must read the WAL record generated > after the promotion but the timeline could not be updated. > > However, I think logical_read_xlog_page() is called after the new WAL records > are generated, i.e., am_cascading_walsender has already been false at that time. > So not sure where is the race? I ended up with this conclusion: During promotion, there is a window where RecoveryInProgress() still returns true but old timeline WAL segments have already been removed or recycled by RemoveNonParentXlogFiles() in CleanupAfterArchiveRecovery(). This is because, in StartupXLOG(), WAL segments are cleaned up before SharedRecoveryState transitions to RECOVERY_STATE_DONE. If a walsender performing logical decoding calls logical_read_xlog_page() during this window, it would get the old timeline from GetXLogReplayRecPtr(), then attempt to open a WAL segment on that old timeline which no longer exists. Attached: 0001: To fix this race Fix by checking GetWALInsertionTimeLineIfSet() when RecoveryInProgress() returns true. If InsertTimeLineID is already set (non-zero), the new timeline is established and we use it directly, avoiding attempts to read from segments that may have been removed. 0002: Adding a test in 035_standby_logical_decoding.pl It makes use of a new injection point "promotion-after-wal-segment-cleanup" in StartupXLOG(), right after CleanupAfterArchiveRecovery() removes old timeline WAL segments but before SharedRecoveryState is set to RECOVERY_STATE_DONE. The test fails without the fix in 0001 so it also somehow proves that the diagnostic is right. 0003: Apply the same timeline fix to read_local_xlog_page_guts() Indeed, it could hit the same race as mentioned by Xuneng-San. 0004: Add a test for 0003 Remark: As far as the backpatching down to 16, it looks like 0001 to 0004 could be backpatched as they are down to 17. For 16, we may want to also introduce GetWALInsertionTimeLineIfSet(). I can have a closer look for the backpatch once we agree on how to fix those races on master. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-08T13:00:17Z
On Mon, Jun 8, 2026 at 4:31 PM Xuneng Zhou <xunengzhou@gmail.com> wrote: > > Hi Kuroda-san, > > On Mon, Jun 8, 2026 at 12:25 PM Hayato Kuroda (Fujitsu) > <kuroda.hayato@fujitsu.com> wrote: > > > > Hi Alexander, Bertrand, Xuneng, > > > > Thanks for seeing the failure. Our team also recognized but could not find the reason. > > > > > Yeah, it looks like there is a race condition here. I think we should check if > > > the insertion timeline has already been set (like the walsummarizer is doing). > > > > Sorry for stupid question; I tried to reproduce the failure but could not, see attached. > > > > IIUC, the issue can happen if the walsender must read the WAL record generated > > after the promotion but the timeline could not be updated. > > I think the race is that the logical walsender is trying to read the > pre-promotion wal from the slot's restart_lsn. > > 1) In 035_standby_logical_decoding.pl, the active slot / > pg_recvlogical process is started before: > INSERT rows 1..4 # before promotion > promote standby > INSERT rows 5..7 # after promotion > > 2) Alexander’s reproducer sleeps immediately after: > ReplicationSlotAcquire(cmd->slotname, true, true); > pg_usleep(200000); > > So with the delay: > slot is active but walsender has not yet create decoding context and > called xlogbeginread(); > > 3) Then the test continues and promotes the standby while the > walsender still sleeping, therefore promotion could happen before the > walsender starts its actual wal read. > > 4) The logical walsender is trying to read the pre-promotion wal from > the slot's restart_lsn. The failing log implies this: > Streaming transactions committing after 0/06487A20, > reading WAL from 0/064879E8. > > This line is emitted from: > errdetail("Streaming transactions committing after %X/%08X, reading > WAL from %X/%08X.", > slot->data.confirmed_flush, > slot->data.restart_lsn) > > Function StartLogicalReplication() initiates the xlogreader at the restart lsn: > XLogBeginRead(logical_decoding_ctx->reader, > MyReplicationSlot->data.restart_lsn); > > Because the slot was created and acquired before promotion, its > restart_lsn is from the standby-era state which points to wal required > for decoding the pre-promotion stream. In the test, the first expected > output is rows 1..4, inserted before promotion. > > The failing output was empty: > got: '' > expected: > BEGIN > rows 1..4 > COMMIT > BEGIN > rows 5..7 > COMMIT > > Empty output suggests that the walsender died before emitting even the > first pre-promotion transaction. > > 5) The walsender try to read wal but failed > Failed run: > LOG: !!!WalSndSegmentOpen| nextSegNo: 6, > path: pg_wal/000000010000000000000006 > ERROR: requested WAL segment > 000000010000000000000006 has already been removed > > Passed run > LOG: !!!WalSndSegmentOpen| nextSegNo: 6, > path: pg_wal/000000020000000000000006 > > As the log suggested, the walsender of the successful run readed the > wal from timeline 2 but failed to do so for timeline1. > This comment in xlogutils.is almost exactly the issue: > > We care about timelines ... reading xlog generated prior to a promotion > ... > The server copied the segment to the new timeline ... there's no guarantee > the old segment will still exist. It may have been deleted or renamed with > a .partial suffix > > So the need for tli1 does not always imply that the wal was generated > after promotion. It could be bytes from before the switchpoint, but > they now be available through the new tli segment file. > > 6) XLogReadDetermineTimeline() needs the current system-wide TLI. > > The current code in does roughly: > > am_cascading_walsender = RecoveryInProgress(); > > if (am_cascading_walsender) > GetXLogReplayRecPtr(&currTLI); > else > currTLI = GetWALInsertionTimeLine(); > > During promotion, we set the wal insertion timeline before marking > recovery as fully done. So there is a window where: > > - InsertTimeLineID is already the new timeline, e.g. tli 2. > - RecoveryInProgress() can still return true in another backend. > - A logical walsender that started before promotion can wake up and > choose its read timeline using the old recovery-state test. > > In the race, replay tli 1 is chosen even though the promotion has > created the new tli 2 wal segment copy. Then WalSndSegmentOpen() tries > to open 000000010000000000000006, which may already be gone or > renamed, while the correct file is 000000020000000000000006. > > 7) Bertrand's proposal to fix this > Recognizing the intermediate promotion state -- if recovery still > shows progress but the insertion tli is already advanced, use that > insertion tli for the timeline to be chosen. > > > However, I think logical_read_xlog_page() is called after the new WAL records > > are generated, i.e., am_cascading_walsender has already been false at that time. > > So not sure where is the race? > > This function is not only called after new post-promotion records are > generated. When START_REPLICATION begins, CreateDecodingContext() / > XLogBeginRead() starts from the slot's restart_lsn, which can be well > before promotion. > I tweaked the reproducer based on the theory outlined above. The main changes from the original reproducer are: 1) blocks at logical-walsender-after-slot-acquire in walsender.c, before the decoding context is created and before the reader starts from restart_lsn, matching the delay set by Alexander 2) Forces the first read to occur during promotion. It inserts rows 1..4, waits for replay, starts promotion with pg_promote(false), holds startup at startup-logical-decoding-status-change-end-of-recovery, then wakes the walsender. The test failed on HEAD and passed after applying the patch by Bertrand. -- Regards, Xuneng Zhou HighGo Software Co., Ltd. -
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-08T14:21:59Z
Hi, On Mon, Jun 08, 2026 at 09:00:17PM +0800, Xuneng Zhou wrote: > I tweaked the reproducer based on the theory outlined above. The main > changes from the original reproducer are: > > 1) blocks at logical-walsender-after-slot-acquire in walsender.c, > before the decoding context is created and before the reader starts > from restart_lsn, matching the delay set by Alexander > 2) Forces the first read to occur during promotion. It inserts rows > 1..4, waits for replay, starts promotion with pg_promote(false), holds > startup at startup-logical-decoding-status-change-end-of-recovery, > then wakes the walsender. Yeah, so this existing startup-logical-decoding-status-change-end-of-recovery injection point already exists in the code tree and is also called after CleanupAfterArchiveRecovery() and before RECOVERY_STATE_DONE change in StartupXLOG(). So this is the same window as the new injection point that was added in the new tests in v1-0002 shared up-thread [1]. That said, I think I prefer the v1-0002 shared up-thread [1] approach for the tests as: - the injection point name clearly describes the tested condition - no new injection point is added in walsender.c (it pauses startup mid-promotion and lets the walsender connect) - the test relies on one injection point (not two) [1]: https://postgr.es/m/aiaBtENl7tTf2MM8%40bdtpg Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-08T14:34:27Z
On Mon, Jun 8, 2026 at 10:22 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > Hi, > > On Mon, Jun 08, 2026 at 09:00:17PM +0800, Xuneng Zhou wrote: > > I tweaked the reproducer based on the theory outlined above. The main > > changes from the original reproducer are: > > > > 1) blocks at logical-walsender-after-slot-acquire in walsender.c, > > before the decoding context is created and before the reader starts > > from restart_lsn, matching the delay set by Alexander > > > 2) Forces the first read to occur during promotion. It inserts rows > > 1..4, waits for replay, starts promotion with pg_promote(false), holds > > startup at startup-logical-decoding-status-change-end-of-recovery, > > then wakes the walsender. > > Yeah, so this existing startup-logical-decoding-status-change-end-of-recovery > injection point already exists in the code tree and is also called after > CleanupAfterArchiveRecovery() and before RECOVERY_STATE_DONE change in > StartupXLOG(). > > So this is the same window as the new injection point that was added in the new > tests in v1-0002 shared up-thread [1]. > > That said, I think I prefer the v1-0002 shared up-thread [1] approach for the > tests as: > > - the injection point name clearly describes the tested condition > - no new injection point is added in walsender.c (it pauses startup mid-promotion > and lets the walsender connect) > - the test relies on one injection point (not two) > > [1]: https://postgr.es/m/aiaBtENl7tTf2MM8%40bdtpg > Thanks for the clarification. I haven't reviewed the patch set other than applying the patch 1 yet, but I plan to do so tomorrow. -- Regards, Xuneng Zhou HighGo Software Co., Ltd.
-
RE: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-06-09T04:04:20Z
Dear Bertrand, Xuneng, Thanks for sharing the reproducer and opinion. I could now understand. While seeing, I felt the same issue can happen even for the streaming case. Please see attached reproducer and possible fix. Can you also include that point? Or this thread discusses only for the logical case? If separated, I can take initiative for the streaming part, after the logical decoding case. Best regards, Hayato Kuroda FUJITSU LIMITED
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-09T05:57:56Z
Hi Hayato-san, On Tue, Jun 09, 2026 at 04:04:20AM +0000, Hayato Kuroda (Fujitsu) wrote: > Dear Bertrand, Xuneng, > > Thanks for sharing the reproducer and opinion. I could now understand. > > While seeing, I felt the same issue can happen even for the streaming case. > Please see attached reproducer and possible fix. I just had a quick look and I feel that's probably an issue too (good find!). I think that your fix should use GetWALInsertionTimeLineIfSet() though (but I only did a quick scan on your patch). > Can you also include that point? Or this thread discusses only for the logical case? > If separated, I can take initiative for the streaming part, after the logical decoding > case. I would says it's probably better to keep this thread focusing on Alexander's issue (logical decoding). Once this thread is fixed, then I'd suggest to open a dedicated thread for the streaming one (mentioning this thread and probably trying to mimic its fix as close as possible). Also I would suggest not to post "real" patches here as they conflict with mine in the cfbot (means the cfbot won't tell me anymore if my patches need a rebase). Please use .txt files or use "nocfbot" instead if you feel the need. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-09T06:03:28Z
Hi, On Mon, Jun 08, 2026 at 08:47:48AM +0000, Bertrand Drouvot wrote: > Attached: > > 0001: To fix this race > > Fix by checking GetWALInsertionTimeLineIfSet() when RecoveryInProgress() > returns true. If InsertTimeLineID is already set (non-zero), the new timeline is > established and we use it directly, avoiding attempts to read from segments that > may have been removed. > > 0002: Adding a test in 035_standby_logical_decoding.pl > > It makes use of a new injection point "promotion-after-wal-segment-cleanup" in > StartupXLOG(), right after CleanupAfterArchiveRecovery() removes old timeline > WAL segments but before SharedRecoveryState is set to RECOVERY_STATE_DONE. > > The test fails without the fix in 0001 so it also somehow proves that the > diagnostic is right. > > 0003: Apply the same timeline fix to read_local_xlog_page_guts() > > Indeed, it could hit the same race as mentioned by Xuneng-San. > > 0004: Add a test for 0003 Re-attaching v1 patches here to have the cfbot focusing on those. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-09T06:13:54Z
On Tue, Jun 9, 2026 at 1:57 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > Hi Hayato-san, > > On Tue, Jun 09, 2026 at 04:04:20AM +0000, Hayato Kuroda (Fujitsu) wrote: > > Dear Bertrand, Xuneng, > > > > Thanks for sharing the reproducer and opinion. I could now understand. > > > > While seeing, I felt the same issue can happen even for the streaming case. > > Please see attached reproducer and possible fix. Good catch! > I just had a quick look and I feel that's probably an issue too (good find!). > I think that your fix should use GetWALInsertionTimeLineIfSet() though (but I > only did a quick scan on your patch). > > > Can you also include that point? Or this thread discusses only for the logical case? > > If separated, I can take initiative for the streaming part, after the logical decoding > > case. > > I would says it's probably better to keep this thread focusing on Alexander's issue > (logical decoding). Once this thread is fixed, then I'd suggest to open a dedicated > thread for the streaming one (mentioning this thread and probably trying to mimic > its fix as close as possible). > +1 for starting a dedicated thread. Please CC me in that thread in case I miss it. -- Regards, Xuneng Zhou HighGo Software Co., Ltd.
-
RE: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Hayato Kuroda (Fujitsu) <kuroda.hayato@fujitsu.com> — 2026-06-09T07:05:20Z
Dear Bertrand, Thanks for re-posting. Let's focus on the logical decoding stuff. > > 0004: Add a test for 0003 I found a comment for the test. ``` +# 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'); +)); ``` Not sure, pg_logical_slot_peek_changes() can be finished without waiting anything, right? So why do we use query_until() and check the output here? IIUC, the command can immediately raise an ERROR if the race happened. So isn't it enough to use safe_psql() to ensure the SQL function can work? Best regards, Hayato Kuroda FUJITSU LIMITED -
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-09T07:49:50Z
On Mon, Jun 8, 2026 at 10:34 PM Xuneng Zhou <xunengzhou@gmail.com> wrote: > > On Mon, Jun 8, 2026 at 10:22 PM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > > > Hi, > > > > On Mon, Jun 08, 2026 at 09:00:17PM +0800, Xuneng Zhou wrote: > > > I tweaked the reproducer based on the theory outlined above. The main > > > changes from the original reproducer are: > > > > > > 1) blocks at logical-walsender-after-slot-acquire in walsender.c, > > > before the decoding context is created and before the reader starts > > > from restart_lsn, matching the delay set by Alexander > > > > > 2) Forces the first read to occur during promotion. It inserts rows > > > 1..4, waits for replay, starts promotion with pg_promote(false), holds > > > startup at startup-logical-decoding-status-change-end-of-recovery, > > > then wakes the walsender. > > > > Yeah, so this existing startup-logical-decoding-status-change-end-of-recovery > > injection point already exists in the code tree and is also called after > > CleanupAfterArchiveRecovery() and before RECOVERY_STATE_DONE change in > > StartupXLOG(). > > > > So this is the same window as the new injection point that was added in the new > > tests in v1-0002 shared up-thread [1]. > > > > That said, I think I prefer the v1-0002 shared up-thread [1] approach for the > > tests as: > > > > - the injection point name clearly describes the tested condition > > - no new injection point is added in walsender.c (it pauses startup mid-promotion > > and lets the walsender connect) > > - the test relies on one injection point (not two) > > > > [1]: https://postgr.es/m/aiaBtENl7tTf2MM8%40bdtpg > > > > Thanks for the clarification. I haven't reviewed the patch set other > than applying the patch 1 yet, but I plan to do so tomorrow. I've readed through the patch set. They look good overall. Here're some comments on them: 1) In the commit messages and comments for all four patches, the reason why the target WAL segment cannot be found on the old timeline is described as follows: "old timeline WAL segments have already been removed or recycled by RemoveNonParentXlogFiles() in CleanupAfterArchiveRecovery()." Is mentioning the 'remove' case only a bit narrow? The timeline-selection comment says this explicitly: "there's no guarantee the old segment will still exist. It may have been deleted or renamed with a .partial suffix" How about phrasing it like: old timeline WAL files may have been removed, recycled, or renamed to .partial. After running the reproducer provided by Hayato-san, the standby’s pg_wal directory looked like this following the failure: 000000010000000000000003.partial 00000002.history 000000020000000000000003 000000020000000000000004 So in this repro, the requested file: 000000010000000000000003 was not unlinked as a regular "removed" file. It had been renamed to: 000000010000000000000003.partial but the log says this explicitly: ERROR: requested WAL segment 000000010000000000000003 has already been removed It appears inconsistent to me... 2) Injection points in tests 0002 and 0004 > > - the injection point name clearly describes the tested condition I think this is true. It better reflects the intention than startup-logical-decoding-status-change-end-of-recovery does. However, I don't know whether this alone warrants a new injection point. > > - no new injection point is added in walsender.c (it pauses startup mid-promotion > > and lets the walsender connect) > > - the test relies on one injection point (not two) I am unsure that the injection point could replace the walsender-side synchronization need. The race has two moving processes: - startup process: promotion cleanup / RECOVERY_STATE_DONE transition - walsender process: logical_read_xlog_page() timeline choice A startup injection point controls only this: startup is paused in the vulnerable promotion window It does not prove this: walsender has reached logical_read_xlog_page() while startup is paused 3) Stricter synchronization point in both tests Both tests use this condition "active_pid IS NOT NULL" for synchronization at the walsender side. However, it only proves that pg_recvlogical has connected walsender has acquired the logical slot, not necessarily the walsender is paused after acquiring the slot and before the promotion window is set. There are several potential states for walsender in this condition: walsender is just after ReplicationSlotAcquire() it has called XLogBeginRead() it is already inside logical_read_xlog_page() it already opened the WAL segment it already failed or succeeded The test cannot distinguish those states reliably. So we may still need another injection point for synchronization at the walsender side 4) Stricter result checks in test files The surrounding 035 test is stricter than the test in 0002. It first waits for COMMITs, then compares exact decoded output. Should we adhere to this pattern too? 0004's \d+ check seems somewhat loose to me. Should we capture the query result and compare it to the expected count, or select data and compare the decoded rows? -- Regards, Xuneng Zhou HighGo Software Co., Ltd.
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-09T11:01:28Z
Hi, On Tue, Jun 09, 2026 at 03:49:50PM +0800, Xuneng Zhou wrote: > On Mon, Jun 8, 2026 at 10:34 PM Xuneng Zhou <xunengzhou@gmail.com> wrote: > > > > On Mon, Jun 8, 2026 at 10:22 PM Bertrand Drouvot > > <bertranddrouvot.pg@gmail.com> wrote: > > I've readed through the patch set. They look good overall. Thanks for the review! > Here're > some comments on them: > > 1) In the commit messages and comments for all four patches, the > reason why the target WAL segment cannot be found on the old timeline > is described as follows: > > "old timeline WAL segments have already been removed or > recycled by RemoveNonParentXlogFiles() in CleanupAfterArchiveRecovery()." > > Is mentioning the 'remove' case only a bit narrow? > > The timeline-selection comment says this explicitly: > "there's no guarantee the old segment will still exist. It may have been > deleted or renamed with a .partial suffix" > > How about phrasing it like: > old timeline WAL files may have been removed, recycled, or renamed to .partial. > > After running the reproducer provided by Hayato-san, the standby’s > pg_wal directory looked like this following the failure: > 000000010000000000000003.partial > 00000002.history > 000000020000000000000003 > 000000020000000000000004 > > So in this repro, the requested file: > > 000000010000000000000003 > > was not unlinked as a regular "removed" file. It had been renamed to: > > 000000010000000000000003.partial > > but the log says this explicitly: > ERROR: requested WAL segment 000000010000000000000003 has already been removed > > It appears inconsistent to me... I'm not sure. The error message says "has already been removed" and the commit messages and comments says"removed or recycled": those are consistent with the error message. We're describing the symptom from the walsender's perspective, not the exact file operation that caused it. > 2) Injection points in tests 0002 and 0004 > > It does not prove this: > walsender has reached logical_read_xlog_page() while startup is paused > > 3) Stricter synchronization point in both tests > Both tests use this condition "active_pid IS NOT NULL" for > synchronization at the walsender side. However, it only proves that > pg_recvlogical has connected walsender has acquired the logical slot, > not necessarily the walsender is paused after acquiring the slot and > before the promotion window is set. There are several potential states > for walsender in this condition: > > walsender is just after ReplicationSlotAcquire() > it has called XLogBeginRead() > it is already inside logical_read_xlog_page() > it already opened the WAL segment > it already failed or succeeded > > The test cannot distinguish those states reliably. > > So we may still need another injection point for synchronization at > the walsender side I agree that with v1 the test could have been fragile. It's fixed in v2 without having to add a second injection point. All we have to do is to ensure that the decoding occurred while the startup is paused on the new injection point. 0002 does that by starting the new walsender and doing the decoding while the startup is paused 0004 does that by ensuring the pre-connected session triggers the decoding while the startup is paused > 4) Stricter result checks in test files > The surrounding 035 test is stricter than the test in 0002. It first > waits for COMMITs, then compares exact decoded output. Should we > adhere to this pattern too? That's done in v2 (and also adress Hayato-san feedback). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-10T08:36:14Z
On Tue, Jun 9, 2026 at 7:01 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > Hi, > > On Tue, Jun 09, 2026 at 03:49:50PM +0800, Xuneng Zhou wrote: > > On Mon, Jun 8, 2026 at 10:34 PM Xuneng Zhou <xunengzhou@gmail.com> wrote: > > > > > > On Mon, Jun 8, 2026 at 10:22 PM Bertrand Drouvot > > > <bertranddrouvot.pg@gmail.com> wrote: > > > > I've readed through the patch set. They look good overall. > > Thanks for the review! > > > Here're > > some comments on them: > > > > 1) In the commit messages and comments for all four patches, the > > reason why the target WAL segment cannot be found on the old timeline > > is described as follows: > > > > "old timeline WAL segments have already been removed or > > recycled by RemoveNonParentXlogFiles() in CleanupAfterArchiveRecovery()." > > > > Is mentioning the 'remove' case only a bit narrow? > > > > The timeline-selection comment says this explicitly: > > "there's no guarantee the old segment will still exist. It may have been > > deleted or renamed with a .partial suffix" > > > > How about phrasing it like: > > old timeline WAL files may have been removed, recycled, or renamed to .partial. > > > > After running the reproducer provided by Hayato-san, the standby’s > > pg_wal directory looked like this following the failure: > > 000000010000000000000003.partial > > 00000002.history > > 000000020000000000000003 > > 000000020000000000000004 > > > > So in this repro, the requested file: > > > > 000000010000000000000003 > > > > was not unlinked as a regular "removed" file. It had been renamed to: > > > > 000000010000000000000003.partial > > > > but the log says this explicitly: > > ERROR: requested WAL segment 000000010000000000000003 has already been removed > > > > It appears inconsistent to me... > > I'm not sure. The error message says "has already been removed" and the commit > messages and comments says"removed or recycled": those are consistent with the > error message. We're describing the symptom from the walsender's perspective, > not the exact file operation that caused it. > > 2) Injection points in tests 0002 and 0004 > > > > It does not prove this: > > walsender has reached logical_read_xlog_page() while startup is paused > > > > 3) Stricter synchronization point in both tests > > Both tests use this condition "active_pid IS NOT NULL" for > > synchronization at the walsender side. However, it only proves that > > pg_recvlogical has connected walsender has acquired the logical slot, > > not necessarily the walsender is paused after acquiring the slot and > > before the promotion window is set. There are several potential states > > for walsender in this condition: > > > > walsender is just after ReplicationSlotAcquire() > > it has called XLogBeginRead() > > it is already inside logical_read_xlog_page() > > it already opened the WAL segment > > it already failed or succeeded > > > > The test cannot distinguish those states reliably. > > > > So we may still need another injection point for synchronization at > > the walsender side > > I agree that with v1 the test could have been fragile. It's fixed in v2 without > having to add a second injection point. All we have to do is to ensure that > the decoding occurred while the startup is paused on the new injection point. > > 0002 does that by starting the new walsender and doing the decoding while the > startup is paused > 0004 does that by ensuring the pre-connected session triggers the decoding while > the startup is paused That should work, and it’s cleverer. I was fixated on the idea that we needed to start the walsender, pause it, suspend the startup process to enter the promotion window, and then resume the walsender. The essential thing is just to ensure that the startup remains paused until decoding output is observed. -- Regards, Xuneng Zhou HighGo Software Co., Ltd.
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-10T17:28:00Z
Hi, On Wed, Jun 10, 2026 at 04:36:14PM +0800, Xuneng Zhou wrote: > On Tue, Jun 9, 2026 at 7:01 PM Bertrand Drouvot > > 0002 does that by starting the new walsender and doing the decoding while the > > startup is paused > > 0004 does that by ensuring the pre-connected session triggers the decoding while > > the startup is paused > > The > essential thing is just to ensure that the startup remains paused > until decoding output is observed. Right, thanks for confirming. That's exactly what v2 is doing. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Michael Paquier <michael@paquier.xyz> — 2026-06-11T01:15:01Z
On Wed, Jun 10, 2026 at 05:28:00PM +0000, Bertrand Drouvot wrote: > On Wed, Jun 10, 2026 at 04:36:14PM +0800, Xuneng Zhou wrote: >> The >> essential thing is just to ensure that the startup remains paused >> until decoding output is observed. > > Right, thanks for confirming. That's exactly what v2 is doing. I have looked at this thread, and my first impression was that this could be a data integrity issue while decoding changes due to the transient errors one could see across the promotion requests. But it's less severe than I thought initially: we have an availability problem here, down to v16, with a correct recovery possible once the promotion request has completed. That could be indeed surprising for users that have HA setups with standbys doing logical decoding.. The SQL function path is less worrying to me, there are as far as I know few users of it compared to the "native" path with sync workers. read_local_xlog_page_guts() does not only impact SQL-callable logirep functions, even it is the spot that should be hit most of the time (again, the RecoveryInProgress() vs promotion window is super narrow). At quick glance, things are: - walinspect. - Slot advance. - Slot creation (?), but it feels even narrower. With two items dealt with on this thread for these two callback paths changed, moving on the part related to physical replication into its own thread would be better. This requires an entirely different analysis and a different lookup. The backpatch of PG16 is straight-forward and adding GetWALInsertionTimeLineIfSet() down there does not look like an issue. Not having any tests in v16 feels sad, but that's life. It does not prevent addressing the availability issue on this branch. I'll go take it up from here. -- Michael
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Michael Paquier <michael@paquier.xyz> — 2026-06-11T09:53:19Z
On Thu, Jun 11, 2026 at 10:15:01AM +0900, Michael Paquier wrote: > The backpatch of PG16 is straight-forward and adding > GetWALInsertionTimeLineIfSet() down there does not look like an issue. > Not having any tests in v16 feels sad, but that's life. It does not > prevent addressing the availability issue on this branch. Backpatch of GetWALInsertionTimeLineIfSet() done in v16. > I'll go take it up from here. I have spent some time on the first part of this patch series, focusing on the noisiest path for logical replication, and applied it down to v17. I have been able to reproduce the problem using Alexander's sleep trick, and let 035 run with the same trick and the patch applied for a few hundred runs. Note that REL_17_STABLE has currently 4 failures in the buildfarm, due to me messing up with the existence of injection_points. I'll probably just remove the test on this branch, 105b2cb33617 adding the requirement on the module only in v18~. -- Michael
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-12T00:57:05Z
Hi Michael, On Thu, Jun 11, 2026 at 9:15 AM Michael Paquier <michael@paquier.xyz> wrote: > > On Wed, Jun 10, 2026 at 05:28:00PM +0000, Bertrand Drouvot wrote: > > On Wed, Jun 10, 2026 at 04:36:14PM +0800, Xuneng Zhou wrote: > >> The > >> essential thing is just to ensure that the startup remains paused > >> until decoding output is observed. > > > > Right, thanks for confirming. That's exactly what v2 is doing. > > I have looked at this thread, and my first impression was that this > could be a data integrity issue while decoding changes due to the > transient errors one could see across the promotion requests. > > But it's less severe than I thought initially: we have an availability > problem here, down to v16, with a correct recovery possible once the > promotion request has completed. That could be indeed surprising for > users that have HA setups with standbys doing logical decoding.. The > SQL function path is less worrying to me, there are as far as I know > few users of it compared to the "native" path with sync workers. > > read_local_xlog_page_guts() does not only impact SQL-callable logirep > functions, even it is the spot that should be hit most of the time > (again, the RecoveryInProgress() vs promotion window is super narrow). > At quick glance, things are: > - walinspect. > - Slot advance. > - Slot creation (?), but it feels even narrower. Yeah, it is used for two-phase commit as well. The usage of it is broader than I observed before. Repack worker also make use of it. > With two items dealt with on this thread for these two callback paths > changed, moving on the part related to physical replication into its > own thread would be better. This requires an entirely different > analysis and a different lookup. +1 > The backpatch of PG16 is straight-forward and adding > GetWALInsertionTimeLineIfSet() down there does not look like an issue. > Not having any tests in v16 feels sad, but that's life. It does not > prevent addressing the availability issue on this branch. > > I'll go take it up from here. > -- Thanks for dealing with this! -- Regards, Xuneng Zhou HighGo Software Co., Ltd.
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Michael Paquier <michael@paquier.xyz> — 2026-06-12T02:44:47Z
On Fri, Jun 12, 2026 at 08:57:05AM +0800, Xuneng Zhou wrote: > On Thu, Jun 11, 2026 at 9:15 AM Michael Paquier <michael@paquier.xyz> wrote: >> read_local_xlog_page_guts() does not only impact SQL-callable logirep >> functions, even it is the spot that should be hit most of the time >> (again, the RecoveryInProgress() vs promotion window is super narrow). >> At quick glance, things are: >> - walinspect. >> - Slot advance. >> - Slot creation (?), but it feels even narrower. > > Yeah, it is used for two-phase commit as well. The usage of it is > broader than I observed before. Repack worker also make use of it. Repack workers cannot work on standbys, and for 2PC files we have the guarantee of a file on disk if the WAL is gone, so they should be basically irrelevant here. That's why I did not mention them. :) Everything's now done. I was pondering about v14 and v15 for a bit for the second patch regarding pg_walinspect (or slot operations), but I could not get excited about these branches. pg_walinspect is less used, and slots have much more uses on standbys since v16~, so expanding that further down feels meh. -- Michael
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-12T03:00:31Z
Hi, On Fri, Jun 12, 2026 at 11:44:47AM +0900, Michael Paquier wrote: > > Everything's now done. Thanks! > I was pondering about v14 and v15 for a bit > for the second patch regarding pg_walinspect (or slot operations), but > I could not get excited about these branches. pg_walinspect is less > used, and slots have much more uses on standbys since v16~, so > expanding that further down feels meh. That sounds reasonable to me. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: t/035_standby_logical_decoding.pl might fail on attempt to read wrong timeline
Xuneng Zhou <xunengzhou@gmail.com> — 2026-06-12T06:28:23Z
On Fri, Jun 12, 2026 at 10:44 AM Michael Paquier <michael@paquier.xyz> wrote: > > On Fri, Jun 12, 2026 at 08:57:05AM +0800, Xuneng Zhou wrote: > > On Thu, Jun 11, 2026 at 9:15 AM Michael Paquier <michael@paquier.xyz> wrote: > >> read_local_xlog_page_guts() does not only impact SQL-callable logirep > >> functions, even it is the spot that should be hit most of the time > >> (again, the RecoveryInProgress() vs promotion window is super narrow). > >> At quick glance, things are: > >> - walinspect. > >> - Slot advance. > >> - Slot creation (?), but it feels even narrower. > > > > Yeah, it is used for two-phase commit as well. The usage of it is > > broader than I observed before. Repack worker also make use of it. > > Repack workers cannot work on standbys, and for 2PC files we have the > guarantee of a file on disk if the WAL is gone, so they should be > basically irrelevant here. That's why I did not mention them. :) Thanks for the clarification. > Everything's now done. I was pondering about v14 and v15 for a bit > for the second patch regarding pg_walinspect (or slot operations), but > I could not get excited about these branches. pg_walinspect is less > used, and slots have much more uses on standbys since v16~, so > expanding that further down feels meh. Making sense to me. This looks like a hard-to-hit issue on HEAD. The likelihood of encountering it and the severity of damage caused by it in those branches are expected to be even smaller. -- Regards, Xuneng Zhou HighGo Software Co., Ltd.