Skip to content

Close the MinIO object body after download - #2204

Closed
SebTardif wants to merge 1 commit into
fluxcd:mainfrom
SebTardif:fix/minio-object-reader-close
Closed

SebTardif wants to merge 1 commit into
fluxcd:mainfrom
SebTardif:fix/minio-object-reader-close

Conversation

@SebTardif

Copy link
Copy Markdown
Contributor

S3-compatible Bucket downloads use minio-go v7.2.1 Client.FGetObject. That method copies the GET body and never closes it. getObject says the caller must close. Go 1.27 does not finalize response bodies, so each successful download keeps the connection until the process exits.

MinioClient.FGetObject still stats the object and sets If-Match from that etag. It then downloads with GetObject and closes the reader. Bytes are written to a part file whose name is the sha256 of the base name and etag, the same scheme minio-go uses, so a parallel fetch of another key ending in .part.minio does not share that path. The part file is renamed into place only after the copy matches the stat size and the file close succeeds. A failed copy deletes the part file and leaves the destination untouched. A reader close error is logged and does not replace the download error. A writable-file close error is returned on the success path and logged when the copy already failed.

minio-go stays at v7.2.1. #2183 moved it from v7.2.0 to v7.2.1 for region ap-southeast-6. The upstream close arrived in minio-go #2272 together with checksum verification, released in v7.3.0. This patch does not take that release.

GCP (#2053) and Azure (#2200) already close their download bodies the same way.

The call has been in the Bucket provider since March 2022 (ed6c6ebc).

Tests

TestMinioClient_FGetObject_ClosesResponseBody fetches 120000 bytes, checks etag abc123 and If-Match: "abc123", writes a nested path, and requires the GET body to close. On the previous code the body was still open after 2 seconds.

TestMinioClient_FGetObject_CopyError serves 5 bytes with Content-Length: 64. The call fails, the destination is absent, no *.part.minio file remains, and the GET body closes. On the previous code the body stayed open.

go test -race ./internal/bucket/minio/ passed. make tidy fmt vet && make test passed.

minio-go v7.2.1 copies the object in FGetObject and never closes
the body. Go 1.27 does not finalize response bodies, so every
successful Bucket download keeps that connection until exit.

Download with GetObject and close the reader. Keep the etag
match. Write to a hashed part file, the same scheme minio-go
uses, and rename it into place only after the full copy and a
successful file close. A failed copy removes the part file.

Assisted-by: Grok/grok-4.7
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
@stefanprodan

Copy link
Copy Markdown
Member

Thanks for tracking this down. Since #2203 bumped minio-go to v7.4.1, Client.FGetObject upstream already closes the reader (and adds checksum verification), this fix is no longer needed.

@SebTardif

Copy link
Copy Markdown
Contributor Author

@stefanprodan

Since #2203 bumped minio-go to v7.4.1, Client.FGetObject upstream already closes the reader (and adds checksum verification), this fix is no longer needed.

Yes. Closing this pull.

@SebTardif SebTardif closed this Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants