Python: fix: make FileCheckpointStorage tmp filename unique per save - #8189
Conversation
|
@microsoft-github-policy-service agree |
|
Heads up on some history you probably could not have seen — your diagnosis is correct, and this is not a criticism of the fix. The same race is already open as #7748 (Evan Mattson (@ermattson), 19 Aug), and #8182 that this PR fixes is a duplicate of it filed today. There is a PR in review for #7748 — #7757, mine — and it started as almost exactly this change: unique per-save temp name plus best-effort cleanup. The reason I am flagging it rather than leaving you to find out at review: Evan Mattson (@moonbox3)'s review on #7757 established that the shared temp name was one of three coupled defects, not the whole thing. The other two only appear once the temp-name collision is gone:
So a unique temp name closes the One concrete note that applies whichever PR lands: Which PR to take is entirely up to the maintainers; I am not arguing for mine. I would rather you knew now than after spending review cycles on it. If they prefer this one, the two follow-on defects above are worth folding in, and I am happy to hand over what the review turned up. |
The previous temp name embedded the destination filename, so a checkpoint id accepted by _validate_file_path could push the temp name past the filesystem filename-length limit and fail the save for an otherwise legal destination. Use a short constant-prefix name instead.
|
Thanks for the heads-up — very helpful context, and I appreciate you flagging #7748/#7757 rather than letting me burn review cycles discovering it. I've updated the fix to use a short, id-independent temp name ( Agreed the serialize-writes and cancellation defects in #7757 are real follow-ons; I've kept this PR scoped to the temp-name collision you diagnosed, and I'm happy to defer entirely if the maintainers prefer #7757's more complete fix. |
Motivation & Context
FileCheckpointStorage.savewrites atomically via a FIXED temp filename (file_path.with_suffix(".json.tmp")) per checkpoint id. Two concurrent saves of the same checkpoint id both write the same.tmppath and both callos.replace(tmp, final); whichever replace runs first consumes the tmp file and the other save dies with a rawFileNotFoundError— neitherWorkflowCheckpointExceptionnor a documented failure.Fixes #8182
Description & Review Guide
_write_atomicnow uses a unique per-save temp filename ({file_path.name}.{uuid.uuid4().hex}.tmp) and best-effort cleanup in afinallyblock, so concurrent saves of the same checkpoint id never share a tmp file.os.replaceremains the atomic commit point..tmpfile (removed best-effort), never a torn checkpoint.Related Issue
Fixes #8182 — verified no open PR exists for it.
AI-Generated Code Disclosure
This change was implemented with AI assistance (OpenCode + muse-spark) and reviewed for correctness, security, and license compatibility.
Verification
test_file_checkpoint_storage_concurrent_saves_same_id— 8 concurrent saves of the same id viaasyncio.gather; before the fix this raisesFileNotFoundError(confirmed by mutation check: reverting to the fixed name reproduces the crash).test_checkpoint,test_checkpoint_storage_conformance,test_checkpoint_encode,test_checkpoint_decode,test_checkpoint_validation).