Skip to content

fix: atomically persist index JSON via temp-file-then-rename#31

Open
mikemolinet wants to merge 1 commit intosrobinson:mainfrom
mikemolinet:fix/atomic-json-writes
Open

fix: atomically persist index JSON via temp-file-then-rename#31
mikemolinet wants to merge 1 commit intosrobinson:mainfrom
mikemolinet:fix/atomic-json-writes

Conversation

@mikemolinet
Copy link
Copy Markdown

Summary

Index JSON files (.mdm/indexes/*.json, .mdm/config.json) are now written atomically via a per-save unique .tmp file plus fs.rename. A process killed mid-write no longer leaves truncated JSON that surfaces as IndexCorruptedError and forces a full rm -rf .mdm/ + reindex. Scope is process-kill, not OS crash.

Changes

  • writeJsonFile (src/index/storage.ts) now writes to ${path}.${pid}.${random}.tmp and renames to path on completion. On error, best-effort unlinks the tmp; the original FileWriteError still propagates.
  • Unique per-save tmp suffix avoids collisions when two saves run concurrently on the same repo.
  • All four savers (saveDocumentIndex, saveSectionIndex, saveLinkIndex, saveConfig) route through writeJsonFile — no call-site changes.
  • Adds tests covering the no-leftover-tmp invariant, the stale-tmp-doesn't-break-reads-or-saves invariant, and the cleanup-on-rename-failure invariant.

Test plan

  • pnpm test passes, including the three new tests.
  • pnpm typecheck and pnpm format clean.
  • Manual repro: on main, run mdm index against a large repo, Ctrl-C during save → subsequent mdm commands error with IndexCorruptedError. On this branch, same kill sequence → subsequent mdm commands read the previous index cleanly.

Writes to .mdm/indexes/*.json and .mdm/config.json now go through a
per-save unique temp file that is renamed to the final path on
completion. A process killed mid-write no longer leaves a truncated
JSON file that forces users to rm -rf .mdm/ and rebuild.

- Refactors writeJsonFile; all save* helpers route through it, so no
  call-site changes are needed.
- Unique temp suffix (pid + random bytes) prevents concurrent saves
  from colliding on a shared .tmp path.
- Leftover .tmp is best-effort unlinked on failure; the original
  FileWriteError still propagates.
- Adds tests covering the no-leftover-tmp invariant, the
  stale-tmp-doesn't-break-reads-or-saves invariant, and the
  cleanup-on-rename-failure invariant.
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