Redis

The recommended production store. Live streams are pushed, not polled.

server/orders.ts
import { createServer } from 'experimental-a2/server'
import { redis } from 'experimental-a2/store-redis'
import { orders } from '@/contracts'

export const ordersServer = createServer({
  contract: orders,
  store: redis({ url: process.env.REDIS_URL }),
  consumers: [
    /* ... */
  ],
})
npm install ioredis

Works on a single Redis or Valkey instance and on non-cluster providers such as Upstash, which is durable by default (use its TCP rediss:// URL). There's nothing to set up: A2 creates its keys as it writes. For rotating credentials or custom TLS and reconnect settings, use the same store on node-redis: Valkey.

Options

Option
urlcreates an ioredis client
clientyour own ioredis client instead
keyPrefixisolates several apps on one Redis. Default 'a2'; no braces
clock, idstest seams for timestamps and generated event ids

Upgrade

The store records its key layout in <keyPrefix>:schema, and refuses a prefix marked with another one with STORE_NOT_CONFIGURED. A release that changes the layout says what to do on its upgrade page. This release names every key differently from earlier ones, so give the store a fresh keyPrefix.

How it works

  • Each session is a Redis Stream, and every key of a session shares one hash tag.
  • Every write is one atomic script over its session's keys. On Upstash, which runs a script under a database-wide lock by default, A2 marks each script to lock only its session's keys. Redis and Valkey run one command at a time, so A2 keeps scripts short instead.
  • Every write publishes a wake-up after its script, so live streams are push-driven, and connections scale with your processes, not with your viewers.
  • A wake-up is disposable: a safety re-read recovers a lost one within ten seconds.

Cost per command

On providers that bill per command, such as Upstash, appending an event with a consumer and running it takes 19 commands, and an idle live stream takes 6 a minute.