Skip to content

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

Open
richardwang1124 wants to merge 4 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 4 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.put_object_multipart_large_allocated_kb - z-score regression: 11672.64 -> 11672.67. Z-score: Infinity

@jterapin jterapin left a comment

Copy link
Copy Markdown
Collaborator

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.

@@ -12,7 +12,7 @@ class DefaultExecutor
def initialize(options = {})
@max_threads = options[:max_threads] || DEFAULT_MAX_THREADS
@state = RUNNING
@queue = Queue.new
@queue = SizedQueue.new(@max_threads)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think its worthwhile to offer @max_queue as a separate option and pass in a reasonable default to :max_queue when creating an executor within the scope of the streaming uploader.

ensure_worker_available
end
@queue << [args, block]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You moved the enqueue out of the mutex to avoid holding the lock while parked but what happens shutdown or kill is called? There is also a gap between @state check and the push: what happens when shutdown/kill slips into that gap?

temp_io.rewind
if bytes_copied.zero?
if temp_io.is_a?(Tempfile)
if @tempfile

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For my own understanding: What's the reason for the split here? The tempfile branch needs copy_stream (streams to disk, no heap copy), but the old code used copy_stream for the in-memory path too...was there a reason to switch it to read?

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