Store is four methods, and the package ships two implementations, memoryStore() and
redisStore(). A SQL one would let people who already run Postgres skip adding Redis
for this.
export type Store = {
load(conversationId: string): Promise<ConversationState | null>
save(state: ConversationState): Promise<void>
delete(conversationId: string): Promise<void>
withLock<T>(conversationId: string, fn: () => Promise<T>): Promise<T>
}
The hard part is withLock
It is not a convenience. Two webhooks for the same conversation arriving together will
read, modify and write over each other, and a message goes missing. The implementation
has to serialise per conversation id. In Postgres, pg_advisory_xact_lock on a hash of
the id is the natural fit, and it releases with the transaction, which is what you want
when a process dies holding it.
What it should look like
- Duck-type the client the way
src/store/redis.ts does, so the package keeps zero
runtime dependencies and works with whichever driver somebody already has.
- State is serializable by design, so one
jsonb column is enough. Document the schema
and let the user create it.
- The Redis store's tests are the model, including the lock test that was verified by
breaking the implementation on purpose to watch it fail.
Comment before you start if you would rather agree on the shape first, particularly on
which client interface to accept.
Storeis four methods, and the package ships two implementations,memoryStore()andredisStore(). A SQL one would let people who already run Postgres skip adding Redisfor this.
The hard part is withLock
It is not a convenience. Two webhooks for the same conversation arriving together will
read, modify and write over each other, and a message goes missing. The implementation
has to serialise per conversation id. In Postgres,
pg_advisory_xact_lockon a hash ofthe id is the natural fit, and it releases with the transaction, which is what you want
when a process dies holding it.
What it should look like
src/store/redis.tsdoes, so the package keeps zeroruntime dependencies and works with whichever driver somebody already has.
jsonbcolumn is enough. Document the schemaand let the user create it.
breaking the implementation on purpose to watch it fail.
Comment before you start if you would rather agree on the shape first, particularly on
which client interface to accept.