feat: fail fast when two chatwoot processes mutate the same conversation - #30
Conversation
Exclusive, non-blocking advisory locks in ~/.chatwoot/locks/, one file per conversation ID. flock on unix, LockFileEx on Windows (promotes x/sys to a direct dependency). The OS releases the lock if the process dies, so there is no stale-lock cleanup.
conv reply, resolve, assign, and snooze now take the per-conversation lock around their API call, so running e.g. reply concurrently from two terminals sends once and errors once instead of double-sending. Fail-fast rather than wait: a queued duplicate would still fire after the holder finishes.
…n lock Completes lock coverage across all mutating conversation verbs, so any overlapping pair of them fails fast instead of double-running.
Completes lock coverage: every mutating conv verb now takes the lock.
|
@codex please review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e08ea1f37
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
resolveAgent hits the agents/profile API when --agent is a name or an uncached me, so locking after it meant a lock conflict could surface as a masked lookup error instead of failing fast before any request. Resolution now happens while the lock is held.
govulncheck flagged the Encrypted Client Hello privacy leak in the 1.26.4 standard library, reachable through the SDK's HTTP client.
|
Merging this branch will increase overall coverage
Coverage by fileChanged files (no unit tests)
Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code. Changed unit test files
|
Running
chatwoot conv 123 reply "hi"in two terminals at once sends the reply twice, since nothing coordinates concurrent invocations. This is easy to hit when scripts or agents drive the CLI, and the same race applies to every mutating verb.This PR adds an exclusive per-conversation file lock (
~/.chatwoot/locks/conv-<id>.lock) that all mutatingconvverbs (reply,resolve,open,pending,snooze,assign,unassign,label,priority) acquire around their API call. The losing process fails fast with "another chatwoot command is already running on this conversation" instead of waiting, because a queued duplicate would still fire once the holder finished. The lock is an OS advisory lock (flockon unix,LockFileExon Windows, promotinggolang.org/x/systo a direct dependency), so the kernel releases it if the process dies and no stale-lock cleanup is needed. Lock files are never unlinked on release, since deleting them races with other processes locking the same path.Tested at both layers: unit tests for conflict/release semantics, plus a command-level test that runs every verb against an empty
Appwhile the lock is held, proving they bail out before reaching the API client.Limitations: this only serializes processes on one machine, and it only guards the overlap window. Sequential duplicate replies are a separate problem.