Learn
Lesson · Storage Engines and Indexes · Durability and layout

fsync and Group Commit

Durability is a count of fsyncs. One commit per fsync on a 10 ms device is 100 durable writes a second, whatever the CPU and the network could have done. Grouping sixty-four commits into one fsync makes it 6 400 a second — paid for with a wait to fill the batch, which is why commit latency has a minimum at a batch size rather than falling for ever.

durable writes a second ≤ 1 / t_fsync one commit per fsync grouped B at a time ≤ B / t_fsync t_fsync/B each t_fsync = 10 ms B = 1 100 a second B = 64 6 400 a second, 156.3 µs of fsync each paid for with a wait: (B − 1)/2λ to fill the batch, then the queue
The durability ceiling, what grouping buys, and what the batch costs

Core ideas

What this lesson establishes

One hard ceiling, one way around it, and the price of that way — which is the only part that needs arithmetic.

1

Durability is a count of fsyncs, not a count of bytes

A write() that returns has put bytes in the operating system's page cache. The disk learns about them when something calls fsync, and until then a power loss takes them. So the durable write rate is bounded by how many fsyncs a second the device can complete: 1/t_fsync, which at 10 ms is 100 — a number that has nothing to do with how fast anything else is.

2

Grouping divides one fsync among a batch

Nothing says one commit must get one fsync. Collect B commits, write them all, call fsync once, and acknowledge all B: the ceiling becomes B/t_fsync and each write carries t_fsync/B of the cost. At B = 64 on a 10 ms device that is 6 400 durable writes a second and 156.3 µs of fsync apiece, on the same disk.

3

The batch is paid for in latency, and the total has a minimum

A write that arrives first waits for the rest of its batch — about (B − 1)/2λ at arrival rate λ — and then waits in the queue in front of the fsync. Raising B lengthens the first wait and shortens the second, so commit latency falls, flattens and rises again. On the worked settings it bottoms at B = 121 and 29.04 ms.

The material

The durability ceiling, and what a batch buys and costs

Why a returning write is not a durable one, what grouping does to the ceiling, and where the latency minimum sits.

Definition · Durable write, fsync, group commit

A write is durable when it survives the loss of power. fsync is the call that forces buffered bytes onto the device and returns when they are there; t_fsync is how long it takes. Group commit is the practice of batching B pending commits behind a single fsync and acknowledging them together.

The durable write rate is at most 1/t_fsync without grouping and B/t_fsync with it. Both are ceilings, not achieved rates.

The misconception this lesson is aimed at is that a successful write() means the data is safe. It means the bytes reached the page cache, which is memory, which is exactly the thing a power loss empties. Every engine that promises durability is therefore paying for fsyncs, and the count of them is the whole budget.

t_fsync = 10 ms = 0.01 s arrivals λ = 5 000 a second B = 1 cap 1/0.01 = 100 durable writes a second batches arrive at 5 000/s, served at 100/s → ρ = 50 no steady state: the backlog grows without bound B = 64 cap 64/0.01 = 6 400 durable writes a second fsync per write 0.01/64 = 156.3 µs fill wait (64 − 1)/(2 × 5 000)= 6.30 ms batches 5 000/64 = 78.125/s against 100/s → ρ = 0.781 queue + fsync 1/(100 − 78.125)= 45.71 ms commit latency 6.30 + 45.71 = 52.01 ms

The B = 1 row is worth reading twice. At five thousand writes a second arriving and one hundred fsyncs a second available, the utilisation is 50 and there is no mean latency to quote at all — the backlog simply grows. The ceiling arrives as a queueing fact rather than as an assertion, which is the M/M/1 model of Queues and Utilisation reused rather than a second model invented here.

What the batch costs

Two waits, moving in opposite directions. The fill wait is how long a write sits while its batch assembles, about (B − 1)/2λ on average, and it grows linearly in B. The queue in front of the fsync shrinks as B grows, because batches arrive at λ/B while the fsync still serves at 1/t_fsync, so the utilisation ρ = λ t_fsync/B falls. Adding the two gives a curve with an interior minimum.

Example · Scanning the curve

At λ = 5 000 and t_fsync = 10 ms: B = 64 gives 6.30 ms of fill and 45.71 ms of queue, 52.01 ms in total. B = 121 gives 12.00 ms and 17.04 ms, 29.04 ms in total — the lowest on the curve. B = 256 gives 25.50 ms and 12.43 ms, 37.93 ms, and is past the minimum: the batch is now the problem. The durable ceiling rose the whole way, from 6 400 to 25 600 a second, while the latency turned around.

The lab finds that minimum by evaluating every B rather than by a formula, and it says so. A closed form exists for this shape of bargain — a fixed cost amortised over a batch against a cost that grows with the batch — and it belongs to “The EOQ Formula Without Calculus” on the Operations Research path. The same trade appears again as “Batching: Cost and Latency” in Scaling Laws and Cost, with throughput in place of durability and a per-item cost in place of a holding cost.

Two things this model takes on trust, both of which have cost people data. It assumes t_fsync is what the device actually does, and a device with a volatile write cache that acknowledges an fsync before the platter has it will report a delightful figure and lose your commits anyway — measure it, do not read it off a datasheet. And it assumes a single thread issuing the fsyncs, which is what makes the M/M/1 arithmetic apply.

Interactive

fsync and group commit

The durability cap, what grouping buys, and the wait it costs

write() returning is not durability1/tfsync durable writes a second, or B/tfsync grouped
writes filling a batchthe fsyncthe wait grouping coststhe batch that minimises latency

Method

Pricing durability

The ceiling takes one division. The interesting part is the curve underneath it.

1

Measure t_fsync on the device you will use

Not the datasheet figure, and not the write latency. An fsync is a round-trip to durable media, and a device that returns from it too quickly is usually acknowledging from a volatile cache.

2

Divide one second by it

1/t_fsync is the ungrouped ceiling: 100 a second at 10 ms, 1 000 at 1 ms. Nothing above the line matters if this number is below what the workload needs — no amount of CPU or network moves it.

3

Choose B and recompute the ceiling and the per-write cost

B/t_fsync durable writes a second, at t_fsync/B of fsync each. This is the only lever that raises the ceiling without changing hardware.

4

Add the fill wait and the queue, then scan B

(B − 1)/2λ plus the sojourn at utilisation ρ = λ t_fsync/B. Evaluate it across the batch sizes you would consider and take the minimum; it is interior, so neither the smallest nor the largest B wins.

Ceilings, batches and waits

Score 0 / 0

A 10 ms fsync under 5 000 writes a second

One division for the ceiling, then the two waits that decide the batch.

t_fsync = 10 ms = 0.01 s arrivals λ = 5 000 a second B = 1 ceiling 1/0.01 = 100 durable writes/s batches at 5 000/s served at 100/s → ρ = 50, no steady state B = 64 ceiling 64/0.01 = 6 400 durable writes/s fsync per write 0.01/64 = 156.3 µs fill wait (64 − 1)/(2 × 5 000) = 6.30 ms ρ = λ·t/B = 5 000 × 0.01 / 64 = 0.781 queue + fsync 1/(100 − 78.125) = 45.71 ms commit latency 6.30 + 45.71 = 52.01 ms scanning the curve B = 64 ceiling 6 400/s fill 6.30 ms queue 45.71 ms total 52.01 ms B = 121 ceiling 12 100/s fill 12.00 ms queue 17.04 ms total 29.04 ms lowest B = 128 ceiling 12 800/s fill 12.70 ms queue 16.41 ms total 29.11 ms B = 256 ceiling 25 600/s fill 25.50 ms queue 12.43 ms total 37.93 ms

The two middle columns move in opposite directions and neither of them is the answer on its own. The ceiling rises monotonically with B all the way to 25 600 a second, so a page that reported only throughput would recommend the largest batch available — and it would be recommending 37.93 ms of commit latency over 29.04 ms.

The minimum here is B = 121, found by evaluating the curve rather than by differentiating it. The closed form for this shape of bargain belongs to “The EOQ Formula Without Calculus” on the Operations Research path; this course chooses B by looking, and says that a formula exists and where it lives.

For a faded rehearsal, keep λ = 5 000 and take the fsync down to 1 ms — a device an order of magnitude better. The supplied first move is the new ungrouped ceiling: 1 000 durable writes a second. Find the batch that minimises commit latency and the latency there, then say what happened to both relative to the 10 ms device. Check in the lab before opening the quiz.

Common mistakes

1
Reading a successful write() as durabilityIt is an acknowledgement from memory. Everything an engine promises about surviving power loss is bought with fsyncs, and the number of them a second is a hard ceiling set by the device. A system whose durability claim is not backed by a count of fsyncs per second does not have a durability claim.
2
Quoting the grouped ceiling as though latency were unchanged64 commits a batch raises the ceiling from 100 to 6 400 a second and adds 6.30 ms of fill wait before the queue is even considered. Throughput and latency move in opposite directions here, and the honest report gives both figures at the chosen B.
3
Raising B until throughput is sufficient and stoppingThe latency minimum is interior. At these settings B = 256 clears any plausible throughput target and costs 37.93 ms where B = 121 costs 29.04 ms. Choosing the batch on the throughput axis alone lands on the wrong side of a curve that was cheap to evaluate.

Check your understanding

✓
Finish when a durability claim sounds like a rate, and a batch size sounds like a latency decision.

You should be able to state the ungrouped ceiling from an fsync time, compute what a given batch does to the ceiling and to the per-write cost, and find the batch that minimises commit latency by evaluating the curve.

Note: The fsync caps how quickly durable bytes land. What a query has to read back is decided somewhere else entirely — by how the values are laid out on the page. “Row vs Column Storage” counts the bytes a scan touches each way and finds the query on which the answer reverses.

Kept in this browser only, for you. Nothing is sent anywhere and nothing checks it. Carry it to another device.

Feedback and recommendations

Something to change about this lesson? Write it down, tick it off when it is handled, and export the list when you want to act on it. Kept in this browser only: nothing is sent anywhere, and nobody is notified.

No recommendations yet for this lesson.