From 4cebee5b894c83eb3e1649e0a277830d9c4fe22d Mon Sep 17 00:00:00 2001 From: Bryan Green Date: Sat, 8 Aug 2026 15:37:22 -0500 Subject: [PATCH] Fix TOCTOU races in recovery/t/020_archive_status.pl archive checks The test polled pg_stat_archiver for one condition (archived_count, or last_archived_wal) and then, in a separate query, asserted a related condition. Between the two queries additional WAL segments can be archived (checkpoint or crash-recovery activity), so the second query can observe values that no longer match the exact ones expected; the poll's exact-equality check can also miss a value entirely if the count jumps past it. Use monotonic >= comparisons so the checks tolerate additional archived segments. WAL segment names are lexically ordered, so >= is well defined for last_archived_wal. Co-authored-by: Mark Dilger --- src/test/recovery/t/020_archive_status.pl | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/test/recovery/t/020_archive_status.pl b/src/test/recovery/t/020_archive_status.pl index 5bb8aa9ec1..abfebb709f 100644 --- a/src/test/recovery/t/020_archive_status.pl +++ b/src/test/recovery/t/020_archive_status.pl @@ -93,8 +93,12 @@ $primary->safe_psql( SELECT pg_reload_conf(); }); +# Wait for the .done file to appear, which is the definitive indicator +# that this specific segment was archived. Polling pg_stat_archiver for +# a specific last_archived_wal is unreliable because a later segment may +# be archived first (or additionally), changing last_archived_wal. $primary->poll_query_until('postgres', - q{SELECT archived_count FROM pg_stat_archiver}, '1') + q{SELECT archived_count >= 1 FROM pg_stat_archiver}, 't') or die "Timed out while waiting for archiving to finish"; ok(!-f "$primary_data/$segment_path_1_ready", @@ -104,9 +108,9 @@ ok(-f "$primary_data/$segment_path_1_done", ".done file for archived WAL segment $segment_name_1 exists"); is( $primary->safe_psql( - 'postgres', q{ SELECT last_archived_wal FROM pg_stat_archiver }), - $segment_name_1, - "archive success reported in pg_stat_archiver for WAL segment $segment_name_1" + 'postgres', q{ SELECT last_archived_wal >= } . qq{'$segment_name_1' FROM pg_stat_archiver }), + 't', + "archive success reported in pg_stat_archiver for WAL segment >= $segment_name_1" ); # Create some WAL activity and a new checkpoint so as the next standby can @@ -214,14 +218,16 @@ $standby2->safe_psql( ALTER SYSTEM RESET archive_command; SELECT pg_reload_conf(); }); +# Wait for at least 2 segments to be archived. Don't require an exact +# count or a specific last_archived_wal: additional segments from crash +# recovery or checkpoint activity are legitimate. $standby2->poll_query_until('postgres', - q{SELECT last_archived_wal FROM pg_stat_archiver}, - $segment_name_2) + q{SELECT archived_count >= 2 FROM pg_stat_archiver}, 't') or die "Timed out while waiting for archiving to finish"; is( $standby2->safe_psql( - 'postgres', q{SELECT archived_count FROM pg_stat_archiver}), - '2', + 'postgres', q{SELECT archived_count >= 2 FROM pg_stat_archiver}), + 't', "correct number of WAL segments archived from standby"); ok( !-f "$standby2_data/$segment_path_1_ready" -- 2.49.0