-
Notifications
You must be signed in to change notification settings - Fork 874
Fix download directory concurrency issue and refactor #4180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
d19e194 to
1e922b6
Compare
| long _transferredBytes; | ||
| string _currentFile; | ||
|
|
||
| internal DownloadDirectoryCommand(IAmazonS3 s3Client, TransferUtilityDownloadDirectoryRequest request) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removing unused constructors
| } | ||
|
|
||
| if (File.Exists(this._request.S3Directory)) | ||
| if (File.Exists(this._request.LocalDirectory)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this was a bug. we were checking if s3 directory existed on machine and not local directory???
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need a dedicated dev config because of this line? @philasmar @normj ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are fixing an unrelated bug to the feature we should have a change log entry for it.
I'm curious if this should have been Directory.Exists instead of File.Exists. Also was the validation basically broken before and users have been successfully working when the directory already exists. This is fixing a bug but might be introducing a breaking change that might not be okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in that case ill remove this line change from this PR and we can update later if we want
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ive removed the change from this pr for now since i didnt feel like checking,testing more that scenario. will recheck and create a new PR if required
| /// Actual (broken): 5 concurrent downloads (all files download simultaneously) | ||
| /// </summary> | ||
| [TestMethod] | ||
| public async Task ExecuteAsync_ConcurrentServiceRequests_RespectsLimit() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these were failing before this fix
57a8525 to
d52eaf5
Compare
normj
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main point of the PR looks good. Not sure if it is safe to make the unrelated bug fix given it changes existing behavior.
| } | ||
|
|
||
| if (File.Exists(this._request.S3Directory)) | ||
| if (File.Exists(this._request.LocalDirectory)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are fixing an unrelated bug to the feature we should have a change log entry for it.
I'm curious if this should have been Directory.Exists instead of File.Exists. Also was the validation basically broken before and users have been successfully working when the directory already exists. This is fixing a bug but might be introducing a breaking change that might not be okay.
normj
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approve but we should continue the discussion about what to do with the bug in a separate thread/pr.
There was a problem hiding this comment.
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 fixes a critical concurrency control bug in the S3 Transfer Utility's DownloadDirectoryCommand where the semaphore was being released immediately after acquiring it, causing all files to download simultaneously instead of respecting the configured concurrency limits. The fix refactors the download logic to use a task pool pattern that properly manages concurrency throughout the download lifecycle.
Key Changes:
- Fixed semaphore release timing to occur after download completion rather than immediately after acquiring
- Refactored download logic into smaller, focused methods for better readability and maintainability
- Introduced
ForEachWithConcurrencyAsynchelper to properly implement the task pool pattern
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/src/Services/S3/Custom/Transfer/Internal/_bcl+netstandard/DownloadDirectoryCommand.cs | Refactored ExecuteAsync into smaller methods using task pool pattern; added logging; fixed concurrency control |
| sdk/src/Services/S3/Custom/Transfer/Internal/DownloadDirectoryCommand.cs | Consolidated constructors to require TransferUtilityConfig parameter |
| sdk/src/Services/S3/Custom/Transfer/Internal/TaskHelpers.cs | Added ForEachWithConcurrencyAsync method for task pool pattern implementation; added debug logging |
| sdk/test/Services/S3/UnitTests/Custom/DownloadDirectoryCommandTests.cs | Updated test constructor calls to include config parameter; added concurrency control tests |
| sdk/test/Services/S3/UnitTests/Custom/FailurePolicyTests.cs | Added comprehensive tests for path validation, sequential mode, and failure handling scenarios |
|
|
||
| public bool DownloadFilesConcurrently { get; set; } | ||
|
|
||
| private Logger Logger | ||
| { | ||
| get { return Logger.GetLogger(typeof(DownloadDirectoryCommand)); } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recursive property definition: the Logger property calls Logger.GetLogger() which references itself. This should be _logger field or use a different pattern.
| public bool DownloadFilesConcurrently { get; set; } | |
| private Logger Logger | |
| { | |
| get { return Logger.GetLogger(typeof(DownloadDirectoryCommand)); } | |
| private readonly Logger _logger = Logger.GetLogger(typeof(DownloadDirectoryCommand)); | |
| public bool DownloadFilesConcurrently { get; set; } | |
| private Logger Logger | |
| { | |
| get { return _logger; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i will fix this before merging
| private static Logger Logger | ||
| { | ||
| get { return Logger.GetLogger(typeof(TaskHelpers)); } | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recursive property definition: the Logger property calls Logger.GetLogger() which references itself. This should use a static field or a different pattern.
0c0d54d to
b1c9123
Compare
Description
Fixes the issue where in the previous code this happened
this was accidentally introduced in #4151
I also refactored download directory to be easier to read
Motivation and Context
#3806
Testing
Types of changes
Checklist
License