Skip to content

fix: Address unbounded memory growth in upload_stream when source outpaces upload - #3407

Open
richardwang1124 wants to merge 6 commits into
version-3from
fix/upload_stream
Open

fix: Address unbounded memory growth in upload_stream when source outpaces upload#3407
richardwang1124 wants to merge 6 commits into
version-3from
fix/upload_stream

Conversation

@richardwang1124

Copy link
Copy Markdown
Contributor

Fixes #3393.


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

  1. To make sure we include your contribution in the release notes, please make sure to add description entry for your changes in the "unreleased changes" section of the CHANGELOG.md file (at corresponding gem). For the description entry, please make sure it lives in one line and starts with Feature or Issue in the correct format.

  2. For generated code changes, please checkout below instructions first:
    https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md

Thank you for your contribution!

@richardwang1124
richardwang1124 requested a review from a team as a code owner July 9, 2026 17:53
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Detected 1 possible performance regressions:

  • aws-sdk-s3.gem_size_kb - z-score regression: 588.5 -> 589.0. Z-score: Infinity

@jterapin jterapin 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 picking this up! this is a real gap and a good catch. I've left a few questions on the current implementation inline. We're also missing a CHANGELOG entry, so we'll want to add one before this merges.

Comment thread gems/aws-sdk-s3/lib/aws-sdk-s3/default_executor.rb Outdated
Comment thread gems/aws-sdk-s3/lib/aws-sdk-s3/default_executor.rb Outdated
Comment thread gems/aws-sdk-s3/lib/aws-sdk-s3/multipart_stream_uploader.rb

@jterapin jterapin 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.

The overall approach makes sense. I would like to see a fuller PR description for historical purposes (aside from the issue link).

@max_queue = options[:max_queue] || 0
@state = RUNNING
@queue = Queue.new
# A bounded queue applies backpressure to producers when full. 0 means unbounded.

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.

Could remove this comment. Seems self-explanatory.

thread_count = upload_opts.delete(:thread_count) || DefaultExecutor::DEFAULT_MAX_THREADS
# A bounded queue prevents the source from reading ahead without limit when it
# produces data faster than parts can be uploaded.
executor = DefaultExecutor.new(max_threads: thread_count, max_queue: thread_count)

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.

What's the rationale for keeping the queue size same as thread count?

if bytes_copied.zero?
if temp_io.is_a?(Tempfile)
if @tempfile
temp_io = Tempfile.new('aws-sdk-s3-upload_stream')

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.

This just occurred to me. Is it important to make the file name unique or no?

end
# Pushed outside the mutex because a bounded queue blocks the caller when
# full and holding the lock while parked would deadlock #shutdown and #kill.
@queue.push([args, block])

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.

I have some thoughts on this but i think offline discussion is better for this. Will DM you.

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.

Object#upload_stream retains unbounded memory when the source outpaces the upload

2 participants