Skip to content

Fix directoryDownload cancel leak#6875

Open
RanVaknin wants to merge 4 commits intomasterfrom
rvaknin/fix-directory-download-cancel-leak
Open

Fix directoryDownload cancel leak#6875
RanVaknin wants to merge 4 commits intomasterfrom
rvaknin/fix-directory-download-cancel-leak

Conversation

@RanVaknin
Copy link
Copy Markdown
Contributor

@RanVaknin RanVaknin commented Apr 17, 2026

When a DirectoryDownload (or DirectoryUpload) future is cancelled or completed exceptionally, AsyncBufferingSubscriber cancels in flight file transfer futures but does not cancel the upstream Subscription.

This means the listObjectsV2 paginator continues delivering objects, and new file downloads continue to be started after the user has cancelled the operation.

This change adds subscription.cancel() to the whenComplete handler so that cancellation stops the entire pipeline of both inflight transfers and new work from the upstream publisher.

Conforms to Reactive Streams spec:

6 A Subscriber MUST call Subscription.cancel() if the Subscription is no longer needed.

@RanVaknin RanVaknin requested a review from a team as a code owner April 17, 2026 18:26
subscriber.onNext("item");

verify(mockSubscription, times(1)).cancel();
verify(mockSubscription, times(2)).cancel();
Copy link
Copy Markdown
Contributor Author

@RanVaknin RanVaknin Apr 17, 2026

Choose a reason for hiding this comment

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

subscription.cancel() now exists in two codepaths:

  1. The existing codepath within onNext() catch block.
  2. The new addition in future.whenComplete()

Flow:

- onNext()
-- catch
-- subscription.cancel()
-- onError()

- onError()
-- completes exceptionally
-- future.whenCompleted()
-- subscription.cancel() // <-- new addition

I believe this is safe to call twice because:

5 Subscription.cancel MUST respect the responsivity of its caller by returning in a timely manner, MUST be idempotent and MUST be thread-safe.

Note: the codepath which the new addition solves is when the cancellation signal comes from outside of the pub/sub pipeline. If a user aborts the operation by cancelling the DirectoryDownload completion future directly, neither onNext nor onError are involved. The whenComplete handler is the only place that reacts to this external cancellation.

subscriber.onNext("item");

verify(mockSubscription, times(1)).cancel();
verify(mockSubscription, times(2)).cancel();
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.

Probably worth adding a quick comment here?

@sonarqubecloud
Copy link
Copy Markdown

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