Relationships
#2553 model method run holds the per-model lock across the datastore pull, so a 40ms method holds it ~70s and any concurrent call hits the 60s LOCK_TIMEOUT
Opened by sntxrr · 9/25/2026· Shipped 9/28/2026
Defect
swamp model method run acquires the per-model lock before the datastore pull and releases it after the push. On an S3 datastore where the pull takes ~66s, a 40ms method holds the lock for ~70s, which is longer than the 60s lock wait. So any two overlapping calls to the same model deterministically fail one with LOCK_TIMEOUT. The backoff from #520 cannot help once hold time > wait time.
Evidence
swamp 20260925.193456.0-sha.752d3384, @swamp/s3-datastore 2026.09.24.1, one namespace (~98k catalog rows), macOS. Single run, --log-level trace, seconds since start:
| t | event |
|---|---|
| 0.8s | datastore·lock: Acquiring lock |
| 1.0s | Syncing model … from datastore... |
| 67.2s | Executing method "investigate" |
| 67.3s | Completed investigate … succeeded in 40ms |
| 68.3s | Wrote catalog export (98407 row(s)) |
| 70.3s | Committed 14 file(s) to datastore index |
Two concurrent runs of the same model method (both inputs valid):
| build | pairs | stagger | result |
|---|---|---|---|
| 20260925.193456 | 1 | 0s | one succeeded (exit 0, 71s), one LOCK_TIMEOUT … timed out after 63537ms (exit 1) |
| 20260924.181834 | 3 | 0s | same in every pair (timeouts 60.7–65.6s, exit 1) |
| 20260924.181834 | 1 | 15s | second run LOCK_TIMEOUT |
| 20260924.181834 | 2 | 30 and 45 seconds | both succeed |
Serial runs: 5/5 exit 0.
Expected
The lock's hold time should be bounded by the method, not by the datastore sync. Either pull before acquiring the per-model lock (re-checking under the lock if needed), or scope the pull to the model being run. Alternatively, make the lock wait configurable from the CLI, or derive it from the observed hold time. The #520 burn-in showed 300s eliminated errors, but I can't find a user-facing knob for model method run.
Also: exit code does not match help
swamp model method run --help says lock_timeout … (exit 75) and 75 = lock contention (temporary — retry with backoff). The losing run above exits 1 with "code": "LOCK_TIMEOUT" in the JSON error, on both builds above, so callers that retry on 75 never retry.
Related (shipped/closed): #520, #812, #829, #1932.
Shipped
Click a lifecycle step above to view its details.
zack commented 9/28/2026, 5:38:01 PM
Thanks @sntxrr for reporting this! We shipped: Core-only fix for swamp-club#2553. Map extension-datastore lock timeouts (code LOCK_TIMEOUT from S3/GCS) to the core LockTimeoutError at the single point where extension datastore providers enter core, so model method run and every other lock-taking command exits 75 with code lock_timeout, and serve's LockTimeoutError handling fires. Stop invalidating the whole catalog after a pull that pulled nothing, which removes a full backfill that otherwise runs while the per-model lock is held. Document SWAMP_LOCK_TIMEOUT_MS in help. The unlocked pre-pull the issue suggests is rejected because it can overwrite a concurrent run's unpushed local writes. Bounding the pull itself is extension work (swamp-club#2590).. The fix has been merged and a release is on its way. We appreciate your contribution to swamp.
zack commented 9/28/2026, 5:39:24 PM
Also: This should help but there are changes we have to make in the extensions as well to really solve this
Sign in to post a ripple.