Overview
Connect to Redis Cluster, route commands by hash slot, and reach sessions and streams through RedisCluster.
A Redis Cluster splits the keys across 16384 hash slots, and each primary owns a range of them. layerCluster discovers the topology, sends every command to the node that owns its slot, and follows MOVED and ASK redirects. The Redis API is the same as on a standalone server.
Example (Connecting to a Cluster)
import { } from "effect";
import { } from "@effect-kit/redis";
import { } from "@effect-kit/redis/node";
const = .({
: "redis://localhost:7000",
: {
: [
{ : "localhost", : 7001 },
{ : "localhost", : 7002 },
],
},
});
const = .(function* () {
const = yield* ;
yield* .("user:42", "Ada");
return yield* .("user:42");
});
export const = .(.());The address in url or endpoint is the first seed, and cluster.seeds adds more. The layer asks the seeds for the topology when it is built, so an unreachable cluster fails at startup. RedisBun.layerCluster takes the same input. Cluster needs TCP endpoints: a Unix socket fails with RedisConfigError. Cluster is not available over Upstash HTTP.
Provided services
The cluster layer provides Redis, RedisCluster, CommandExecutor, BatchExecutor and RedisCredentials.
Example (The Services a Cluster Layer Provides)
import type { } from "effect";
import { } from "@effect-kit/redis/node";
const = .({
: "redis://localhost:7000",
});
type Provided = .<typeof >;No RedisSessions, RedisStreams or RedisPubSub
The cluster layer does not provide RedisSessions, RedisStreams, RedisScripts or
RedisPubSub. A session lives on one node, so you pick the node with a key or a node id through
RedisCluster.
Reaching a node with RedisCluster
RedisCluster takes a target: { key } routes to the node that owns the key's slot, and { node } names a node by id.
| Method | Purpose |
|---|---|
withSession(target, body) | A leased session on that node, for WATCH and manual control |
withBlockingSession(target, body) | A session on a blocking connection |
scripts(target) | RedisScripts for that node |
streams(target) | RedisStreams for that node |
pubsub(target), tracking(target) | Subscriptions and tracking on that node |
node(id), admin(id) | Commands and RedisAdmin on one node |
onPrimaries(command) | Run one command on every primary |
topology, refresh, snapshot | Inspect and refresh the topology |
Example (Running a Blocking Pop on the Node That Owns a Key)
import { , } from "effect";
import { } from "@effect-kit/redis";
export const = .(function* () {
const = yield* ;
return yield* .({ : "{jobs}:queue" }, () =>
.(["{jobs}:queue"], { : .(1) }),
);
});A session checks every command against its node. A command for a different slot fails with ClusterError where reason is "cross-slot" or "session-route".
onPrimaries returns a lazy Stream of { node, result }, one per primary, in sequence. Each result is a Result, so one failed node does not stop the others. There is no atomicity and no retry.
Topology
cluster.topology returns the current ClusterTopology: a revision and a list of shards, each with its slots ranges, primary and replicas. The layer refreshes it every refreshIntervalMillis and after a redirect. cluster.refresh forces a refresh.
keySlot(key) computes the slot for a key. It applies hash tags, so keySlot("{user:42}:name") equals keySlot("{user:42}:visits").
Options
Pass these as cluster next to url or endpoint. The connection and session options at the top level apply to every node.
Prop
Type
A database other than 0 fails with ClusterError and reason: "database" unless databaseCapability is "valkey-multi-db", which Valkey 9.0 and later can be configured for.