Hi,

Konstantin Knizhnik <[email protected]> wrote:
> I have not re-run the recovery TAP suite against this tree (no local
> install of current master).

I ran it on master 2c10c2ce4d7 with v2 (cassert, injection points):
src/test/recovery passes, 802 tests against 799 on master, the new
ones being 058.  Without the C changes, 058 fails as intended, with
startup rereading the corrupt local copy until the timeout.

One ordering issue: the reset of applyFlushedUpto runs after
RequestXLogStreaming().  If the new walreceiver flushes the
replacement bytes before the reset, the pointer goes back below them
and startup waits for the next flush.  The window is tiny, but with a
3 s sleep there, replay sat for about 18 s until an unrelated
running-xacts record arrived.

Doing the reset before RequestXLogStreaming() closes it: with the
same sleep there is no stall, and with only that change (attached, on
top of v2) the recovery suite passes and 058 passed 20 of 20 runs.

Logs and the review-only diffs are in the second attachment.

Regards,
Manu
diff --git a/src/backend/access/transam/xlogrecovery.c 
b/src/backend/access/transam/xlogrecovery.c
index 061c7e1ff5b..e3dc1138c4e 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -3880,11 +3880,6 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool 
randAccess,
                                                }
                                                curFileTLI = tli;
                                                
SetInstallXLogFileSegmentActive();
-                                               RequestXLogStreaming(tli, ptr, 
PrimaryConnInfo,
-                                                                               
         PrimarySlotName,
-                                                                               
         wal_receiver_create_temp_slot);
-                                               flushedUpto = InvalidXLogRecPtr;
-
                                                /*
                                                 * RequestXLogStreaming() 
rounds ptr down to a segment
                                                 * boundary and leaves 
flushedUpto unchanged on a
@@ -3911,6 +3906,11 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool 
randAccess,
                                                                
ResetWalRcvApplyFlushRecPtr(ptr);
                                                        }
                                                }
+
+                                               RequestXLogStreaming(tli, ptr, 
PrimaryConnInfo,
+                                                                               
         PrimarySlotName,
+                                                                               
         wal_receiver_create_temp_slot);
+                                               flushedUpto = InvalidXLogRecPtr;
                                        }
 
                                        /*
Review of v2 (applyFlushedUpto) on master 2c10c2ce4d7
--enable-cassert --enable-debug --enable-tap-tests --enable-injection-points

===== recovery suite (make check in src/test/recovery, PROVE_FLAGS=-j6) =====
v2:     Files=57, Tests=802, 62 wallclock secs ( 0.45 usr  0.07 sys + 20.70 
cusr 41.80 csys = 63.02 CPU) Result: PASS
master: Files=56, Tests=799, 67 wallclock secs ( 0.46 usr  0.08 sys + 21.34 
cusr 42.92 csys = 64.80 CPU) Result: PASS

===== 058 alone, 20 runs with v2 =====
run 1: PASS in 6 s
run 2: PASS in 4 s
run 3: PASS in 5 s
run 4: PASS in 7 s
run 5: PASS in 4 s
run 6: PASS in 6 s
run 7: PASS in 6 s
run 8: PASS in 6 s
run 9: PASS in 7 s
run 10: PASS in 5 s
run 11: PASS in 4 s
run 12: PASS in 4 s
run 13: PASS in 4 s
run 14: PASS in 4 s
run 15: PASS in 4 s
run 16: PASS in 5 s
run 17: PASS in 5 s
run 18: PASS in 4 s
run 19: PASS in 5 s
run 20: PASS in 5 s
v2: 20 passed, 0 failed of 20

===== 058 with the test but without the C changes =====
run 1: FAIL in 77 s
# poll_query_until timed out executing this query:
tonly: 0 passed, 1 failed of 1
standby log: 'incorrect prev-link 0/DEADBEEF' x 663, 'started streaming WAL' x 2

===== delay.diff (review only: 3 s between RequestXLogStreaming() and the 
reset) =====
diff --git a/src/backend/access/transam/xlogrecovery.c 
b/src/backend/access/transam/xlogrecovery.c
index 061c7e1ff5b..318d2db8c26 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -3896,6 +3896,11 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool 
randAccess,
                                                 * apply pointer so replay is 
not stalled on WAL that
                                                 * is already on disk.
                                                 */
+                                               /* REVIEW ONLY: widen the 
window between RequestXLogStreaming()
+                                                * and the reset below, to see 
what happens if the new
+                                                * walreceiver flushes before 
the reset. */
+                                               if (resetApplyFlush)
+                                                       pg_usleep(3000000L);
                                                if (resetApplyFlush)
                                                {
                                                        XLogRecPtr      
sharedFlush;

===== reorder.diff (review only: reset moved before RequestXLogStreaming(), 
same 3 s sleep after it) =====
diff --git a/src/backend/access/transam/xlogrecovery.c 
b/src/backend/access/transam/xlogrecovery.c
index 061c7e1ff5b..8c4760b1a78 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -3880,11 +3880,6 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool 
randAccess,
                                                }
                                                curFileTLI = tli;
                                                
SetInstallXLogFileSegmentActive();
-                                               RequestXLogStreaming(tli, ptr, 
PrimaryConnInfo,
-                                                                               
         PrimarySlotName,
-                                                                               
         wal_receiver_create_temp_slot);
-                                               flushedUpto = InvalidXLogRecPtr;
-
                                                /*
                                                 * RequestXLogStreaming() 
rounds ptr down to a segment
                                                 * boundary and leaves 
flushedUpto unchanged on a
@@ -3911,6 +3906,15 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool 
randAccess,
                                                                
ResetWalRcvApplyFlushRecPtr(ptr);
                                                        }
                                                }
+                                               RequestXLogStreaming(tli, ptr, 
PrimaryConnInfo,
+                                                                               
         PrimarySlotName,
+                                                                               
         wal_receiver_create_temp_slot);
+                                               flushedUpto = InvalidXLogRecPtr;
+
+                                               /* REVIEW ONLY: same 3 s window 
as the v2delay build, now
+                                                * after the reset instead of 
before it. */
+                                               if (resetApplyFlush)
+                                                       pg_usleep(3000000L);
                                        }
 
                                        /*

===== t/058b_stream_repair_probe.pl (058 + primary at debug2 + PROBE note) =====
--- /home/manu/pgwal/src-v2/src/test/recovery/t/058_stream_repair.pl    
2026-09-24 22:39:11.024531848 -0300
+++ 
/home/manu/pgwal/src-v2delay/src/test/recovery/t/058b_stream_repair_probe.pl    
    2026-09-24 22:46:25.933116755 -0300
@@ -21,6 +21,7 @@
        'postgresql.conf', qq(
 autovacuum = off
 wal_keep_size = 1GB
+log_min_messages = debug2
 ));
 $primary->start;
 
@@ -145,6 +146,13 @@
        "SELECT pg_last_wal_replay_lsn() >= '$target_lsn'")
   or die "standby did not replace corrupt WAL and catch up";
 
+# REVIEW PROBE: did the primary have to write WAL past the target for the
+# standby to get there?
+my $primary_now = $primary->lsn('insert');
+my $past = $primary->safe_psql('postgres',
+       "SELECT pg_wal_lsn_diff('$primary_now', '$target_lsn')");
+note "PROBE target_lsn=$target_lsn primary_insert_now=$primary_now 
bytes_past_target=$past";
+
 ok( $standby->log_contains(
                qr/record with incorrect prev-link 0\/DEADBEEF/),
        'standby observed the injected corrupt record');

===== probe with delay.diff =====
2026-09-24 22:46:34.626 -03 checkpointer[912365] DEBUG:  snapshot of 0+0 
running transaction ids (lsn 0/02000088 oldest 
2026-09-24 22:46:58.978 -03 background writer[912366] DEBUG:  snapshot of 0+0 
running transaction ids (lsn 0/050020D8 ol
2026-09-24 22:46:34.876 -03 walreceiver[912494] LOG:  started streaming WAL 
from primary at 0/03000000 on timeline 1
2026-09-24 22:46:38.002 -03 walreceiver[913390] LOG:  started streaming WAL 
from primary at 0/03000000 on timeline 1
2026-09-24 22:46:38.115 -03 startup[912493] LOG:  record with incorrect 
prev-link 0/DEADBEEF at 0/04000028
2026-09-24 22:46:38.117 -03 walreceiver[913456] LOG:  started streaming WAL 
from primary at 0/04000000 on timeline 1
2026-09-24 22:46:41.116 -03 startup[912493] LOG:  restarting WAL streaming from 
0/04000018; ignoring previously flushed WAL up to 0/050020A0
[22:46:38.009](4.490s) ok 1 - receive LSN does not move backward on walreceiver 
restart with apply lag
[22:46:59.085](21.076s) # PROBE target_lsn=0/050020A0 
primary_insert_now=0/050020D8 bytes_past_target=56
[22:46:59.087](0.002s) ok 2 - standby observed the injected corrupt record
[22:46:59.087](0.000s) ok 3 - standby ignored stale flush pointer after the 
corrupt record

===== probe with reorder.diff =====
Files=1, Tests=3,  8 wallclock secs ( 0.01 usr  0.00 sys +  0.25 cusr  0.55 
csys =  0.81 CPU)
Result: PASS
2026-09-24 22:48:49.248 -03 startup[957343] LOG:  record with incorrect 
prev-link 0/DEADBEEF at 0/04000028
2026-09-24 22:48:49.248 -03 startup[957343] LOG:  restarting WAL streaming from 
0/04000018; ignoring previously flushed WAL up to 0/050020A0
2026-09-24 22:48:49.250 -03 walreceiver[958288] LOG:  started streaming WAL 
from primary at 0/04000000 on timeline 1
[22:48:49.124](4.254s) ok 1 - receive LSN does not move backward on walreceiver 
restart with apply lag
[22:48:52.371](3.247s) # PROBE target_lsn=0/050020A0 
primary_insert_now=0/050020A0 bytes_past_target=0
[22:48:52.372](0.000s) ok 2 - standby observed the injected corrupt record
[22:48:52.372](0.000s) ok 3 - standby ignored stale flush pointer after the 
corrupt record

===== v2 + nocfbot-reset-before-request.diff.txt (no sleep) =====
v2fix built, warnings=0
v2fix: 20 passed, 0 failed of 20
recovery suite v2fix: rc=0 in 45 s; Files=57, Tests=802, 43 wallclock secs ( 
0.36 usr  0.06 sys + 14.60 cusr 30.96 csys = 45.98 CPU) Result: PASS 

Reply via email to