Skip to content

fix(registry): persist race on Windows; recover metadata-less done jobs - #301

Merged
thcp merged 2 commits into
mainfrom
fix/registry-resilience
Jul 17, 2026
Merged

fix(registry): persist race on Windows; recover metadata-less done jobs#301
thcp merged 2 commits into
mainfrom
fix/registry-resilience

Conversation

@thcp

@thcp thcp commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Closes #281, closes #284. Phase 2 of #273 — plan: #273 (comment) (PR-2C + PR-2E, combined: both are small fixes to the same file).

What

Persist race (#281)

persist() runs concurrently from the pipeline thread, API threads (delete/sections), and the sweep loop — all writing the same registry.json.tmp. Interleaved writers could clobber each other, and on Windows os.replace over a tmp file another writer holds open raises an uncaught PermissionError (silently killing a pipeline task via the generic callback). Fix, both belts from the plan:

  • unique temp name per call (.registry.<uuid>.tmp — the _ensure_cached_mp3 pattern),
  • write+replace moved under the existing _lock, wrapped best-effort (log-and-return, matching the settings store's documented philosophy), with a finally unlink so no temp files leak.

Crash-window recovery (#284)

_recover_done_job demanded metadata.json, which is written after status=done — a crash in between left a complete stems dir permanently unrecoverable ("re-upload to restore"). Now: recovered with a placeholder title (Recovered track <id6>), and a minimal metadata.json is written immediately so the next restart takes the normal path — self-healing, not a permanent special case. Dirs without stems are still ignored (unchanged rule).

Testing

  • 8 threads × 30 concurrent persist() calls: no exceptions, valid JSON, zero stray temp files.
  • Metadata-less recovery: placeholder title + self-healed metadata asserted; no-stems dirs still ignored.
  • Updated test_restore_skips_orphan_without_metadatatest_restore_recovers_orphan_without_metadata (the old assertion was the Jobs finished during a crash window are unrecoverable #284 bug's behavior).
  • Full suite: 182 passed; ruff clean.

🤖 Generated with Claude Code

persist() is called concurrently from the pipeline thread, API threads,
and the sweep loop, all sharing one temp path. Two writers could
collide, and on Windows os.replace over a file another writer holds
open raises an uncaught PermissionError. The write+replace now happens
under the existing lock with a unique temp name per call (the
_ensure_cached_mp3 pattern), best-effort like the settings store.

_recover_done_job required metadata.json, which is written after status
flips to done -- a crash in that window left a complete stems dir
permanently unrecoverable. Such dirs now recover with a placeholder
title, and a minimal metadata.json is written immediately so the next
restart takes the normal path (self-healing, not a lasting special
case). The stems-present requirement is unchanged.

Closes #281
Closes #284
Comment thread app/core/registry.py Dismissed
@thcp
thcp merged commit 666005e into main Jul 17, 2026
8 checks passed
@thcp
thcp deleted the fix/registry-resilience branch July 17, 2026 00:44
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.

Jobs finished during a crash window are unrecoverable Registry persist race on Windows

1 participant