feat: durable SQLite-backed job store (replaces in-memory _JOBS dict)#199
Closed
seonghobae wants to merge 1 commit into
Closed
feat: durable SQLite-backed job store (replaces in-memory _JOBS dict)#199seonghobae wants to merge 1 commit into
seonghobae wants to merge 1 commit into
Conversation
Replace the per-process in-memory _JOBS dict with job_store.JobStore, a SQLite (WAL) store shared by every worker process on the host: - job state survives service restarts and crashes; polling clients of interrupted jobs get an honest 'failed: Interrupted by service restart' instead of a permanent 404/hang - status/result requests work under multi-worker uvicorn/gunicorn, since all workers now read the same store - startup maintenance reconciles the store with reality: orphaned queued/processing jobs are failed and their temp workspaces reclaimed; terminal jobs past the retention window are purged together with their workspaces (no more disk leaks from results nobody downloaded) - CODEC_CARVER_JOB_DB points the store at a persistent volume; CODEC_CARVER_JOB_RETENTION_SECONDS tunes the sweep window - field-whitelisted SQL, one short-lived connection per operation, delete-returning semantics so workspace cleanup happens exactly once under concurrent deletes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJRVbDrp1vGYkJgNHMGPpG
Contributor
Author
|
Closing to avoid a duplicate The valuable ideas here ( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #166 (async job API). Retarget to
mainafter #166 lands.What
Replaces the async job API's per-process in-memory
_JOBSdict withjob_store.JobStore— a small SQLite (WAL) store — and adds a startup maintenance pass that reconciles the store with reality.Why (what the dict could not do)
_JOBSdictqueued/processingjobs are markedfailed: Interrupted by service restartat startupuvicorn --workers N→ submit lands on worker A, status poll on worker B → 404Design notes
sqlite3, WAL mode + 30s busy timeout, one short-lived connection per operation (thread- and process-safe, no leaked handles).CODEC_CARVER_JOB_DBsets the DB path (point at a persistent volume in production; defaults to a per-host path under the system tempdir so it is zero-config).CODEC_CARVER_JOB_RETENTION_SECONDStunes the sweep window.ValueErroron anything else), values are always bound parameters;DELETE … RETURNINGgives exactly-once workspace cleanup under concurrent result downloads;/jobs/{id}/resultkeeps the workspace-confinement check from feat: async job API for the web service (submit → status → result) #166 and now degrades to 410 (not a crash) on a corrupted row with nooutput_path.Tests
tests/test_job_store.py(15 cases): CRUD roundtrip, reopen-survival (simulated restart / second worker), interrupted-job recovery, stale purge boundaries, field-whitelist rejection (incl. SQL-injection-shaped field names), exactly-once delete, 8-thread concurrent writer stress.tests/test_saas_web.py: job tests ported to an isolated per-test store; newJobDurabilityTestscover status surviving a store reopen and the startup maintenance path end-to-end (verified through the real ASGI lifespan).pytest tests/→ 144 passed; the 5 pre-existingtest_media_shrinkerxattr failures also fail on the base branch (environment-specific, unrelated).interrogatedocstring gate: 100%.🤖 Generated with Claude Code
https://claude.ai/code/session_01CJRVbDrp1vGYkJgNHMGPpG