Apr 25, 2026
Commits
161dd64 — Add ETag CAS + per-key lock for race-safe location DB writes
Concurrent writes to the same location's SQLite DB silently lost rows: the download/modify/put_object cycle had no ETag check, so two writers (cross-container or same-container) would both download, both write, and the second put_object would clobber the first.
Two protection layers now guard writes:
-
S3 conditional writes (ETag CAS) — cross-container. Each
put_objectincludesIfMatch=etag(the ETag captured at download). A412 PreconditionFailedtriggers cache eviction, re-download, and retry (up to 5 attempts with jittered exponential backoff). New-location seed race fixed viaIfNoneMatch='*'atomic create. -
threading.Lockkeyed by S3 key — same-container serialization. Concurrent requests for the same location DB within one Modal container are serialized by a per-key lock, preventing wasted S3 round-trips and retry budget.
with_location_db(location, fn) is the new write entry point. All existing write call sites migrated: store, store_chunked_init, store_chunk, delete_asset, register_external_asset, sync_project_from_supabase. Read paths unchanged. Legacy _save_location_db retained with deprecation notice.
Testing:
- 5 new unit tests: CAS conflict retry, CAS exhaustion, seed race, parallel writes, lock serialization
- Gated real-S3 chaos suite (
test_concurrency.py,RUN_S3_CONCURRENCY_TESTS=1): 20 parallelstore()+ 5 parallel chunk uploads againstsparky-datadev prefix
Changed:
dev/core/data.py— +397/-105 lines:with_location_db,_download_location_db,_upload_location_db,_seed_location_db,_get_db_lock,_DBPreconditionFailed,_db_cache,_db_locks; all write call sites migrateddev/data/sync.py— migratedsync_project_from_supabasetowith_location_dbdev/tests/test_data_service/test_concurrency.py— new: gated S3 chaos tests (parallel store + chunk upload)dev/tests/test_data_service/test_data_service.py— +279 lines: unit tests for CAS retry, exhaustion, seed race, lock serialization