Shardcake Documentation

repository·series/2.x·Indexed 19 days ago

https://github.com/devsisters/shardcake

A Scala library for distributed entity management and sharding built on the ZIO ecosystem. Shardcake provides location transparency, allowing developers to interact with entities via IDs while the library handles distribution and routing across multiple pods using a single-writer approach. It features a pluggable architecture for storage (default Redis), pod communication (default gRPC), serialization (default Kryo), and health monitoring (default k8s API).

Tokens
14K
Snippets
41
Records
60
Agent score
63%

What's inside Shardcake

  1. Overview of Shardcake

    series/2.x

    Shardcake is a Scala open source library designed to facilitate the distribution of entities across multiple servers. It provides location transparency, allowing developers to interact with entities using only their IDs without needing to know their physical location.

    Key characteristics:

    • Purely functional API: Built with a functional programming approach.
    • ZIO-based: Depends heavily on the ZIO ecosystem for concurrency and effect management.
  2. Overview of Shardcake features

    series/2.x

    Shardcake is a distributed system framework designed around several core principles:

    • Single writer pattern: Ensures that each entity exists in exactly one place across all servers at any given time, preventing write conflicts.
    • Location transparency: Allows you to communicate with remote entities using only their unique ID, abstracting away their physical network location.
    • Customization: Provides flexibility to use provided implementations or bring your own for storage, serialization, and messaging protocols.
  3. What is Shardcake and how does it work?

    series/2.x

    Shardcake is a Scala library designed for Entity Sharding. It allows you to distribute entities (small message handlers addressed by ID) across multiple application servers (pods) while maintaining location transparency.

    Instead of using global locks to manage state across servers, Shardcake uses a single writer approach: each entity is guaranteed to exist on exactly one pod at any given time. You can interact with an entity using only its ID, and Shardcake handles finding the correct pod and routing the message there.

    Key characteristics:

    • Location Transparency: You send messages to an entity ID without knowing which server it resides on.
    • Purely Functional: The API is built on ZIO.
    • Scalable: It manages the assignment of entities to pods automatically.
  4. How to send a message to an entity (`Messenger#send`)

    series/2.x

    When a pod needs to communicate with an entity, it follows a redirection pattern to ensure the message reaches the correct owner.

    1. Calculate Shard: The sender calculates the shardId for the target entity.
    2. Lookup Owner: The sender checks its local cache to find which pod is responsible for that shard.
    3. Redirect: If the owner is a different pod, the sender calls the sendMessage endpoint on the target pod's sharding API.
    4. Local Execution: The target pod's EntityManager verifies the shard is indeed managed locally. If so, it starts the entity (if not already running) and forwards the message.
    5. Response: The response is routed back through the network to the original sender.

    Error Handling:

    • If the target pod is unresponsive, the sender calls notifyUnhealthyPod on the Shard Manager. The Shard Manager then verifies the pod's health via a Health API (e.g., Kubernetes) and unregisters it if it is dead.
    • If a pod receives a message for a shard it no longer manages, it returns EntityNotManagedByThisPod.
    // Conceptual flow of Messenger#send
    // 1. Calculate shard
    const shardId = abs(entityId.hashCode % numberOfShards) + 1;
    // 2. Find pod (Pod2)
    const targetPod = localCache.getPodForShard(shardId);
    // 3. Remote call
    await targetPod.shardingApi.sendMessage(entityId, message);
  5. Understand Shardcake terminology

    series/2.x

    To use Shardcake effectively, you should understand its core abstractions:

    • Entity: A small message handler addressed by a unique ID (e.g., a specific User or Guild). An Entity Type refers to the template/logic (e.g., User).
    • Pod: An application server instance that hosts entities. While many pods can run in a cluster, a single entity instance will only ever run on one pod at a time.
    • Shard: A logical grouping of entities. Instead of mapping every individual entity to a pod, Shardcake groups entities into shards, and then maps shards to pods. This makes the mapping management efficient even with millions of entities.
  6. Key components and pluggable architecture

    series/2.x

    Shardcake's architecture is split into two main functional parts and four pluggable implementation layers:

    Core Components

    • Shard Manager: A single, independent instance responsible for assigning shards to pods.
    • Entities: The logic running on your application servers that processes incoming messages. Note that Shardcake does not manage entity persistence or behavior; you are responsible for implementing how an entity saves its state.

    Pluggable Traits

    You can implement the following interfaces using your preferred technology:

    • Storage: Defines where shard assignments are stored (Default: Redis).
    • Pods: Defines how pods communicate with each other (Default: gRPC).
    • Serialization: Defines how messages are encoded/decoded (Default: Kryo).
    • PodsHealth: Defines how to monitor pod health (Default: k8s API).
  7. How Shardcake architecture works

    series/2.x

    Shardcake is a distributed sharding system composed of a Shard Manager and multiple Pods.

    • Shard Manager: A single active node responsible for maintaining pod-to-shard assignments. It handles pod registration/unregistration and triggers rebalances.
    • Pods: The worker nodes that host entities. Pods register with the Shard Manager, cache shard assignments from a Storage layer, and communicate directly with each other to route messages.
    • Sharding Logic: An entity's shard is determined by a stable calculation: shardId = abs(entityId.hashCode % numberOfShards) + 1.

    Key Reliability Feature: The Shard Manager is not a single point of failure for message routing. Because pods cache assignments and communicate peer-to-peer, the system continues to function even if the Shard Manager is offline, as long as the pod membership remains stable.

  8. Pod lifecycle: Registration and Unregistration

    series/2.x

    Pods manage their presence in the cluster through specific lifecycle flows involving the Shard Manager and the Sharding API.

    Pod Start (Sharding#register)

    1. The pod retrieves current shard assignments from the Storage layer.
    2. The pod exposes its own sharding API (e.g., a gRPC server) to allow communication from the Shard Manager and other pods.
    3. The pod calls Sharding#register on the Shard Manager. This triggers a rebalance where the Shard Manager may call the pod's assign API to give it shards.

    Pod Stop (Sharding#unregister)

    1. The pod enters a stopping state where it prevents new entities from starting locally. Any incoming messages from other pods will return an EntityNotManagedByThisPod error.
    2. The pod stops all local entities by sending termination messages and waits for in-progress messages to complete.
    3. The pod calls Sharding#unregister on the Shard Manager. This triggers an immediate rebalance to reassign its shards to other pods.
    4. The pod shuts down its sharding API.
  9. Shardcake vs Akka/Pekko

    series/2.x

    Shardcake is not a full replacement for Akka or Pekko. It is a specialized library designed specifically to provide distributed sharding capabilities for ZIO.

    • What Shardcake provides: Distributed sharding (similar to Akka/Pekko Cluster Sharding).
    • What ZIO provides: Local concurrency tools (e.g., Queue, Hub, Promise).
    • What Shardcake does NOT provide: Features like event sourcing or actor persistence.

    If your application requires event sourcing or persistence, you should implement these patterns on top of ZIO and Shardcake.

  10. How the Assignment Algorithm distributes shards

    series/2.x

    The assignment algorithm aims for an even distribution of shards across pods while respecting rolling updates (pod versions).

    Logic Steps:

    1. Calculate Target: It calculates the average shards per pod (total shards / total pods).
    2. Identify Extra Shards: For pods with more than the average, it identifies extraShardsToAllocate. Note: If pods have different versions (rolling update), extraShardsToAllocate is set to empty to prevent unnecessary movement.
    3. Prioritize: Shards to rebalance are sorted: unassigned shards first, then shards from pods with the most shards, then shards from old pods.
    4. Assign to Least Loaded: For each shard to rebalance:
      • Find the pod with the fewest shards (excluding pods with old versions).
      • If the target pod is the current owner, do nothing.
      • If the difference in shard count between the current and target pod is only 1, skip to avoid jitter.
      • Otherwise, create an assign for the new pod and an unassign for the old pod.
  11. How the Rebalance Algorithm works

    series/2.x

    A rebalance is the process of assigning or unassigning shards to/from pods. It is triggered by the first pod registering, a pod unregistering, or at a regular rebalanceInterval.

    The Rebalance Workflow:

    1. Decision: The Shard Manager determines which shards need to move based on the Assignment Algorithm.
    2. Health Check: The Shard Manager pings involved pods (using pingTimeout). Unresponsive pods are excluded from the current rebalance.
    3. Unassign: The Shard Manager calls unassign on pods losing shards in parallel. A successful unassign ensures local entities have stopped.
    4. Assign: The Shard Manager calls assign on pods receiving shards in parallel.
    5. Verification: If any pods failed the ping/assign/unassign steps, the Shard Manager checks the Health API. If they are dead, it unregisters them and triggers a new rebalance.
    6. Persistence: New assignments are saved to the Storage layer, and pods are notified.