Skip to content

Partitioning & Sharding

LoomCache uses PartitionRouter, PartitionTable, and a consistent hash ring (ConsistentHashRing) for routing and ownership planning. Today the default data model is full replication — every node in the single Raft group holds every partition, so partitioning is primarily an affinity/routing hint rather than a sharded placement strategy. The ingredients for per-partition Raft groups ship in the sharding/ package and are exercised by tests; production activation is unsupported/fail-closed until per-group WAL, Raft metadata, snapshot, install-snapshot, and restart recovery are proven.

Consistent Hash Ring

Keys are hashed to 271 partitions mapped cleanly across virtual nodes, decoupling data from physical servers.

N-1
N-2
N-3
Hash(Key)
Physical Node
Active Slot Route

Rather than hashing keys directly to nodes, LoomCache hashes sharded-Raft keys to one of 271 routing partitions by default. That count matches Hazelcast’s default partition count and is intentionally small enough for compact client routing-table refreshes.

[!NOTE] Because LoomCache maps through routing partitions and server-side ownership slots instead of hashing directly to nodes, adding or removing a node only requires moving specific slices of the keyspace. That minimizes network impact compared to a traditional mod-N hash ring.

partition = MurmurHash3(key) % 271

The server publishes PartitionRouter snapshots to clients. The client SDK uses the same hash function for smart routing — sending requests directly to the owning group without an extra redirect hop — while PartitionTable and ConsistentHashRing handle server-side ownership planning during membership changes.

LoomCache separates three counts that older comparisons sometimes collapsed into “1024 vs 271”:

  • PartitionRouter.DEFAULT_NUM_PARTITIONS = 271 controls smart-client and multi-Raft-group routing.
  • PartitionTable.TOTAL_SLOTS = 16_384 controls membership-migration ownership planning.
  • ConsistentHashRing.VNODES_PER_NODE = 256 controls how many virtual positions each physical member contributes to the ring; a four-member cluster therefore has 1,024 ring positions.

More slots or virtual nodes make rebalancing smoother because ownership can move in smaller chunks and hot keys are less likely to cluster on one member. The cost is larger metadata: every owner table, migration preview, and membership broadcast grows linearly with the slot count. As a rule of thumb, an int routing table is about 1 KiB at 271 entries, about 4 KiB at 1,024 entries, and about 64 KiB at 16,384 entries before protocol and JVM object overhead. LoomCache keeps the client-facing routing table at 271 entries and uses the larger slot table only for server-side migration planning.

Cluster SizePartitions per NodeQuorum
3 nodes~90 each2
5 nodes~54 each3
7 nodes~38 each4

Partition Migration (Dynamic Scaling)

Non-blocking state transfer when topologies change

CLUSTER STABLE
Node 1
Slot 1
Slot 2
Node 2
Slot 3
Slot 4
Node 3
Slot 5
Slot 6

When a development sharded cluster scales out, the PartitionMigrationManager negotiates a new layout via Raft and streams slot data in the background. This is not a production sharding claim; production must stay on the single-group model until the recovery gaps above are closed.

  1. New node joins and announces itself via discovery
  2. PartitionTable computes the optimal slot redistribution
  3. Migration plan is committed through Raft consensus (all nodes agree)
  4. Data streams in the background — source node sends slot data to target
  5. Individual slots are briefly paused during the ownership handoff
  6. Client routing tables are updated — subsequent requests go to the new owner

LoomCache tracks fine-grained metrics for each hash slot via SlotMetrics:

  • Access count — requests hitting this slot
  • Key count — number of keys stored in this slot
  • Memory usage — bytes consumed by this slot’s data

Use SlotMetrics.getTopSlots(n) to identify hot partitions and rebalance proactively.

The RequestRouter in the client SDK uses the same MurmurHash3 function and partition table to route requests to the best known owner or leader:

// Client internally computes:
int partition = MurmurHash3.hash(key) % 271;
NodeInfo owner = partitionTable.getOwner(partition);
// Sends request directly to owner — zero redirects

Redirects remain part of the correctness path when leadership or ownership information is stale.