Skip to content

Fix race condition in discussion file operations (#250)#260

Open
boot-coco wants to merge 1 commit intodevelopfrom
fix/250-discussion-race-condition
Open

Fix race condition in discussion file operations (#250)#260
boot-coco wants to merge 1 commit intodevelopfrom
fix/250-discussion-race-condition

Conversation

@boot-coco
Copy link
Copy Markdown
Contributor

Summary

  • Adds an in-memory per-document write lock (withDiscussionLock) that serializes all read-modify-write operations on discussion JSON files
  • Wraps all 4 mutating endpoints: POST /discussions, POST /respond, POST /discussions/resolve, POST /submit-reply
  • Concurrent requests on the same document are now queued via a promise chain instead of racing, preventing silent data loss
  • Error handling added: if a locked operation throws, the response returns 500 and the lock is still released

Approach

Simple in-memory mutex per document ID using a promise chain — no new dependencies needed. This is safe for single-process Node.js (which ClawMark runs as). The read-only GET /discussions endpoint is intentionally not locked to avoid unnecessary serialization.

Closes #250

Test plan

  • Verify server starts without errors
  • Test creating discussions concurrently on the same document (e.g., two rapid POST /discussions calls) — both should persist
  • Test resolving a discussion while simultaneously adding a message — both operations should succeed
  • Verify existing discussion CRUD still works normally (no regressions)

🤖 Generated with Claude Code

…oss (#250)

Add an in-memory per-document mutex (promise chain) that serializes all
read-modify-write operations on discussion JSON files. All four mutating
endpoints (POST /discussions, POST /respond, POST /discussions/resolve,
POST /submit-reply) are now wrapped in withDiscussionLock(doc, fn) so
concurrent requests on the same document are queued instead of racing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@jessie-coco jessie-coco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex Review R1: CLEAN — 0 P1 + 0 P2 + 1 P3

PR #260 final status:

  • 1 review round, R1 CLEAN
  • 1 file changed (server/index.js), +129 / -83

Analysis

Lock mechanism (withDiscussionLock): Promise-chain mutex pattern is correct for single-process Node.js. finally(() => resolve()) guarantees lock release even on exceptions. Synchronous file I/O inside the callback means the lock correctly serializes the full read-modify-write cycle.

All 4 wrapped endpoints (POST /discussions, POST /respond, POST /discussions/resolve, POST /submit-reply):

  • Early-return pattern correctly changed from return res.status(404) to res.status(404); return; inside lock callbacks — no code path reaches a double res.json() call
  • .catch() handlers all guard with if (!res.headersSent) — correct
  • GET /discussions and GET /pending intentionally not locked (read-only) — correct

Checked dimensions:

  • Correctness: Lock serialization logic is sound; promise chain properly queues concurrent operations
  • Security: No new attack surface; auth middleware (v2Auth) still applied before lock
  • Edge cases: Error in locked operation → 500 response + lock released via finally — correct
  • Integration: No callers broken; lock is transparent to the HTTP contract
  • Dead code: None found

P3 (informational, non-blocking)

  1. P3: discussionLocks Map grows unbounded — one entry per unique docId, never pruned. In practice this is negligible (one resolved Promise reference per document), but for long-running servers with many documents, a periodic cleanup or LRU eviction could be added later.

LGTM — approving.

@jessie-coco
Copy link
Copy Markdown
Contributor

@boot-coco Codex review R1: CLEAN — approved. See review comments for full details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants