fix(upload): preserve resume offset, survive pod restart, reject chunk gaps#350
Merged
Conversation
…k gaps Co-authored-by: Cursor <cursoragent@cursor.com>
1cfd234 to
342bc96
Compare
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.
Summary
Three small, independent fixes to the chunked upload state machine that together stop a class of resume bugs (cursor stuck, finalize ENOENT, "completed" but truncated file):
SaveFiledropsO_APPENDon the resume path. POSIX forces every write to EOF whenO_APPENDis set, which silently nullified the explicitSeek(offset, SeekStart)below it: any chunk that did not start exactly at the temp file's tail (e.g. a half-written chunk left by a network drop or page refresh) ended up appended to EOF instead of overwriting at the requested offset, accumulating drift until the final chunk hit the early-return atinfo.Offset > offsetEndand the response was rejected assuccessbut unmarshal-mismatched (500).HandleUploadChunksonly purges the on-disk temp/.info on chunk 1. Mid-stream retries that arrive after a pod restart find an empty in-memoryInfoSyncMap; previously this branch unconditionally deleted the on-disk temp file, after which subsequent chunks no longer matched anySaveFilecondition and were silently skipped, leaving the final chunk'sMoveFileByInfoto fail withno such file or directory.chunk 1(which truncates the temp vianewFile=true) with mid-streamchunk Nrequests still in flight, the gap chunks fell through everySaveFilepredicate and were quietly accepted as 200, so the final chunk'sMoveFileByInfofinalized a truncated file as if the upload had succeeded. Such requests now return an explicit error so the client can react instead of finalizing bad data.Test plan
uploadedBytesand completes; final size matches source.no such file or directoryon the final chunk.completedfor a truncated file.chunk 1tochunk Nstraight through) unaffected.Made with Cursor