Documentation for agents: an index at /llms.txt, every docs page in one file at /llms-full.txt, and Markdown for any docs page by adding .md to its URL.

Effect KitRedis

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 >;
type Provided = BatchExecutor | CommandExecutor | Crypto | Redis | RedisCluster | RedisCredentials

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.

MethodPurpose
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, snapshotInspect 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.