Re: [Patch] Batch fsyncs when recycling WAL segments

From: Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
To: PostgreSQL Hackers <pgsql-hackers(at)postgresql(dot)org>
Cc: Andres Freund <andres(at)anarazel(dot)de>, Nathan Bossart <nathandbossart(at)gmail(dot)com>
Subject: Re: [Patch] Batch fsyncs when recycling WAL segments
Date: 2026-08-18 18:52:56
Message-ID: CAJTYsWW5nRTuPkU+mwM2ohN3L4SxN4xwca-5a7s99dOr49Roug@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

On Sun, 2 Aug 2026 at 16:18, Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
wrote:

> Hi,
>
> While working on [1], I read on the "Pre-allocating WAL files" thread [2]
> that the fsyncs we do when recycling WAL segments are a real bottleneck,
> and Andres suggested batching them: rename a batch first, then fsync the
> files and pg_wal once, instead of the current per-segment
> durable_rename() in InstallXLogFileSegment(). The attached patch does
> that. (Andres and Nathan, who were on that thread, are cc'd.)
>
> The tricky part was durability. As soon as a segment is renamed it can
> be handed to the WAL write path, but the rename isn't on disk yet, and
> issue_xlog_fsync() only fsyncs the segment file, never the directory.
> So if a backend writes a commit into a just-recycled segment and we
> crash before the batched fsync, the rename can be lost.
>
> What I ended up with is a small "durability frontier" in XLogCtl (a
> timeline and the highest segment whose rename is known durable). The
> checkpointer advances it after the batched fsync, and XLogFileInit()
> checks it before letting anyone write into a segment; if the segment
> isn't covered yet it just fsyncs the file and pg_wal itself. Normally
> the checkpointer stays ahead, so that fallback basically never fires(?)
> it's
> only there to close the crash window. An injection point and a TAP test
> force that window open and confirm a committed row survives an immediate
> crash.
>
> Rough numbers: a WAL-heavy workload recycling ~44 segments per
> checkpoint (min_wal_size=1GB, max_wal_size=2GB, wal_recycle=on,
> fsync=on), ext4 on an Azure disk, 14 checkpoints with the first dropped:
>
> median mean min max
> master 138.6 ms 143 ms 123 ms 171 ms
> patch 27.3 ms 28 ms 25 ms 39 ms
>
> ~5x here, and a lot steadier: master pays ~44 flushes every checkpoint,
> the patch pays one.
>
> Is the frontier + write-path check the right shape for this, or is there a
> simpler invariant that keeps the write path from ever reaching a
> not-yet-durable recycled segment?
>
> Thoughts?
>
> Regards,
> Ayush
>
> [1]
> https://www.postgresql.org/message-id/CAJTYsWXAPabSWBeT%3DbqJD%2B6w0D4-ozvoVoH7KiFUncM88bsQOg%40mail.gmail.com
> [2]
> https://www.postgresql.org/message-id/745zvagaf6tfn2zbvtmqvxsr6kmybjbzcw6rsm55jnql4233je%40tudeipoewruo
>

While testing v1 I noticed that the TAP test still passed if I removed the
EnsureXLogSegDirDurable() call. An immediate server stop does not discard
the
kernel's filesystem state, so that did not prove that the write-path
fallback
had run. v2 adds an injection point in the fallback and waits for the WAL
writer to reach it while the checkpointer is stopped before the batch fsync.
With the fallback removed, the test now fails at that wait.

Added the test to the Meson list as well. It was missing earlier.

I also checked the segment-size question. On ext4, with fsync enabled and
512 MiB recycled per checkpoint, the median of five checkpoints was:

segment size files master patched
1 MiB 512 1900 ms 80 ms
16 MiB 32 156 ms 49 ms
64 MiB 8 46 ms 21 ms

Regards,
Ayush

Attachment Content-Type Size
v2-0001-Batch-fsyncs-when-recycling-WAL-segments-guarded-.patch application/octet-stream 21.9 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message shihao zhong 2026-08-18 19:32:40 Re: [PATCH] Add planner support function to two-argument regexp_like()
Previous Message Pavel Stehule 2026-08-18 18:47:32 Re: missing possibility to use alternative translated month names in to_char function