PATTERN Cited by 1 source
Pluggable state backend¶
Definition¶
A factoring in which a system's state needs — key-value cache, distributed locks, thread / session subscriptions, TTL-based expiry, namespaced key prefixes — are expressed against a generic state interface, and the concrete backend (Redis, ioredis, PostgreSQL, etc.) is supplied as an adapter instance at construction time.
The developer writes:
import { Chat } from "chat";
import { createPostgresState } from "@chat-adapter/state-postgres";
const bot = new Chat({
adapters: { slack: createSlackAdapter() },
state: createPostgresState(),
});
createPostgresState() for createRedisState() without
changing the rest of the bot's code.
(Source: sources/2026-04-21-vercel-chat-sdk-brings-agents-to-your-users.)
Why it exists¶
The canonical reason to abstract state in a chat-bot / agent SDK is that teams already run some store — Postgres, Redis, or something else — and forcing them to add a second dependency for bot state is a deployment tax.
Vercel's Chat SDK post makes this explicit: "PostgreSQL is now supported as a production-ready option, so teams already running Postgres can persist bot state without adding Redis to their infrastructure." The adapter is the primitive that makes this swap possible without a rewrite.
Common interface (minimum viable)¶
From the Chat SDK post, the state layer covers:
- Key-value cache with TTL for ephemeral data (rate-limit counters, in-flight conversation state).
- Distributed locks across multiple bot instances — needed e.g. so
two replicas don't both
thread.subscribe()to the same mention and post duplicate responses. - Namespaced state via a "configurable key prefix" — lets multiple bots share a store without colliding.
- Thread subscriptions — the bot-layer abstraction over "this thread is one the agent should track".
Chat SDK's backend adapters (2026-04-21 snapshot)¶
| Backend | Package | Notes |
|---|---|---|
| Redis | @chat-adapter/state-redis |
Shipped from launch. |
| ioredis | (implicit in post) | Shipped from launch. |
| PostgreSQL | @chat-adapter/state-postgres |
Uses pg (node-postgres), raw SQL. "Automatically creates the required tables on first connect." Distributed lock across instances. Community PR #154 by @bai. |
What the Chat SDK post does NOT disclose¶
- Distributed-lock implementation on Postgres. The post says the
Postgres adapter "supports…distributed locking across multiple
instances", but doesn't disclose whether it uses
pg_advisory_lock, row-levelSELECT … FOR UPDATE, or an application-level heartbeat lease. Each has different correctness properties under network partitions. - Consistency guarantees. Redis's default TTL-based cache has weak ordering guarantees vs Postgres's MVCC. Whether the SDK's interface papers over this — or surfaces backend-specific semantics — is unspecified.
- Migration semantics. Swapping from Redis to Postgres mid-flight requires migrating live state; the post doesn't cover migration tooling.
The pluggable-backend pattern more broadly¶
This pattern appears beyond chat SDKs — wherever a framework doesn't want to prescribe infrastructure: - BullMQ / Bee-Queue (Node job queues, Redis-pinned historically; pluggable backend variants exist). - Django's cache framework (local-mem / Redis / Memcached / database-cache / file-cache as swappable backends via Python entry points). - LangChain's memory/storage abstractions.
The Chat SDK's instance is distinctive because the adapter layer is also where distributed locking and namespacing live — those are not always part of a cache abstraction. Chat SDK puts them there because chat bots in production need both.
Trade-offs¶
- Lowest-common-denominator interface. The common interface can only expose operations every adapter supports. Power users may want Postgres-specific advisory-lock semantics or Redis-specific Lua scripting — they're unreachable through the shared interface.
- Backend-dependent reliability. A correctness bug in one adapter doesn't affect the other, but it also doesn't get tested through the other — adapter parity takes active effort.
- Operational complexity shifts, not disappears. Postgres's connection-pool tuning, Redis's eviction policies — the adapter abstracts the interface but not the operational burden.
- No free cross-backend failover. If Redis goes down, you can't fail over to Postgres without a restart; the backend choice is effectively deploy-time.
Seen in¶
- sources/2026-04-21-vercel-chat-sdk-brings-agents-to-your-users — Redis + ioredis (launch) + Postgres (now production-ready); TTL caching, distributed locks across instances, namespaced key prefix, auto-schema on first connect for Postgres.