Partition Boxes by key hash with balanced, fenced per-partition ownership#19
Merged
Merged
Conversation
…ship
Removes the single-node write bottleneck per Box: every Box is split into
a creation-time-fixed number of hash partitions (CRC32C of the key, mod
the count in the new ZK BoxDescriptor; default 8), each an independent
LSM engine (WAL/memtable/manifest/SSTables/Syrups) under its own fenced
ZooKeeper ownership lease, so one Box's writes spread across the cluster.
Design and decisions are captured in BOX_PARTITIONING_PLAN.md.
- Coordination: per-partition owner/manifest keys plus boxes/<box>/meta
(immutable descriptor) and cluster/{balancer,assignment}; new
CoordinationService.children() (fake + ZK + contract test).
- Server: BoxOwnership becomes PartitionOwnership; CandyboxNode keys
ownership/maintenance by (box, partition); createBox publishes the
descriptor and owns all partitions initially; deleteBox takes over or
(force) defers to the owners' deleted-box sweep; boxExists/listBoxes
now answer cluster-wide from coordination.
- Balancer (new): every node runs rounds; the cluster/balancer lease
holder is the elected coordinator publishing a sticky, failover-eager
assignment with rate-limited moves away from live owners
(balancer.max.moves.per.round); all nodes converge on it, releasing
with a pre-handover flush and acquiring once leases free up.
- Protocol: partition fields on list/delete-range/list-uploads requests,
CreateBox partitionCount, new BoxInfo request/response; MOVED now
names the requested partition's owner.
- Client: routes keyed ops (incl. all multipart ops) to the key's
partition owner, caches descriptors, scatter-gathers listings in
global key order, fans out range deletes, and falls back to a byte
copy for cross-partition copy/rename/uploadPartCopy.
- Fix: recovering a partition whose WAL is empty observed Hlc.MIN and
tripped an overflowing skew check in HybridLogicalClock.observe; skip
the observe on empty replay and make the check overflow-safe.
- Tests: Partitioning/BoxDescriptor/PartitionBalancer units, the
fakes-based PartitionedBoxIT (multi-node partitioned Box through the
cluster client), and updates across the existing suites; docs/conf
updated (DESIGN.md, OPERATIONS.md, README, candybox.properties).
https://claude.ai/code/session_015ZRRX63Hie7PDKxsH4Zosn
✅ S3 compatibility gate passed
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #19 +/- ##
============================================
+ Coverage 83.01% 84.05% +1.04%
- Complexity 518 528 +10
============================================
Files 125 129 +4
Lines 6435 6925 +490
Branches 906 1014 +108
============================================
+ Hits 5342 5821 +479
+ Misses 726 719 -7
- Partials 367 385 +18 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…y codecov The codecov patch gate flagged 138 uncovered lines in the partitioning change. Adds targeted unit tests for the cold branches: - NodeRequestHandler: MOVED redirection for every keyed/partitioned request type, BoxInfo dispatch, the NotOwner deleteBox error path, and the cross-partition zero-copy rejection. - CandyboxNode: createBox rollback on a half-created Box, non-force deleteBox against a live foreign owner, releaseBox/openBox replay. - CandyboxClient: scatter-gather merge order/truncation/reverse, descriptor caching + invalidation on deleteBox, delete-range fan-out, merged multipart listings, and the cross-partition copy/rename/ uploadPartCopy byte-copy fallbacks (against a recording stub node). - CandyboxConfig partitioning/balancer knobs + validation; PartitionAssignment round-trip (and drops its unused targetNode); dashboard owner summary for spread partition ownership. https://claude.ai/code/session_015ZRRX63Hie7PDKxsH4Zosn
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.
Summary
Removes the single-node write bottleneck per Box: every Box is now split into a creation-time-fixed number of hash partitions (CRC32C of the key, mod the count in the new ZK
BoxDescriptor; default 8,partitions.per.box.default). Each partition is an independent LSM engine (WAL / memtable / manifest / SSTables / Syrups) under its own fenced ZooKeeper ownership lease, so the partitions of one Box are owned and served by multiple nodes concurrently. Design, decisions, and trade-offs are captured inBOX_PARTITIONING_PLAN.md.Coordination
boxes/<box>/meta(immutable descriptor),boxes/<box>/partitions/<p>/{owner,manifest}, pluscluster/{balancer,assignment}.CoordinationService.children()(fake + ZooKeeper impl + shared contract test) for Box enumeration.Server
BoxOwnership→PartitionOwnership; the lease + recover + pointer-CAS handover sequence is unchanged, at partition granularity.CandyboxNodekeys ownership, heartbeat, compaction/GC/multipart-sweep by(box, partition).createBoxpublishes the descriptor and owns all partitions initially;deleteBoxtakes over free partitions (or, withforce, defers to the owners' deleted-box sweep);boxExists/listBoxesnow answer cluster-wide from coordination.PartitionBalancer(elected coordinator under thecluster/balancerlease): publishes a sticky, failover-eager assignment; migration away from live owners is rate-limited (balancer.max.moves.per.round, default 4) while dead-node failover is never limited; releases flush the memtable first so the next owner's WAL replay is tiny. This also adds automatic failover, which previously only existed as a manual/test path.Protocol & client
CreateBoxcarries an optional partition count; newBoxInforequest/response;MOVEDnames the requested partition's owner.lastKeycursor still works), fans out range deletes, and falls back to a client-side byte copy for cross-partition copy/rename/uploadPartCopy(same-partition stays zero-copy and atomic).Bug fix
Recovering a partition whose current WAL is empty (now the common case thanks to the pre-handover flush, but reachable before this change too) called
hlc.observe(Hlc.MIN), and the skew check inHybridLogicalClock.observeoverflowed and threw. Fixed by skipping the observe on an empty replay and making the check overflow-safe.Tests & docs
PartitioningTest,BoxDescriptorTest,PartitionBalancerTest(even spread, rate limit, stickiness, failover, deleted-box sweep), andPartitionedBoxIT— a fakes-based multi-node IT driving a partition-spread Box through the cluster client (routing, merged listings, fan-out deletes, cross-partition copy fallbacks, multipart).ServerClientLifecycleITnow runs with 2 partitions per Box over embedded BK).candybox.properties.example(balancer enabled at 5 s) updated.All unit suites and all 12 integration test classes pass locally (
mvn test,mvn verify -pl candybox-integration-tests).https://claude.ai/code/session_015ZRRX63Hie7PDKxsH4Zosn
Generated by Claude Code