Skip to content

Fsync the directories a follower creates with the next fsync request - #2555

Merged
viktorerlingsson merged 7 commits into
mainfrom
defer-follower-dir-fsyncs
Oct 27, 2026
Merged

viktorerlingsson merged 7 commits into
mainfrom
defer-follower-dir-fsyncs

Conversation

@weistrand

@weistrand weistrand commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Why

#2296 made a follower create directories with FileSystem.mkdir_p, which fsyncs the parent of each directory it creates. That runs inline on the replication stream, so the first file of every new queue or MQTT session held up the follower's stream for an fsync. Declares wait for the followers, so on a cluster each new queue cost the leader that fsync's latency too. 2.10 created them without an fsync. Raised in the review of #2544.

What

The follower creates directories without fsyncing them and remembers the parent of each one it creates. Those parents are fsynced with the next fsync request, before the ack that makes anything in them durable. A syncfs, from a v1 leader or after a full sync, covers them, so they're forgotten then.

A replace's ack doesn't cover a directory created for it. The only such replace is a new queue's .queue file, which the leader doesn't fsync either.

FileSystem.mkdir_p takes a block that gets each created parent, so the follower doesn't keep its own copy of it.

Follower fsyncs counted with strace on a release build, a leader and one follower, 2,000 durable queues:

base this PR
Import 2,000 queues as definitions 5,515 2,636
of which on the replication stream 4,001 2,000
Declare 2,000 queues, one at a time 8,002 8,000
of which on the replication stream 4,001 2,000

Each declare makes the leader request a fsync of definitions.amqp, so the vhost directory is fsynced with that request, in the ack loop instead of on the stream. An import makes one request for all its queues, and they share a parent. The fsyncs left on the stream are replace's fsync of .queue.tmp.

How

make test

@claude

claude Bot commented Oct 26, 2026

Copy link
Copy Markdown

No issues found.

Comment thread src/lavinmq/clustering/client.cr Resolved
weistrand and others added 7 commits October 27, 2026 17:03
`FileSystem.mkdir_p` now takes a block that gets the parent of each directory it creates. The follower's `make_dirs` uses it to collect the parents for its next fsync request, instead of keeping its own copy of the directory walk.
A directory the follower creates for a replaced file is fsynced with the next fsync request, not before the replace is acked. The only such replace is a new queue's `.queue` file, which the leader doesn't fsync either.
A replace's new directories are fsynced with the next fsync request, and a syncfs, from a request without a path or a full sync, makes the next request skip them.
A v1 leader never sends fsync requests, so `@created_dirs` was only
cleared on a full sync. Clear it in `send_ack_loop` before each syncfs
instead, so a dir created while the syncfs blocks stays marked.
@viktorerlingsson
viktorerlingsson force-pushed the defer-follower-dir-fsyncs branch from b23b462 to 0f1e718 Compare October 27, 2026 16:07
@viktorerlingsson
viktorerlingsson merged commit 23858c9 into main Oct 27, 2026
24 checks passed
@viktorerlingsson
viktorerlingsson deleted the defer-follower-dir-fsyncs branch October 27, 2026 16:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants