Actors/Packages/Redis/API reference
@sigx/actors-redis · Preview

API reference#

Exports of @sigx/actors-redis v0.11.0.

redisCluster(options)#

Returns ClusterProviders — membership and directory together. The common case.

TypeScript
cluster({ providers: redisCluster({ client }), advertise, secret });
TypeScript
interface RedisClusterOptions {
    client?: RedisClient;      // an ioredis instance, used with its own options
    url?: string;              // …or a URL to construct one, with enableAutoPipelining: true
    namespace?: string;        // 'sigx'
    heartbeatMs?: number;      // 5000
    ttlMs?: number;            // 15000
    pollMs?: number;           // 5000
}

Call it once per host — each call returns that host's own membership handle.

redisMembership(client, options?)#

Membership alone: TTL heartbeats, a polled view with pub/sub push, and self-fencing when this host cannot renew past ttlMs.

redisDirectory(client, options?)#

The single-activation claim directory. Options: { namespace?: string }.

Pairs with any membership provider — notably k8sMembership().

redisStorage(options)#

An ActorStorage with etag compare-and-set.

TypeScript
defineActorApp({ storage: redisStorage({ client, namespace: 'sigx' }) });
TypeScript
interface RedisStorageOptions {
    client?: RedisClient;      // used with its own options
    url?: string;              // constructs a client with enableAutoPipelining: true
    namespace?: string;        // 'sigx' — share it with the cluster providers
}

Etags are client-minted UUIDs. save, clear and appendText compare-and-set atomically in Redis via Lua, registered as EVALSHA commands because save is the hot path, and throw the branded ActorStorageConflict on mismatch — which the runtime turns into discard-and-reload. An append pushes one entry onto the record's log LIST; a full save or a clear deletes it in the same script.

With url, the package constructs its own client with enableAutoPipelining: true, so a host's same-tick saves coalesce into fewer socket writes. Pass client to own the options.

redisReminders(options)#

An ActorReminders on a due-time-indexed sorted set, passed as reminders:.

TypeScript
defineActorApp({ storage: redisStorage({ client }), reminders: redisReminders({ client }) });
TypeScript
interface RedisRemindersOptions {
    client?: RedisClient;      // shared with the other providers
    url?: string;              // …or a URL — the package constructs an auto-pipelined client
    namespace?: string;        // 'sigx'
    batchSize?: number;        // 200 — members claimed per script
}

It throws unless client or url is given. Every operation — set, clear, a claim batch, the re-arm of failed dispatches — is one Lua script, with Redis TIME as the clock. The claim advances or removes a member before dispatch (at-most-once per firing); a periodic member advances to max(next_due, now) + period, so downtime costs one firing rather than replaying the gap. A tick keeps claiming batches until one comes back short, up to ten per tick, so batchSize caps memory rather than throughput. The minimum period is 60 seconds, as with the default provider.

remEscape / remUnescape are exported for tooling that reads the member encoding.

Types#

RedisClient (the ioredis Redis type), RedisClusterOptions, RedisStorageOptions, RedisRemindersOptions.

Key layout#

{ns}:host:{hostId}    HASH {d: descriptor JSON}   PX ttlMs, renewed each beat
{ns}:hosts            SET of hostIds              lazily pruned
{ns}:mver             INCR'd version counter      cheap view-poll compare
{ns}:dir:{actorId}    "hostId\nactivationId"      no TTL (validity = owner liveness)
{ns}:membership       pub/sub channel             change push (poll = fallback)
{ns}:st:{type}\x00{key}   HASH {e: etag, s: state JSON}
{ns}:rem:due          ZSET  score = next_due (ms), member = type\x00key\x00name
{ns}:rem:p            HASH  member → period ms (periodic reminders only)
{ns}:rem:a:{type}\x00{key}  SET of the actor's reminder names — list() never scans
{ns}:sl:{type}\x00{key}   LIST of appended entry JSON, oldest first

Reminder members escape each field (\ → \\, NUL → \0) so a NUL-bearing key splits back into its ref and name.

The log has a prefix of its own (sl) rather than a suffix on the record key, because actor keys are opaque and a suffix could collide with another actor's key.

The storage record key joins actor type and key with a NUL byte — the same actorId shape the directory uses. Redis keys are binary-safe, and a NUL renders invisibly in redis-cli SCAN output.

Directory entries carry no TTL: their validity is the owner's liveness, and entries for dead hosts are evicted lazily on lookup.

Operations used: the directory claim is SET NX GET; release and evict are Lua compare-and-delete; evictHost is a SCAN plus an owner-prefix delete.

Next steps#