Skip to content

chore(release): memory-core 0.3.2 - lock-tolerant LocalBackend commit#20

Merged
vreshch merged 1 commit into
masterfrom
fix/commit-lock-retry
Jul 6, 2026
Merged

chore(release): memory-core 0.3.2 - lock-tolerant LocalBackend commit#20
vreshch merged 1 commit into
masterfrom
fix/commit-lock-retry

Conversation

@vreshch

@vreshch vreshch commented Jul 6, 2026

Copy link
Copy Markdown
Member

Closes #19

Problem

LocalBackend's per-write git commit (writeDoc/edit/delete -> git add + git commit) was not index.lock-retry-tolerant. When a couch/git sync-cycle commit held .git/index.lock at the same moment, the engine write failed with a spurious Command failed: git commit. CLI M5 adversarial verification (20s write+sync storm: 211 CRUD + 138 sync ticks) saw ~9/211 writes and 10/138 syncs hit this. Data always converged (atomic rename + next tick) - only the caller saw a phantom failure.

Fix

A short, bounded retry on index.lock contention, mirroring the classify logic the sync cycles already use.

  • git.ts: isIndexLockError() classifier (matches index.lock / "another git process seems to be running", reads err.message + any err.stderr) + withIndexLockRetry() - 5 retries, 50ms steps capped at 250ms (~750ms worst case), then surfaces the original error.
  • Only the lock race is retried. Any non-lock git error throws immediately - real failures are never masked.
  • local-backend.ts: wrap the mutating add / status / commit calls in the retry.
  • No-op-write short-circuit (0.1.2) and all existing behavior unchanged.

Tests (test/m5-commit-lock-retry.test.ts, 11 new)

  • isIndexLockError classification: real lock stderr, "another git process" wording, separate stderr prop, false for non-lock + non-Errors.
  • withIndexLockRetry: retries a lock error then succeeds; fails fast on a non-lock error (no retry, no wait); bounded - a permanent lock surfaces after exactly 5 retries with backoffs [50,100,150,200,250].
  • Real-repo integration: a write succeeds once a transient .git/index.lock is released mid-retry; a permanent lock surfaces the error in < 3s (no hang); no-op short-circuit unaffected; 20 concurrent writes all land while an external process cycles the index lock (racing-writers proof).

Reverting only the wiring (keeping the helpers) reproduces the bug: both integration tests fail with Command failed: git add -A.

Full npm run verify green: 21 files, 165 tests. Zero new deps.

The per-write git commit (writeDoc/edit/delete) now retries on transient
.git/index.lock contention from a concurrent sync-cycle commit, instead of
surfacing a spurious 'Command failed: git commit' to the caller.

- git.ts: isIndexLockError classifier + withIndexLockRetry (5 retries, 50ms
  steps capped 250ms); only the lock race is retried, every other git error
  throws immediately so real failures are never masked.
- local-backend.ts: wrap the add/status/commit mutations in the retry.
- No-op-write short-circuit and all existing behavior unchanged.

Closes #19
@vreshch vreshch marked this pull request as ready for review July 6, 2026 22:07
@vreshch vreshch merged commit a72a0c6 into master Jul 6, 2026
1 check passed
@vreshch vreshch deleted the fix/commit-lock-retry branch July 6, 2026 22:07
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