Writers waiting on the metadata log share a barrier - #520
Open
bjmeetsfo wants to merge 3 commits into
Open
Conversation
Every recorded change takes a durability barrier, and the barrier is nearly
the whole cost. Of the 2.6ms an append takes, 2.1ms is the write and the
sync; opening the file is 5us and copying the change is 0.2us.
So writers did not go faster together than alone:
writers before after
1 409 rec/s 398 rec/s
4 452 rec/s 944 rec/s
16 460 rec/s 2573 rec/s
Flat before, because each writer took its own barrier and the next one
waited. A cluster coming back -- every datanode registering every shard it
holds -- paid one barrier per shard however many datanodes were doing it.
The barrier a writer needs is not its own. The file is append-only and the
writes are ordered by the lock, so any sync beginning after a writer's bytes
reached the file covers them. Numbering the records lets a writer notice that
somebody else's sync already covered it and return without taking another.
The part that is easy to get wrong, and the reason this is written the way it
is: how far the log has been written is read BEFORE the sync and published
only AFTER it returns. The other order would tell a writer its bytes were
durable while the sync was still running.
Test: eight writers, twelve records each, and every record every writer was
told had landed is in the log exactly once.
How many barriers that took is deliberately not asserted. The barrier counter
is process-wide and every other test in the binary adds to it, so a count read
in a test says nothing about this log -- and a test that passes alone and
fails in the suite is worse than no test. That is how the first version of
this one behaved.
Sharing the barrier split the write from the sync, and that meant a record
opened the file twice where it used to open it once. Measured end to end --
registering shards through the metaserver, not appending to the log directly
-- that cost a lone writer more than sharing saved it:
writers before split barrier handles kept
1 499/s 363/s 490/s
4 467/s 678/s 858/s
16 478/s 1461/s 1941/s
A metaserver with one writer is the ordinary case, so a 27% loss there was
not worth the win under load. Both handles are now opened once and kept.
Syncing is per file rather than per handle, so the second handle still covers
what the first one wrote, and the barrier still runs outside the write lock --
which is what lets the writer behind this one get its bytes down meanwhile.
Holding the handle open is what the write-ahead log next door already does.
Nothing in the tree rewrites this file: the only truncate nearby belongs to
snapshot export, which writes a temporary file and renames it.
Measured twice, arms interleaved, because the box is shared -- the second pair
ran under load 12.4 and the numbers moved together while the shape held.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every recorded change to the metadata takes a durability barrier, and the barrier is nearly the whole cost. Of the 2.6ms an append takes, 2.1ms is the write and the sync; opening the file is 5us and copying the change is 0.2us.
So writers did not go faster together than alone. Measured end to end -- registering shards through the metaserver, not appending to the log directly:
Flat before, because each writer took its own barrier and the next one waited. A cluster coming back -- every datanode registering every shard it holds -- paid one barrier per shard however many datanodes were doing it.
Why it is safe
The barrier a writer needs is not its own. The file is append-only and the writes are ordered by the write lock, so any sync beginning after a writer's bytes reached the file covers them. Numbering the records lets a writer notice that somebody else's sync already covered it and return without taking another.
The part that is easy to get wrong, and the reason it is written this way: how far the log has been written is read BEFORE the sync and published only AFTER it returns. The other order would tell a writer its bytes were durable while the sync was still running.
The first version of this was slower for one writer
Splitting the write from the sync meant a record opened the file twice where it used to open it once. End to end that cost a lone writer more than sharing saved it -- 499 registrations a second became 363 -- and one writer is the ordinary case for a metaserver.
Both handles are now opened once and kept, which is what the write-ahead log next door already does. Syncing is per file rather than per handle, so the barrier handle still covers what the write handle wrote, and the barrier still runs outside the write lock.
Nothing in the tree rewrites this file: the only nearby truncate belongs to snapshot export, which writes a temporary file and renames it.
Testing
Eight writers, twelve records each, and every record every writer was told had landed is in the log exactly once.
How many barriers that took is deliberately not asserted. The barrier counter is process-wide and every other test in the binary adds to it, so a count read in a test says nothing about this log -- and a test that passes alone and fails in the suite is worse than no test. That is how the first version of this test behaved.
Measurements were taken twice with the arms interleaved, because the box is shared. The second pair ran under load 12.4 and the absolute numbers moved together while the shape held.
Suites: 327 metadata tests, 246 consensus tests, 47 metaserver binary tests, all green.