Re: walsummarizer can get stuck when switching timelines

From: Andrey Borodin <x4mmm(at)yandex-team(dot)ru>
To: Robert Haas <robertmhaas(at)gmail(dot)com>
Cc: "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: walsummarizer can get stuck when switching timelines
Date: 2026-07-14 12:40:55
Message-ID: 5FEEA309-3417-43D6-8961-67D311C680D3@yandex-team.ru
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

> On 14 Jul 2026, at 01:39, Robert Haas <robertmhaas(at)gmail(dot)com> wrote:
>
> Here is a patch, with a test case.

Hi Robert!

I read the patch and I think it's correct. Two small things on the new
fallback. First, the test covers a single switch; the tricky case is several
switches within one segment, where the fallback loop and the switch_lsn bound
interact per timeline. It seems correct to me: we stop at switch_lsn, so we
only read the pre-switch bytes both timelines share. But it might be worth a
test. Second, in the fallback path WALRead reopens the segment on every page,
because the requested tli never matches seg.ws_tli once
summarizer_wal_segment_open() has moved to a descendant. These open/close
might be an exatra overhead?

I think, there's a symmetric bug on the recovery side with the same root cause.
Your patch makes the summarizer fall forward to a descendant timeline, which
looks safe. XLogFileReadAnyTLI() does the opposite: when the switch-point segment
is missing on the target timeline, it falls back to an ancestor and reads the
same segno. But that segment holds divergent data continuing old timeline!
Recovery applies it silently and then can't reach the intended timeline.

The comment you added to t/003_tli_switch.pl states the assumption that breaks
here:

# ... recovery will read them from there and work just fine.

That holds only when the new timeline's segment is already there. Concretely,
with just two timelines: TL2 forks from TL1 in segment 67, which is archived
for TL1 but not yet for TL2. Recovery targets TL2 and asks for segment 67;
TL2's copy isn't in the archive, so XLogFileReadAnyTLI() falls back to TL1 and
hands back TL1's segment 67. Past the switch point that is the old primary's
divergent WAL, recovery applies it, and now it can never get onto TL2. The
beginseg check doesn't help here - it only skips a timeline for segments older
than that timeline's start, so nothing stops the fallback to the ancestor TL1
for the very segment where the two diverge. I reported it with a fix [0].

Regards, Andrey Borodin.

[0] https://postgr.es/m/85386EF6-16B7-4D62-86BE-526A10F93825%40yandex-team.ru (CF #6525)

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Zsolt Parragi 2026-07-14 12:45:04 pg_dump: ALTER INDEX SET STATISTICS missing for index-backed constraints
Previous Message Tatsuo Ishii 2026-07-14 12:20:47 Re: Row pattern recognition