Skip to content

fix(kvstoreentry): close directory upload files and rewind before retry - #1966

Closed
Tyagiquamar wants to merge 4 commits into
fastly:mainfrom
Tyagiquamar:fix/1942-kvstoreentry-dir-close
Closed

Tyagiquamar wants to merge 4 commits into
fastly:mainfrom
Tyagiquamar:fix/1942-kvstoreentry-dir-close

Conversation

@Tyagiquamar

@Tyagiquamar Tyagiquamar commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Problem

kv-store-entry create --dir opens each file with os.Open in ProcessDir and never closes it, which can exhaust file descriptors on large directories. On a retry after net/http: cannot rewind body after connection loss, the first attempt already consumed the request body, so the retry could upload empty or truncated content.

Reproduction

See issue #1942: read from the captured request body after upload (still open, not os.ErrClosed); simulate connection-loss error on first insert and observe empty body on retry.

Root cause

Missing Close() on opened files and no seek back to offset 0 before the retry path.

Solution

defer f.Close() after each successful os.Open. On the connection-loss retry path, Seek(0, io.SeekStart) on the same open file and reuse the existing FileLengthReader (no second FileLengthReader call).

Tests

create_processdir_test.go:

  • TestProcessDirClosesUploadedFiles
  • TestProcessDirRetriesAfterConnectionLossWithRewoundBody (asserts exactly two insert attempts)

Test plan

  • go test ./pkg/commands/kvstoreentry/... -run 'TestProcessDir' -count=1 (Docker)

Fixes #1942

Close each file opened in ProcessDir and seek to the start before
retrying insert after a connection-loss rewind error.

Fixes fastly#1942

Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown

Thanks for the contribution! This pull request doesn't follow our pull request template:

  • the ### Change summary section is missing
  • the All Submissions: checklist is missing

Please edit the description to use the template. If it is still missing after 3 days, this pull request will be closed automatically.

Tyagiquamar and others added 2 commits November 12, 2026 17:37
ProcessDir tests construct CreateCommand without CLI flag defaults, leaving
dirConcurrency at zero and deadlocking the upload semaphores. Use the same
concurrency as the --dir-concurrency default and normalize test file LF.

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Tyagiquamar <mohdquamartyagi@gmail.com>
@jedisct1

jedisct1 commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Please update the description to use the PR template. Otherwise the bot will close the PR.

Also: the changelog entry should link to this PR (#1966), not to the issue.

APIClient: &mock.API{
InsertKVStoreKeyFn: func(_ context.Context, in *fastly.InsertKVStoreKeyInput) error {
if in.Key == "foo.txt" && capturedBody == nil {
if lr, ok := in.Body.(fastly.LengthReader); ok {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in.Body is already a fastly.LengthReader so you can write capturedBody = in.Body directly.

}
}

func TestProcessDirRetryStillFailsReportsConnectionLoss(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That test doesn't test these changes. Remove it, or at least make it check that the upload is tried only twice.

Comment thread pkg/commands/kvstoreentry/create.go Outdated
Comment on lines +332 to +342
lr, rewindErr := fastly.FileLengthReader(f)
if rewindErr != nil {
mu.Lock()
processingErrors = append(processingErrors, ProcessErr{
File: filePath,
Err: rewindErr,
})
mu.Unlock()
return
}
opts.file = lr

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That block can go.

Going back to the start of the file is enough. It still reads from the same file, so there's no need to call FileLengthReader() again.

@jedisct1 jedisct1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for these changes.

Left a few comments on things that should maybe be changed before it can be merged.

Close opened files after directory upload and rewind with Seek(0) before retrying insert after connection loss, per review feedback.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

Copy link
Copy Markdown

Closing because the pull request template is still missing after 3 days. Feel free to open a new pull request that uses the template.

@github-actions github-actions Bot closed this Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kv-store-entry create --dir never closes uploaded files and retries with an empty body

2 participants