moonshardkit

    Deterministic shard placement, replica isolation and migration planning for MoonBit.

    consistent-hashing
    rendezvous-hashing
    sharding
    replica-placement
    migration-planning
    Download zip
    Author
    Version
    0.4.0
    License
    Apache-2.0
    Last updated
    3 months ago
    Downloads
    50

    No dependencies

    #MoonShardKit

    Deterministic shard placement and migration planning for MoonBit.

    MoonShardKit is a backend-neutral foundation library for caches, object stores, task queues, distributed indexes, and stateless routing systems. The same topology and key set produce the same placement on Native, JavaScript, WebAssembly, and WebAssembly-GC.

    #Features

    • Weighted consistent hashing with virtual nodes
    • Integer-score weighted Rendezvous hashing
    • Zone- and rack-aware replica placement
    • Deterministic primary and replica migration plans
    • Five-phase safe migration workflows: add, backfill, verify, cut over, clean up
    • Global wave concurrency and per-node pressure budgets
    • Migration-plan validation and blocked-key reporting
    • Capacity-admitted batch placement with per-node primary/replica budgets
    • Capacity adjustments, failure-domain exclusion, regional isolation, and resumable migration waves
    • Distribution, skew, and topology-violation reports
    • Stable JSON output and a runnable CLI
    • No network, database, service-discovery, or platform dependency

    #Install

    moon add Dz-6666/moonshardkit

    #Example

    let nodes = [
    @moonshardkit.ShardNode::new("node-a", zone="east", rack="rack-1"),
    @moonshardkit.ShardNode::new("node-b", zone="west", rack="rack-1"),
    @moonshardkit.ShardNode::new("node-c", zone="south", rack="rack-2"),
    ]

    let placement = @moonshardkit.place_key(nodes, "customer-42", 3)
    println(placement.to_json())

    placement.owners[0] is the primary owner. Remaining entries are replicas, ordered deterministically while preferring zone and rack diversity.

    #Safe Migration Workflow

    let workflow = @moonshardkit.plan_safe_migration(
    keys,
    old_nodes,
    new_nodes,
    replicas=3,
    max_actions_per_wave=64,
    max_actions_per_node=8,
    )

    assert_eq(@moonshardkit.validate_safe_migration(workflow).length(), 0)

    The planner never emits source cleanup before target creation, backfill, verification, and primary cutover. Keys without a readable source are reported in blocked_keys instead of receiving an unsafe plan. The output is a pure, deterministic control-plane plan; storage adapters execute and checkpoint it.

    #Capacity-constrained admission

    plan_capacity_placement turns placement into an admission decision for a batch. It retains deterministic weighted Rendezvous ordering and topology-aware replica preference, while never exceeding a node's primary or replica budget. Keys that cannot obtain every requested owner remain visible in rejected_keys; there is no hidden overload fallback.

    moon run cmd/scenario --target js

    The scenario models a four-region control plane with 24 tenants, three replicas, and explicit per-node limits. It prints a stable JSON audit report with accepted/rejected keys and every budgeted node load.

    For control-plane recovery, resume_safe_migration takes a durable completed- wave checkpoint and returns a re-indexed, validation-ready remaining workflow. exclude_failure_domains and isolate_regions produce offline-marked topology snapshots without mutating the source topology.

    #Verify

    moon fmt --check moon check --deny-warn --target all moon info && git diff --exit-code -- '*.mbti' moon test --deny-warn --target all moon run cmd/main --target js moon run bench/main --target js moon run cmd/scenario --target js

    The benchmark emits reproducible 1k/10k/100k rows containing movement ratio, load spread, migration waves, validation issues, and a stable evidence hash. Run the same command with --target native, js, wasm, or wasm-gc to compare backend output; CI executes the supported matrix.

    CI uses the executable quality gates supported by the current MoonBit CLI: moon fmt --check, moon check --deny-warn --target all, moon info plus a clean .mbti diff, and moon test --deny-warn --target all. moon fmt and moon info do not accept --deny-warn in this CLI version, so CI documents their equivalent checks rather than adding commands that always fail.

    #Documentation

    #License

    Apache-2.0.

    CapacityAdjustment

    pub(all) struct CapacityAdjustment {
    node_id : String
    primary_delta : Int
    replica_delta : Int
    } derive(Eq,
    Debug
    )

    A signed update to one node's ownership budgets.

    CapacityAdjustment::new

    fn CapacityAdjustment::new(node_id : String, primary_delta : Int, replica_delta : Int) -> CapacityAdjustment

    CapacityLoad

    pub(all) struct CapacityLoad {
    node_id : String
    primary_keys : Int
    replica_keys : Int
    max_primary : Int
    max_replicas : Int
    } derive(Eq,
    Debug
    )

    Observed ownership and the configured budgets for one node.

    CapacityPlan

    pub(all) struct CapacityPlan {
    placements : Array[KeyPlacement]
    rejected_keys : Array[String]
    loads : Array[CapacityLoad]
    replicas : Int
    } derive(Eq,
    Debug
    )

    Deterministic batch placement result with explicit admission failures.

    CapacityPlan::accepted_keys

    fn CapacityPlan::accepted_keys(self : CapacityPlan) -> Int

    CapacityPlan::is_within_capacity

    fn CapacityPlan::is_within_capacity(self : CapacityPlan) -> Bool

    CapacityPlan::to_json

    fn CapacityPlan::to_json(self : CapacityPlan) -> String

    Encodes capacity-admitted placement evidence for control-plane logs.

    ConsistentHashRing

    pub struct ConsistentHashRing {
    points : Array[RingPoint]
    eligible_nodes : Int
    seed : UInt
    }

    Immutable consistent hash ring.

    ConsistentHashRing::build

    fn ConsistentHashRing::build(nodes : Array[ShardNode], seed? : UInt) -> ConsistentHashRing

    Builds a ring from active nodes.

    ConsistentHashRing::node_count

    fn ConsistentHashRing::node_count(self : ConsistentHashRing) -> Int

    Returns the number of active real nodes.

    ConsistentHashRing::owner

    fn ConsistentHashRing::owner(self : ConsistentHashRing, key : String) -> String?

    Returns the primary owner for a key.

    ConsistentHashRing::owners

    fn ConsistentHashRing::owners(self : ConsistentHashRing, key : String, replicas : Int) -> Array[String]

    Returns distinct clockwise owners, primary first.

    ConsistentHashRing::point_count

    fn ConsistentHashRing::point_count(self : ConsistentHashRing) -> Int

    Returns the number of virtual points.

    DistributionReport

    pub(all) struct DistributionReport {
    keys : Int
    replicas : Int
    nodes : Array[NodeLoad]
    min_primary : Int
    max_primary : Int
    spread : Int
    incomplete_placements : Int
    zone_violations : Int
    rack_violations : Int
    } derive(Eq,
    Debug
    )

    Distribution quality summary.

    DistributionReport::to_json

    fn DistributionReport::to_json(self : DistributionReport) -> String

    Encodes distribution evidence.

    KeyPlacement

    pub(all) struct KeyPlacement {
    key : String
    owners : Array[String]
    complete : Bool
    message : String
    } derive(Eq,
    Debug
    )

    One key and its ordered owner list.

    KeyPlacement::to_json

    fn KeyPlacement::to_json(self : KeyPlacement) -> String

    Encodes one placement.

    MigrationAction

    pub(all) struct MigrationAction {
    key : String
    phase : MigrationPhase
    from_node : String?
    to_node : String?
    } derive(Eq,
    Debug
    )

    One idempotent operation in a safe migration workflow.

    MigrationCheckpoint

    pub(all) struct MigrationCheckpoint {
    completed_waves : Int
    } derive(Eq,
    Debug
    )

    Durable progress marker for resuming a safe migration by wave.

    MigrationCheckpoint::new

    fn MigrationCheckpoint::new(completed_waves : Int) -> MigrationCheckpoint

    MigrationPhase

    pub(all) enum MigrationPhase {
    AddTarget
    Backfill
    Verify
    SwitchPrimary
    RemoveSource
    } derive(Eq,
    Debug
    )

    Ordered safety phase for an executable shard migration.

    MigrationPlan

    pub(all) struct MigrationPlan {
    keys : Int
    moved_keys : Int
    unchanged_keys : Int
    moves : Array[ShardMove]
    } derive(Eq,
    Debug
    )

    Aggregate migration evidence.

    MigrationPlan::to_json

    fn MigrationPlan::to_json(self : MigrationPlan) -> String

    Encodes one migration plan.

    MigrationWave

    pub(all) struct MigrationWave {
    index : Int
    phase : MigrationPhase
    actions : Array[MigrationAction]
    } derive(Eq,
    Debug
    )

    A deterministic group of actions that may execute concurrently.

    MoveKind

    pub(all) enum MoveKind {
    PrimaryChanged
    ReplicaAdded
    ReplicaRemoved
    } derive(Eq,
    Debug
    )

    Why an ownership assignment changed.

    NodeCapacity

    pub(all) struct NodeCapacity {
    node_id : String
    max_primary : Int
    max_replicas : Int
    } derive(Eq,
    Debug
    )

    Hard ownership budgets for one node during a batch placement run. A missing node from the capacity list is treated as unbounded.

    NodeCapacity::new

    fn NodeCapacity::new(node_id : String, max_primary : Int, max_replicas : Int) -> NodeCapacity

    NodeLoad

    pub(all) struct NodeLoad {
    node_id : String
    primary_keys : Int
    replica_keys : Int
    } derive(Eq,
    Debug
    )

    Per-node ownership count.

    NodeStatus

    pub(all) enum NodeStatus {
    Active
    Draining
    Offline
    } derive(Eq,
    Debug
    )

    Administrative state used by placement algorithms.

    PlacementAlgorithm

    pub(all) enum PlacementAlgorithm {
    ConsistentRing
    Rendezvous
    TopologyAware
    } derive(Eq,
    Debug
    )

    Built-in owner-selection strategy.

    RingPoint

    type RingPoint

    One virtual point on a consistent hash ring.

    SafeMigrationPlan

    pub(all) struct SafeMigrationPlan {
    keys : Int
    migrating_keys : Int
    blocked_keys : Array[String]
    actions : Array[MigrationAction]
    waves : Array[MigrationWave]
    max_actions_per_wave : Int
    max_actions_per_node : Int
    } derive(Eq,
    Debug
    )

    Executable migration workflow with explicit safety and pressure budgets.

    SafeMigrationPlan::to_json

    fn SafeMigrationPlan::to_json(self : SafeMigrationPlan) -> String

    Encodes a safe migration workflow for audit logs and control planes.

    ShardMove

    pub(all) struct ShardMove {
    key : String
    from_node : String?
    to_node : String?
    kind : MoveKind
    } derive(Eq,
    Debug
    )

    One planned ownership change between topology snapshots.

    ShardNode

    pub(all) struct ShardNode {
    id : String
    weight : Int
    virtual_nodes : Int
    zone : String
    rack : String
    status : NodeStatus
    } derive(Eq,
    Debug
    )

    A storage or compute node participating in shard placement.

    ShardNode::is_eligible

    fn ShardNode::is_eligible(self : ShardNode) -> Bool

    Returns whether the node can receive new ownership.

    ShardNode::new

    fn ShardNode::new(id : String, zone? : String, rack? : String, weight? : Int, virtual_nodes? : Int) -> ShardNode

    Creates a normalized active node.

    ShardNode::with_status

    fn ShardNode::with_status(self : ShardNode, status : NodeStatus) -> ShardNode

    Returns a copy with a new administrative state.

    TopologyIssue

    pub(all) enum TopologyIssue {
    EmptyNodeId(Int)
    DuplicateNodeId(String)
    EmptyZone(String)
    EmptyRack(String)
    } derive(Eq,
    Debug
    )

    Structural problem in a topology snapshot.

    adjust_capacities

    fn adjust_capacities(capacities : Array[NodeCapacity], adjustments : Array[CapacityAdjustment]) -> Array[NodeCapacity]

    Applies capacity changes without mutating the caller's policy array. Unknown adjustment targets are ignored so stale control-plane updates are safe to replay.

    analyze_distribution

    fn analyze_distribution(keys : Array[String], nodes : Array[ShardNode], replicas? : Int, salt? : String) -> DistributionReport

    Measures placement balance and topology constraint outcomes.

    exclude_failure_domains

    fn exclude_failure_domains(nodes : Array[ShardNode], unavailable_zones : Array[String], unavailable_racks : Array[String]) -> Array[ShardNode]

    Marks every node in an unavailable zone or rack as offline.

    isolate_regions

    fn isolate_regions(nodes : Array[ShardNode], allowed_zones : Array[String]) -> Array[ShardNode]

    Restricts new placement to an explicit regional allow-list.

    json_escape

    fn json_escape(value : String) -> String

    Escapes a string for JSON output.

    migration_phase_name

    fn migration_phase_name(phase : MigrationPhase) -> String

    Returns the stable machine-readable name of a workflow phase.

    move_kind_name

    fn move_kind_name(kind : MoveKind) -> String

    Returns a stable name for one migration action.

    movement_ratio

    fn movement_ratio(plan : MigrationPlan) -> Double

    Returns the fraction of keys whose ordered owner set changed.

    place_key

    fn place_key(nodes : Array[ShardNode], key : String, replicas : Int, algorithm? : PlacementAlgorithm, seed? : UInt, salt? : String) -> KeyPlacement

    Places one key through a selected built-in algorithm.

    place_keys

    fn place_keys(nodes : Array[ShardNode], keys : Array[String], replicas : Int, algorithm? : PlacementAlgorithm, seed? : UInt, salt? : String) -> Array[KeyPlacement]

    Places a deterministic key batch with one algorithm.

    place_replicas

    fn place_replicas(nodes : Array[ShardNode], key : String, replicas : Int, salt? : String) -> KeyPlacement

    Places ordered replicas while preferring zone and rack diversity.

    placement_score

    fn placement_score(key : String, node_id : String, salt : String) -> UInt

    Deterministically mixes a key, node identity, and namespace salt.

    plan_capacity_placement

    fn plan_capacity_placement(keys : Array[String], nodes : Array[ShardNode], capacities : Array[NodeCapacity], replicas : Int, salt? : String) -> CapacityPlan

    Plans a capacity-admitted, topology-aware placement for a key batch.

    Candidate order is deterministic weighted Rendezvous order. The planner never exceeds a configured budget: keys that cannot receive every requested owner remain in rejected_keys with an incomplete placement record.

    plan_migration

    fn plan_migration(keys : Array[String], before : Array[ShardNode], after : Array[ShardNode], replicas? : Int, salt? : String) -> MigrationPlan

    Compares two topology snapshots and emits deterministic ownership changes.

    plan_safe_migration

    fn plan_safe_migration(keys : Array[String], before : Array[ShardNode], after : Array[ShardNode], replicas? : Int, max_actions_per_wave? : Int, max_actions_per_node? : Int, salt? : String) -> SafeMigrationPlan

    Builds a five-phase, budgeted migration workflow.

    All target replicas are added, backfilled, and verified before any primary switch or source cleanup is emitted. Keys without a readable source are reported as blocked instead of receiving unsafe actions.

    rendezvous_node_score

    fn rendezvous_node_score(key : String, node : ShardNode, salt? : String) -> UInt

    Computes a deterministic weighted score for one node.

    Integer tickets avoid backend-dependent floating-point logarithms. A node with weight N receives N independent score opportunities and keeps its best.

    rendezvous_owner

    fn rendezvous_owner(nodes : Array[ShardNode], key : String, salt? : String) -> String?

    Returns the highest-ranked active node.

    rendezvous_owners

    fn rendezvous_owners(nodes : Array[ShardNode], key : String, replicas : Int, salt? : String) -> Array[String]

    Ranks all active nodes using weighted highest-random-weight placement.

    resume_safe_migration

    fn resume_safe_migration(plan : SafeMigrationPlan, checkpoint : MigrationCheckpoint) -> SafeMigrationPlan

    Returns a self-contained remaining workflow after a durable checkpoint. Wave indexes are renumbered from zero so validation remains meaningful for the resumed executor.

    stable_hash

    fn stable_hash(text : String, seed? : UInt) -> UInt

    Stable FNV-1a style 32-bit hash over MoonBit UTF-16 code units.

    topology_is_valid

    fn topology_is_valid(nodes : Array[ShardNode]) -> Bool

    Returns true when the topology has no structural issues.

    validate_safe_migration

    fn validate_safe_migration(plan : SafeMigrationPlan) -> Array[String]

    Checks phase ordering, wave indexes, and both concurrency budgets.

    validate_topology

    fn validate_topology(nodes : Array[ShardNode]) -> Array[TopologyIssue]

    Validates identifiers and failure-domain metadata in one snapshot.

    virtual_position

    fn virtual_position(node_id : String, replica : Int, seed : UInt) -> UInt

    Creates a virtual-node position without ambiguous string concatenation.