# Cluster

Most of Effect lets you build a single process. **Cluster** lets you take stateful
services and spread them across many machines — without changing the way you write
your business logic. You define an *entity* (a sharded, addressable actor), give it
an RPC protocol, and the cluster routes every message for a given id to exactly one
live instance somewhere in the fleet. State lives in memory while the entity is
active, and is recreated on demand after idle passivation or failover.

The cluster modules are **unstable** and // 1. Describe the messages the entity can handle, as RPCs.
const Increment = Rpc.make("Increment", {
  payload: { amount: Schema.Number },
  success: Schema.Number
})

// 2. Pair a stable entity-type name with its RPC protocol.
const Counter = Entity.make("Counter", [Increment])

// 3. Implement the handlers. State (here a Ref) lives for as long as the
//    entity instance is active on its owning runner.
const CounterLayer = Counter.toLayer(
  Effect.gen(function*() {
    const count = yield* Ref.make(0)
    return Counter.of({
      Increment: ({ payload }) => Ref.updateAndGet(count, (n) => n + payload.amount)
    })
  })
)

// 4. From anywhere in the cluster, get a client for a specific entity id and
//    send it a message. Routing to the live instance is handled for you.
const program = Effect.gen(function*() {
  const clientFor = yield* Counter.client
  const total = yield* clientFor("counter-123").Increment({ amount: 1 })
  yield* Effect.log(`counter-123 is now ${total}`)
})
```

## Why entities?

An entity is the cluster-facing version of a stateful service. Two properties make
it different from a plain [service](https://effect.plants.sh/services-and-layers/):

- **Single owner per id.** For any entity id (`"counter-123"`), the cluster
  guarantees that messages are processed by a single live instance at a time, on
  whichever runner currently owns that id's shard. You get serialized access to
  in-memory state without locks.
- **Location transparency.** Callers never address machines. They ask for a client
  by entity id and send typed RPCs; the cluster handles routing, failover, and
  passivation (stopping idle entities and recreating them on the next message).

Because the protocol is described with [RPC](https://effect.plants.sh/rpc/) and [Schema](https://effect.plants.sh/schema/), every
message is type-safe end to end and can be serialized for the wire or persisted to
durable storage.

## The pieces of a cluster

A running cluster is a handful of cooperating modules. You usually only touch
`Entity` and the platform layer that bundles the rest, but it helps to know what is
underneath:

- **Entity** (`Entity`) — the addressable actor and its RPC protocol. You define the
  messages, implement the handlers (holding in-memory state), and obtain a client by
  id. See [Entities](https://effect.plants.sh/cluster/entities/).
- **Sharding** (`Sharding`, `ShardingConfig`) — the runtime service. It hashes
  entity ids into shard ids within a shard group, places those shards on a hash ring
  of healthy runners, and routes each message to the runner that currently owns the
  target shard. See [Sharding & runners](https://effect.plants.sh/cluster/sharding-and-runners/).
- **Runners & transport** (`SocketRunner`, `HttpRunner`, `SingleRunner`,
  `TestRunner`) — the per-process node and the wire protocol between nodes. The
  platform packages bundle a transport: `NodeClusterSocket.layer` (raw TCP) and the
  HTTP/WebSocket runners for HTTP deployments. `SingleRunner` runs the whole cluster
  in one process; `TestRunner` runs it in memory for tests. See
  [Sharding & runners](https://effect.plants.sh/cluster/sharding-and-runners/).
- **Storage** (`MessageStorage`, `RunnerStorage`) — where the cluster keeps its
  coordination state and (optionally) durable mailboxes. The SQL-backed layers
  (`SqlMessageStorage`, `SqlRunnerStorage`) give you durability across restarts; the
  in-memory layers are for development and tests. See
  [Message storage](https://effect.plants.sh/cluster/message-storage/).
- **Singleton & ClusterCron** (`Singleton`, `ClusterCron`) — register work that
  should have exactly one owner across the whole fleet, with ownership following
  shard placement so it fails over automatically. See
  [Singletons & cron](https://effect.plants.sh/cluster/singletons-and-cron/).

The boundary that matters most is **volatile vs persisted** messaging: with the
in-memory storage layers a message lives only as long as the process, while with the
SQL-backed layers a message is written to durable storage before it is acknowledged,
so it survives a runner crash and is redelivered on failover. That trade-off, and
how to opt into persistence per message, is covered in
[Message storage](https://effect.plants.sh/cluster/message-storage/).

## In this section

- **[Entities](https://effect.plants.sh/cluster/entities/)** — Define an entity's RPCs, implement handlers
  that keep in-memory state, send messages from a client, and wire everything into a
  runnable cluster (including an in-memory test setup).
- **[Sharding & runners](https://effect.plants.sh/cluster/sharding-and-runners/)** — How `Sharding` hashes
  ids into shards and places them on runners, the `ShardingConfig` knobs, and the
  available runner transports (`SocketRunner`, `HttpRunner`, `SingleRunner`,
  `TestRunner`, plus `NodeClusterSocket`).
- **[Singletons & cron](https://effect.plants.sh/cluster/singletons-and-cron/)** — Run an effect with a
  single fleet-wide owner using `Singleton`, and schedule clustered recurring work
  with `ClusterCron`.
- **[Message storage](https://effect.plants.sh/cluster/message-storage/)** — Volatile vs persisted
  messaging, `MessageStorage` / `RunnerStorage`, and the SQL-backed durable layers.
- **[Entity proxies](https://effect.plants.sh/cluster/entity-proxies/)** — Expose an entity over an external
  protocol (e.g. an HTTP/RPC server) with `EntityProxy` / `EntityProxyServer`.
- **[Reference](https://effect.plants.sh/cluster/reference/)** — The remaining modules at a glance:
  addresses and ids (`EntityAddress`, `EntityId`, `RunnerAddress`, `ShardId`,
  `SingletonAddress`), `Envelope` / `Message` / `Reply`, `Snowflake`,
  `ClusterError`, `ClusterMetrics`, and more.

## Related modules

Beyond the pieces above, the `effect/unstable/cluster` namespace also ships
lower-level building blocks you reach for less often:

- **`Snowflake`** — the time-ordered unique id generator used for message ids.
- **`ClusterMetrics`** — predefined metrics for shard counts, message rates, and
  storage activity.
- **`ClusterWorkflowEngine`** — runs the [Workflow](https://effect.plants.sh/workflow/) engine on top of the
  cluster so durable workflows execute across the fleet.

Cluster builds directly on [RPC](https://effect.plants.sh/rpc/), [Schema](https://effect.plants.sh/schema/),
[Services & Layers](https://effect.plants.sh/services-and-layers/), and the [Platform](https://effect.plants.sh/platform/) packages —
read those first if any of the snippets here are unfamiliar.