diff --git a/sdk/src/Services/S3/Custom/Transfer/Internal/BufferedDownloadConfiguration.cs b/sdk/src/Services/S3/Custom/Transfer/Internal/BufferedDownloadConfiguration.cs index 43121706d940..867774da8810 100644 --- a/sdk/src/Services/S3/Custom/Transfer/Internal/BufferedDownloadConfiguration.cs +++ b/sdk/src/Services/S3/Custom/Transfer/Internal/BufferedDownloadConfiguration.cs @@ -30,9 +30,9 @@ namespace Amazon.S3.Transfer.Internal internal class BufferedDownloadConfiguration : DownloadManagerConfiguration { /// - /// Maximum parts to keep in memory simultaneously. + /// Maximum parts to keep in memory simultaneously. When null, defaults to 1024. /// - public int MaxInMemoryParts { get; set; } + public int? MaxInMemoryParts { get; set; } /// /// Buffer size for I/O operations. @@ -48,20 +48,20 @@ internal class BufferedDownloadConfiguration : DownloadManagerConfiguration /// Creates a BufferedDownloadConfiguration with the specified configuration values. /// /// Maximum concurrent HTTP requests for downloading parts. - /// Maximum number of parts to keep in memory simultaneously. + /// Maximum number of parts to keep in memory simultaneously. When null, defaults to 1024. /// Buffer size used for optimal I/O operations. /// Target size for each part in bytes. /// Optional chunk buffer size for ChunkedBufferStream. When null, defaults to 64KB. /// Thrown when any parameter is less than or equal to 0. public BufferedDownloadConfiguration( int concurrentServiceRequests, - int maxInMemoryParts, + int? maxInMemoryParts, int bufferSize, long targetPartSizeBytes, int? chunkBufferSize = null) : base(concurrentServiceRequests, targetPartSizeBytes) { - if (maxInMemoryParts <= 0) + if (maxInMemoryParts.HasValue && maxInMemoryParts.Value <= 0) throw new ArgumentOutOfRangeException(nameof(maxInMemoryParts), "Must be greater than 0"); if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize), "Must be greater than 0"); diff --git a/sdk/src/Services/S3/Custom/Transfer/Internal/PartBufferManager.cs b/sdk/src/Services/S3/Custom/Transfer/Internal/PartBufferManager.cs index a27622ea4377..b6b5cd455c36 100644 --- a/sdk/src/Services/S3/Custom/Transfer/Internal/PartBufferManager.cs +++ b/sdk/src/Services/S3/Custom/Transfer/Internal/PartBufferManager.cs @@ -189,14 +189,17 @@ public PartBufferManager(BufferedDownloadConfiguration config) if (config == null) throw new ArgumentNullException(nameof(config)); + // Resolve null MaxInMemoryParts to default value of 1024 + var maxInMemoryParts = config.MaxInMemoryParts ?? 1024; + _partDataSources = new ConcurrentDictionary(); _bufferSpaceAvailable = new SemaphoreSlim( - config.MaxInMemoryParts, // initialCount - config.MaxInMemoryParts // maxCount - prevents exceeding configured limit + maxInMemoryParts, // initialCount + maxInMemoryParts // maxCount - prevents exceeding configured limit ); _partAvailable = new AutoResetEvent(false); - _logger.DebugFormat("PartBufferManager initialized with MaxInMemoryParts={0}", config.MaxInMemoryParts); + _logger.DebugFormat("PartBufferManager initialized with MaxInMemoryParts={0}", maxInMemoryParts); } /// diff --git a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamRequest.cs b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamRequest.cs index a9ed2b468b8b..c36e8c79def5 100644 --- a/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamRequest.cs +++ b/sdk/src/Services/S3/Custom/Transfer/TransferUtilityOpenStreamRequest.cs @@ -32,22 +32,17 @@ namespace Amazon.S3.Transfer /// public class TransferUtilityOpenStreamRequest : BaseDownloadRequest { - private int _maxInMemoryParts = 1024; - /// /// Gets or sets the maximum number of parts to buffer in memory during multipart downloads. - /// The default value is 1024. + /// When null, defaults to 1024. /// /// /// This property controls memory usage during streaming downloads. When combined with the /// default part size of 8MB, the default value of 1024 parts allows up to 8GB of memory usage. /// Adjust this value based on your application's memory constraints and performance requirements. + /// Set to null to use the default value of 1024. /// - public int MaxInMemoryParts - { - get { return this._maxInMemoryParts; } - set { this._maxInMemoryParts = value; } - } + public int? MaxInMemoryParts { get; set; } /// /// Gets or sets the chunk buffer size (in bytes) used for buffering out-of-order parts during multipart downloads. diff --git a/sdk/test/Services/S3/UnitTests/Custom/BufferedDownloadConfigurationTests.cs b/sdk/test/Services/S3/UnitTests/Custom/BufferedDownloadConfigurationTests.cs index 3574aa24914c..50eba721ff7d 100644 --- a/sdk/test/Services/S3/UnitTests/Custom/BufferedDownloadConfigurationTests.cs +++ b/sdk/test/Services/S3/UnitTests/Custom/BufferedDownloadConfigurationTests.cs @@ -18,7 +18,7 @@ public void Constructor_WithValidParameters_CreatesConfiguration() { // Arrange int concurrentRequests = 10; - int maxInMemoryParts = 5; + int? maxInMemoryParts = 5; int bufferSize = 8192; long targetPartSize = 8 * 1024 * 1024; // 8MB @@ -38,7 +38,7 @@ public void Constructor_WithChunkBufferSize_SetsProperty() { // Arrange int concurrentRequests = 10; - int maxInMemoryParts = 5; + int? maxInMemoryParts = 5; int bufferSize = 8192; long targetPartSize = 8 * 1024 * 1024; // 8MB int chunkBufferSize = 32 * 1024; // 32KB @@ -59,7 +59,7 @@ public void Constructor_WithNullChunkBufferSize_LeavesPropertyNull() { // Arrange int concurrentRequests = 10; - int maxInMemoryParts = 5; + int? maxInMemoryParts = 5; int bufferSize = 8192; long targetPartSize = 8 * 1024 * 1024; // 8MB @@ -162,6 +162,22 @@ public void Constructor_WithZeroConcurrentRequests_ThrowsException() var config = new BufferedDownloadConfiguration(0, 5, 8192, 8 * 1024 * 1024); } + [TestMethod] + public void Constructor_WithNullMaxInMemoryParts_AcceptsValue() + { + // Arrange + int concurrentRequests = 10; + int? maxInMemoryParts = null; + int bufferSize = 8192; + long targetPartSize = 8 * 1024 * 1024; // 8MB + + // Act + var config = new BufferedDownloadConfiguration(concurrentRequests, maxInMemoryParts, bufferSize, targetPartSize); + + // Assert + Assert.IsNull(config.MaxInMemoryParts); + } + [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void Constructor_WithNegativeMaxInMemoryParts_ThrowsException()