Skip to content

Per-user history lock is in-process only — revisit before going multi-replica #33

Description

@scarolan

Context

handleMessage in lib/chat.js does a read-modify-write on each user's conversation history (convoStore.getchat.chatconvoStore.set). Two messages arriving close together from the same user would otherwise race: both read the same base history and the second set() clobbers the first turn ("Data forgot what I just said").

This was fixed in the commit that added withUserLock by serializing each user's turns through an in-process promise chain.

The limitation

The lock lives in a module-level Map in a single Node process. It is correct only while the bot runs as exactly one process — which is true today (single bot-data.service systemd unit).

The moment the bot runs as more than one replica, or a second process talks to the same Redis, the in-memory lock no longer serializes anything and the read-modify-write race returns across processes.

What to do if/when we go multi-replica

Replace the in-process Map lock with a Redis-level primitive on the per-user key, e.g.:

  • Optimistic concurrency with WATCH/MULTI/EXEC (retry on conflict), or
  • A short-lived advisory lock via SET <lockkey> <token> NX PX <ttl> with token-checked release.

Deliberately not done now: it adds a Redis round-trip to every turn and is over-engineering for a single-process bot. This issue exists so the single-process assumption is documented rather than a silent trap.

Pointers

  • lib/chat.jswithUserLock + handleMessage
  • Concurrency regression test: test/chat.test.js — "serializes concurrent turns from the same user"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions