Skip to content

feat: Redis distributed lock for high concurrency#661

Closed
jlin53882 wants to merge 13 commits intoCortexReach:masterfrom
jlin53882:feat/redis-distributed-lock
Closed

feat: Redis distributed lock for high concurrency#661
jlin53882 wants to merge 13 commits intoCortexReach:masterfrom
jlin53882:feat/redis-distributed-lock

Conversation

@jlin53882
Copy link
Copy Markdown
Contributor

Summary

This PR implements a Redis-based distributed lock to solve the high concurrency lock contention problem identified in Issue #643.

Problem

When captureAssistant=true and sessionMemory.enabled=true:

  • Multiple agents write to memory simultaneously
  • File lock cannot handle high concurrency
  • 200 concurrent writes -> only 6% success

Solution

Add src/redis-lock.ts with:

  • Token-based lock acquisition (prevents race conditions)
  • Lua script for safe release (only releases if token matches)
  • Graceful fallback: Returns no-op lock when Redis unavailable (no blocking)
  • 60s TTL with exponential backoff
  • Added ioredis dependency

Test Results

Concurrency File Lock Redis Lock
10 100% 100%
20 55% 100%
50 22% 100%
200 6% 97.5%

15x improvement!

Tests Added

  • est/lock-extreme-concurrent.test.mjs
  • est/lock-production-simulation.test.mjs
  • est/lock-bottleneck-identification.test.mjs
  • est/lock-200-concurrent.test.mjs
  • est/redis-lock-real.test.mjs
  • est/redis-lock-edge-cases.test.mjs
  • est/redis-lock-optimized.test.mjs
  • est/redis-lock-simulated.test.mjs

Required

\
npm install ioredis
\\

Related

Tests verify:
1. Current implementation: lock acquired once per entry (not per batch)
2. Two-phase approach: lock acquired once per batch (N -> 1)
3. Concurrent writes: read-modify-write has data consistency boundaries
4. Plugin vs Upgrader: updates different fields, no direct overwriting

Ref: Issue CortexReach#632
Two-Phase Processing:
- Phase 1: LLM enrichment (no lock)
- Phase 2: Single lock per batch for DB writes

Reduces lock contention from N locks (one per entry) to 1 lock per batch.

Tests:
- Lock count verification: 10 entries = 1 lock (was 10)
- LLM failure graceful degradation
- Batch boundary handling
- 100 entries stress test
- Added REFACTORING NOTE at class level explaining why upgradeEntry was split
- Documented the key difference: OLD = lock per entry, NEW = 1 lock per batch
- Added comments in prepareEntry explaining it contains the SAME logic as old upgradeEntry
- Added comments in writeEnrichedBatch explaining the single lock approach
- Added detailed comments in upgrade() showing before/after example
- Added inline comments in the batch loop explaining why Phase 1 doesn't hold lock
The old test was designed to verify the BUGGY behavior (1 lock per entry).
This update changes it to verify the FIXED behavior (1 lock per batch).

This aligns with Issue CortexReach#632 fix - the test now confirms:
- 3 entries = 1 lock (was 3 locks before)
…fixes)

[FIX F2] 移除 writeEnrichedBatch 的 outer runWithFileLock
- store.update() 內部已有 runWithFileLock,巢狀會造成 deadlock
- proper-lockfile 的 O_EXCL 不支援遞迴 lock

[FIX MR2] 每個 entry 寫入前重新讀取最新狀態
- Phase 1 讀取的 entry 是 snapshot
- plugin 在 enrichment window 寫入的資料會被 shallow merge 覆蓋
- 改用 getById() 重新讀取最新資料再 merge
- F1: 修正硬編碼 /opt/homebrew/ 路徑,改用動態路徑
- F3: 修復 EnrichedEntry.error 未設置問題(LLM fallback 時設置 error)
- F5: 新增 yield-every-5-writes 防止 plugin 長期飢餓
- 測試檔同步更新 F1 動態路徑
- Test 2: 實際呼叫 upgrader.upgrade() 觀察 lock 次數
- Test 3: 實際測試 Plugin + Upgrader 並發寫入
- Test 5: 實際驗證不同欄位互不覆蓋
- Return no-op lock instead of throwing Error when Redis is down
- Log Redis errors instead of silent swallow
- Fix duplicate ioredis entry in package.json
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@jlin53882 jlin53882 force-pushed the feat/redis-distributed-lock branch from 2a64448 to e22cd64 Compare April 18, 2026 17:19
@jlin53882 jlin53882 closed this Apr 18, 2026
@jlin53882 jlin53882 deleted the feat/redis-distributed-lock branch April 18, 2026 17:19
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.

1 participant