Skip to content

FIX: Stream S3 job-file downloads instead of buffering into memory#125

Merged
prasadlohakpure merged 3 commits into
mainfrom
fix/s3-job-file-streaming
Jul 21, 2026
Merged

FIX: Stream S3 job-file downloads instead of buffering into memory#125
prasadlohakpure merged 3 commits into
mainfrom
fix/s3-job-file-streaming

Conversation

@prasadlohakpure

@prasadlohakpure prasadlohakpure commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • GET /api/v1/job/{id}/{file} buffered the entire S3-backed job log/result file into a []byte (aws.ReadFromS3) before writing it to the HTTP response. This was the Let's go #1 allocation site in production pprof profiling (31-54% of per-sample heap allocation), hit on every download of an S3-stored job artifact.
  • aws.ReadFromS3 now returns the object body as a streaming io.ReadCloser plus its content length, instead of fully buffering it into memory. Its one other caller, glue.GetTableMetadata, reads the body fully at the call site since it genuinely needs the bytes (a small JSON metadata file) -- no behavior change there.
  • getJobFile streams S3-backed files directly to the response via io.Copy, setting Content-Length from the object's metadata, and records (via telemetry) if the copy fails mid-stream after the 200 has already been written. The local-disk (os.ReadFile) path is unchanged.

This is slice 1 of a larger set of buffered-reading fixes found via pprof analysis. Remaining slices (Trino stream-decode, Avro stream read, pprof CPU-profile registration, payload size cap, Ranger client cleanup, StarRocks streaming) will follow separately.

Testing

  • go build ./..., go vet, and the full go test ./internal/... ./pkg/... suite pass on this branch.

Test (against archived job):

  • Job id: 00000066-5a19-4623-9115-759a8d5720d5
  • File /stdout
image

🤖 Generated with Claude Code

GET /api/v1/job/{id}/{file} buffered the entire S3 object into a []byte
via ReadFromS3 before writing it to the response. This was the #1
allocation site in production pprof profiling (31-54% of heap alloc),
hit on every download of a job log/result stored in S3. The handler now
streams the S3 body directly to the response via io.Copy, setting
Content-Length from the object's metadata.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 20, 2026 08:14
@wiz-55ccc8b716

wiz-55ccc8b716 Bot commented Jul 20, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Medium
Software Management Finding Software Management Findings -
Total 1 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

Comment thread internal/pkg/heimdall/job.go

Copilot AI 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.

Pull request overview

This PR reduces memory pressure in the job-file download endpoint by switching S3-backed artifact downloads from “read whole object into memory” to streaming the S3 response body directly to the HTTP client.

Changes:

  • Added aws.GetS3ObjectReader to return an S3 object io.ReadCloser plus content length (streaming API).
  • Updated getJobFile to stream S3-backed job files via io.Copy, setting Content-Length when available.
  • Kept the local-disk path (os.ReadFile) unchanged.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
internal/pkg/heimdall/job.go Streams S3-backed job artifacts directly to the HTTP response instead of buffering in memory.
internal/pkg/aws/s3.go Introduces a streaming S3 object reader helper returning body + content length.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/pkg/heimdall/job.go Outdated
prasadlohakpure and others added 2 commits July 20, 2026 13:51
Per Copilot review on #125: the 200 status is written before streaming
starts, so a mid-stream S3 read failure would previously go unnoticed
server-side while the client silently got a truncated body. Now logs
and counts the error via telemetry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Instead of two near-duplicate functions (buffered ReadFromS3 and
streaming GetS3ObjectReader), ReadFromS3 itself now returns a
streaming reader + content length. Its one remaining []byte caller,
glue.GetTableMetadata, reads the body fully at the call site since it
genuinely needs the bytes (a small JSON metadata file).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@prasadlohakpure prasadlohakpure changed the title Stream S3 job-file downloads instead of buffering into memory FIX: Stream S3 job-file downloads instead of buffering into memory Jul 20, 2026
@prasadlohakpure
prasadlohakpure merged commit e9c8636 into main Jul 21, 2026
7 checks passed
@prasadlohakpure
prasadlohakpure deleted the fix/s3-job-file-streaming branch July 21, 2026 06:28
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.

3 participants