Stop a shard stacking its replicas on one host - #232
Open
bjmeetsfo wants to merge 2 commits into
Open
Conversation
A shard could put both its replicas on the same physical host while another host sat idle. It reported the full replica count and lost all of them to one host failure. push_replica records used_hosts but does not enforce it -- only the separation ladder above checked it. When every server shares a location the ladder rejects every candidate after the first, so every replica after the first came from the unconditional fallback fill, which never looked at used_hosts at all. Four datanodes, two per host, one rack, replica_count 2: both replicas landed on the first host. One rack is not an exotic case. It is what a cluster looks like before it grows into multiple availability units, and it is exactly when the ladder can separate nothing and the fallback does all the work. The fallback now fills in two passes: a host already holding a replica of this shard is taken only when nothing else is left. That keeps its purpose, reaching the replica count, without spending redundancy it did not have to spend. Placement is otherwise untouched -- the load-based ordering, the ladder, the owner handling and primary selection all behave as before.
bjmeetsfo
force-pushed
the
oss/spread-replicas-across-hosts
branch
from
August 24, 2026 18:09
c7df088 to
eae8a9c
Compare
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.
The problem
A shard could put both its replicas on the same physical host while another host
sat idle. It reported the full replica count, and one host failure took all of
them.
Four datanodes, two per host, all in one rack — an ordinary small deployment:
Why.
push_replicarecordsused_hostsbut does not enforce it — onlythe separation ladder above checked it. When every server shares a location, the
ladder rejects every candidate after the first (
shared == depth, never< separation), so every replica after the first came from the unconditionalfallback fill, which never looked at
used_hostsat all.The one-rack case is not exotic. It is what a cluster looks like before it grows
into multiple availability units, and it is exactly when the ladder can separate
nothing and the fallback does all the work.
The change
The fallback fills in two passes instead of one: a host already holding a
replica of this shard is taken only when nothing else is left.
That keeps the fallback's purpose — reach
replica_count— while not spendingredundancy it did not have to spend.
Tests
without the change.
replica_count: 2, still returns two replicas.The second is the one that matters for review: the point is to spread, not to
refuse, and it would be easy to "fix" this into a shard that comes back short.
It passes with and without the change, on purpose — it guards the fix rather
than proving it.
Scope
Placement order otherwise is untouched: the load-based candidate ordering, the
separation ladder, the owner handling and the primary selection all behave
exactly as before. Only the final fill changed.
Verification
cargo check --all-targets— 0 errorscargo test --bin metaserver -- --test-threads=1cargo test --lib -- --test-threads=1The
data_node::…jitter_backoff…andengine::…recovery_validates_…failuresreproduce on an unmodified tree at this base and are untouched here.
Correction to the verification note above
I described two failures as reproducing on an unmodified tree. Re-checked on
a quiet machine with disk headroom, run in isolation against unmodified
main:data_node::…jitter_backoff…engine::…recovery_validates_all_timestamped_kv_page_familiesOnly the first is pre-existing. My original evidence for the second came from a
run taken immediately after the disk hit 100%, so it was environmental — that
test is sensitive to disk pressure and load, not broken on
main.The conclusion this change is not responsible for either failure is unchanged.
The evidence offered for half of it was wrong, and the record should say so.