From 30a8349627fb50133529c3fac2bdb0176a1b8744 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Thu, 23 Jul 2026 13:15:30 -0700 Subject: [PATCH 1/9] cleanup --- sdk/parents/azure-client-sdk-parent/pom.xml | 1 - .../com/azure/storage/blob/specialized/PageBlobClient.java | 3 --- 2 files changed, 4 deletions(-) diff --git a/sdk/parents/azure-client-sdk-parent/pom.xml b/sdk/parents/azure-client-sdk-parent/pom.xml index cc3e408703db..ae363a524be1 100644 --- a/sdk/parents/azure-client-sdk-parent/pom.xml +++ b/sdk/parents/azure-client-sdk-parent/pom.xml @@ -602,7 +602,6 @@ com.google.code.findbugs:jsr305:[3.0.2] - diff --git a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/PageBlobClient.java b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/PageBlobClient.java index 898355cb3e39..23e520ed956e 100644 --- a/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/PageBlobClient.java +++ b/sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/PageBlobClient.java @@ -189,7 +189,6 @@ public PageBlobClient getCustomerProvidedKeyClient(CustomerProvidedKey customerP * @return A {@link BlobOutputStream} object used to write data to the blob. * @throws BlobStorageException If a storage service error occurred. */ - @ServiceMethod(returns = ReturnType.SINGLE) public BlobOutputStream getBlobOutputStream(PageRange pageRange) { return getBlobOutputStream(pageRange, null); } @@ -206,7 +205,6 @@ public BlobOutputStream getBlobOutputStream(PageRange pageRange) { * @return A {@link BlobOutputStream} object used to write data to the blob. * @throws BlobStorageException If a storage service error occurred. */ - @ServiceMethod(returns = ReturnType.SINGLE) public BlobOutputStream getBlobOutputStream(PageRange pageRange, BlobRequestConditions requestConditions) { return BlobOutputStream.pageBlobOutputStream(pageBlobAsyncClient, pageRange, requestConditions); } @@ -218,7 +216,6 @@ public BlobOutputStream getBlobOutputStream(PageRange pageRange, BlobRequestCond * @return A {@link BlobOutputStream} object used to write data to the blob. * @throws BlobStorageException If a storage service error occurred. */ - @ServiceMethod(returns = ReturnType.SINGLE) public BlobOutputStream getBlobOutputStream(PageBlobOutputStreamOptions options) { if (options == null) { throw LOGGER.logExceptionAsError(new NullPointerException("'options' cannot be null.")); From b9b1455e74132a628307f6af60284de19128f339 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Thu, 23 Jul 2026 13:36:45 -0700 Subject: [PATCH 2/9] reducing size of tests where large payloads are not necessary --- .../com/azure/storage/blob/RetryTests.java | 2 ++ .../BufferStagingAreaTests.java | 6 ++-- .../MessageEncoderTests.java | 28 ++++++++++++++----- .../StorageCrc64CalculatorTests.java | 5 +++- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/RetryTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/RetryTests.java index 44ef32fad304..b94b8f79daae 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/RetryTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/RetryTests.java @@ -9,6 +9,7 @@ import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.RetryPolicyType; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -26,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +@Isolated public class RetryTests extends BlobTestBase { private static URL retryTestURL; diff --git a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/BufferStagingAreaTests.java b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/BufferStagingAreaTests.java index 66cac7dfeaf7..77384c6630ce 100644 --- a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/BufferStagingAreaTests.java +++ b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/BufferStagingAreaTests.java @@ -105,8 +105,10 @@ private static Stream bufferStagingAreaSupplier() { Arguments.of(1000, 1000, 1000), // _ Arguments.of(10000, 1000, 1000), // _ Arguments.of(10000, 1, 1000), // These test variation in buffSize. - Arguments.of(100, 1, Constants.MB * 4), // _ - Arguments.of(100, Constants.MB * 4, Constants.MB * 8) // _ + // Exercise repartitioning of many variably-sized buffers by maxBuffSize. The buffer count and size + // variation are what matter here, not the absolute MB size; sizes were reduced from 4-8 MiB. + Arguments.of(100, 1, Constants.KB * 256), // _ + Arguments.of(100, Constants.KB * 256, Constants.KB * 512) // _ ); } diff --git a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java index 899a2b5f5425..0f515ff4be31 100644 --- a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java +++ b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java @@ -137,18 +137,18 @@ private static Stream readAllSupplier() { Arguments.of(1024 * 1024, 512, StructuredMessageFlags.STORAGE_CRC64), Arguments.of(1024 * 1024, 1024, StructuredMessageFlags.NONE), Arguments.of(1024 * 1024, 1024, StructuredMessageFlags.STORAGE_CRC64), + // A single multi-MB case with >= 1 MiB segments is sufficient to cover large content and large + // segment sizes. Larger 8 MiB / 12 MiB variants were removed as they add no structural coverage + // (same handful of segments) while multiplying the test's heap footprint. Arguments.of(1024 * 1024 * 4, 1024 * 1024, StructuredMessageFlags.NONE), Arguments.of(1024 * 1024 * 4, 1024 * 1024, StructuredMessageFlags.STORAGE_CRC64), - Arguments.of(1024 * 1024 * 8, 1024 * 1024, StructuredMessageFlags.NONE), - Arguments.of(1024 * 1024 * 8, 1024 * 1024, StructuredMessageFlags.STORAGE_CRC64), Arguments.of(1234, 123, StructuredMessageFlags.NONE), Arguments.of(1234, 123, StructuredMessageFlags.STORAGE_CRC64), Arguments.of(1234 * 10, 12, StructuredMessageFlags.NONE), Arguments.of(1234 * 10, 12, StructuredMessageFlags.STORAGE_CRC64), + // Non-power-of-two content and segment sizes to exercise uneven segment boundaries. Arguments.of(1234 * 1234, 567, StructuredMessageFlags.NONE), - Arguments.of(1234 * 1234, 567, StructuredMessageFlags.STORAGE_CRC64), - Arguments.of(1234 * 1234 * 8, 1234 * 1234, StructuredMessageFlags.NONE), - Arguments.of(1234 * 1234 * 8, 1234 * 1234, StructuredMessageFlags.STORAGE_CRC64)); + Arguments.of(1234 * 1234, 567, StructuredMessageFlags.STORAGE_CRC64)); } @ParameterizedTest @@ -263,7 +263,6 @@ private static Stream encodedLengthSupplier() { Arguments.of(1024, 512, StructuredMessageFlags.NONE), Arguments.of(1024, 512, StructuredMessageFlags.STORAGE_CRC64), Arguments.of(4 * 1024 * 1024, V1_DEFAULT_SEGMENT_CONTENT_LENGTH, StructuredMessageFlags.STORAGE_CRC64), - Arguments.of(10 * 1024 * 1024, V1_DEFAULT_SEGMENT_CONTENT_LENGTH, StructuredMessageFlags.STORAGE_CRC64), Arguments.of(1234, 123, StructuredMessageFlags.STORAGE_CRC64)); } @@ -283,8 +282,23 @@ public void encodedLengthMatchesActualOutput(int size, int segmentSize, Structur // Direct (non-array-backed) ByteBuffer // =========================================================================================== + private static Stream readAllDirectSupplier() { + // Direct (non-array-backed) buffers take the scratch-buffer CRC path + // (StructuredMessageEncoder.CRC64_SCRATCH_BUFFER_SIZE == 64 KiB). These cases only need to cover + // that path: empty/uneven segment boundaries plus content larger than the scratch buffer (to force + // multiple scratch iterations). They intentionally stay small so the direct-buffer test doesn't + // re-run the large readAllSupplier cases and double their heap footprint. + return Stream.of(Arguments.of(10, 1, StructuredMessageFlags.NONE), + Arguments.of(10, 1, StructuredMessageFlags.STORAGE_CRC64), + Arguments.of(1024, 200, StructuredMessageFlags.NONE), + Arguments.of(1024, 200, StructuredMessageFlags.STORAGE_CRC64), + Arguments.of(1024 * 128, 512, StructuredMessageFlags.NONE), + Arguments.of(1024 * 128, 512, StructuredMessageFlags.STORAGE_CRC64), + Arguments.of(1024 * 128, 1024 * 128, StructuredMessageFlags.STORAGE_CRC64)); + } + @ParameterizedTest - @MethodSource("readAllSupplier") + @MethodSource("readAllDirectSupplier") public void readAllDirectByteBuffer(int size, int segmentSize, StructuredMessageFlags flags) throws IOException { byte[] data = getRandomData(size); ByteBuffer directBuffer = ByteBuffer.allocateDirect(size); diff --git a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StorageCrc64CalculatorTests.java b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StorageCrc64CalculatorTests.java index a07cef1f2013..a4443ccb9aa1 100644 --- a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StorageCrc64CalculatorTests.java +++ b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StorageCrc64CalculatorTests.java @@ -112,7 +112,10 @@ private static Stream testComposeSupplier() { Arguments.of(10, Constants.KB, Constants.KB), Arguments.of(2, Constants.KB, 4 * Constants.KB), Arguments.of(3, Constants.KB, 4 * Constants.KB), Arguments.of(10, Constants.KB, 4 * Constants.KB), Arguments.of(2, Constants.KB, Constants.MB), Arguments.of(3, Constants.KB, Constants.MB), - Arguments.of(2, Constants.KB, 512 * Constants.MB), Arguments.of(3, Constants.KB, 512 * Constants.MB)); + // Cover large, non-uniform block sizes to exercise concat-composition without allocating + // multi-hundred-MB buffers (previously 512 MB, which intermittently OOMed the 4 GB fork and + // triggered a multi-GB heap dump that filled the CI agent disk). + Arguments.of(2, Constants.KB, 4 * Constants.MB), Arguments.of(3, Constants.KB, 4 * Constants.MB)); } @Test From edf44f0b80bb59f94fa9be908033fd8dcd56303d Mon Sep 17 00:00:00 2001 From: Isabelle Date: Thu, 23 Jul 2026 13:37:22 -0700 Subject: [PATCH 3/9] renaming messagencodertests to structuredmessageencodertests --- ...sageEncoderTests.java => StructuredMessageEncoderTests.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/{MessageEncoderTests.java => StructuredMessageEncoderTests.java} (99%) diff --git a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StructuredMessageEncoderTests.java similarity index 99% rename from sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java rename to sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StructuredMessageEncoderTests.java index 0f515ff4be31..f04a9909b57f 100644 --- a/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/MessageEncoderTests.java +++ b/sdk/storage/azure-storage-common/src/test/java/com/azure/storage/common/implementation/contentvalidation/StructuredMessageEncoderTests.java @@ -29,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertNotNull; -public class MessageEncoderTests { +public class StructuredMessageEncoderTests { private static byte[] getRandomData(int size) { byte[] result = new byte[size]; From c373f8c8bf8d94f5d200fc5b7d550e0debc0ca29 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Thu, 23 Jul 2026 15:03:57 -0700 Subject: [PATCH 4/9] reducing flakyness of append content val test and abort copy tests --- .../com/azure/storage/blob/BlobApiTests.java | 27 ++++++++++++++--- ...BlobContentValidationAsyncUploadTests.java | 30 +++++++++++++------ 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java index 50a9eb63ef21..e42bf74e34e7 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java @@ -129,6 +129,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class BlobApiTests extends BlobTestBase { private BlobClient bc; @@ -2153,12 +2154,19 @@ public void copyDestACFail(OffsetDateTime modified, OffsetDateTime unmodified, S () -> bu2.copyFromUrlWithResponse(bc.getBlobUrl(), null, null, null, bac, null, null)); } + // Abort-copy tests race the service: the cross-account copy must still be pending when the abort request + // lands. A larger source widens that window (A1); combined with an assumeTrue guard on the poll status (B), + // the tests assert abort behavior when a copy is genuinely pending and skip (rather than fail) if the + // service finished the copy first. Server-to-server copy, so the larger size costs only the initial upload. + private static final int ABORT_COPY_SOURCE_SIZE_BYTES = 64 * Constants.MB; + @Test public void abortCopyBaseSimple() { // Data has to be large enough and copied between accounts to give us enough time to abort new SpecializedBlobClientBuilder().blobClient(bc) .buildBlockBlobClient() - .upload(new ByteArrayInputStream(getRandomByteArray(8 * 1024 * 1024)), 8 * 1024 * 1024, true); + .upload(new ByteArrayInputStream(getRandomByteArray(ABORT_COPY_SOURCE_SIZE_BYTES)), + ABORT_COPY_SOURCE_SIZE_BYTES, true); BlobContainerClient cu2 = alternateBlobServiceClient.getBlobContainerClient(generateBlobName()); cu2.create(); @@ -2171,6 +2179,8 @@ public void abortCopyBaseSimple() { PollResponse lastResponse = poller.poll(); assertNotNull(lastResponse); assertNotNull(lastResponse.getValue()); + assumeTrue(lastResponse.getStatus() == LongRunningOperationStatus.IN_PROGRESS, + "Copy already completed before abort could be issued; nothing pending to abort."); bu2.abortCopyFromUrl(lastResponse.getValue().getCopyId()); BlobStorageException e = assertThrows(BlobStorageException.class, () -> bu2.abortCopyFromUrl(lastResponse.getValue().getCopyId())); @@ -2186,7 +2196,8 @@ public void abortCopyLeaseFail() { // Data has to be large enough and copied between accounts to give us enough time to abort new SpecializedBlobClientBuilder().blobClient(bc) .buildBlockBlobClient() - .upload(new ByteArrayInputStream(getRandomByteArray(8 * 1024 * 1024)), 8 * 1024 * 1024, true); + .upload(new ByteArrayInputStream(getRandomByteArray(ABORT_COPY_SOURCE_SIZE_BYTES)), + ABORT_COPY_SOURCE_SIZE_BYTES, true); BlobContainerClient cu2 = alternateBlobServiceClient.getBlobContainerClient(generateBlobName()); cu2.create(); @@ -2202,6 +2213,8 @@ public void abortCopyLeaseFail() { bu2.beginCopy(bc.getBlobUrl() + "?" + sas, null, null, null, null, blobRequestConditions, null)); PollResponse response = poller.poll(); assertNotEquals(LongRunningOperationStatus.FAILED, response.getStatus()); + assumeTrue(response.getStatus() == LongRunningOperationStatus.IN_PROGRESS, + "Copy already completed before abort could be issued; cannot observe the 412 lease mismatch."); BlobCopyInfo blobCopyInfo = response.getValue(); BlobStorageException e = assertThrows(BlobStorageException.class, @@ -2217,7 +2230,8 @@ public void abortCopy() { // Data has to be large enough and copied between accounts to give us enough time to abort new SpecializedBlobClientBuilder().blobClient(bc) .buildBlockBlobClient() - .upload(new ByteArrayInputStream(getRandomByteArray(8 * 1024 * 1024)), 8 * 1024 * 1024, true); + .upload(new ByteArrayInputStream(getRandomByteArray(ABORT_COPY_SOURCE_SIZE_BYTES)), + ABORT_COPY_SOURCE_SIZE_BYTES, true); BlobContainerClient cu2 = alternateBlobServiceClient.getBlobContainerClient(generateBlobName()); cu2.create(); @@ -2230,6 +2244,8 @@ public void abortCopy() { PollResponse lastResponse = poller.poll(); assertNotNull(lastResponse); assertNotNull(lastResponse.getValue()); + assumeTrue(lastResponse.getStatus() == LongRunningOperationStatus.IN_PROGRESS, + "Copy already completed before abort could be issued; nothing pending to abort."); Response response = bu2.abortCopyFromUrlWithResponse(lastResponse.getValue().getCopyId(), null, null, null); HttpHeaders headers = response.getHeaders(); @@ -2247,7 +2263,8 @@ public void abortCopyLease() { // Data has to be large enough and copied between accounts to give us enough time to abort new SpecializedBlobClientBuilder().blobClient(bc) .buildBlockBlobClient() - .upload(new ByteArrayInputStream(getRandomByteArray(8 * 1024 * 1024)), 8 * 1024 * 1024, true); + .upload(new ByteArrayInputStream(getRandomByteArray(ABORT_COPY_SOURCE_SIZE_BYTES)), + ABORT_COPY_SOURCE_SIZE_BYTES, true); BlobContainerClient cu2 = alternateBlobServiceClient.getBlobContainerClient(generateContainerName()); cu2.create(); @@ -2264,6 +2281,8 @@ public void abortCopyLease() { assertNotNull(lastResponse); assertNotNull(lastResponse.getValue()); + assumeTrue(lastResponse.getStatus() == LongRunningOperationStatus.IN_PROGRESS, + "Copy already completed before abort could be issued; nothing pending to abort."); String copyId = lastResponse.getValue().getCopyId(); assertResponseStatusCode(bu2.abortCopyFromUrlWithResponse(copyId, leaseId, null, null), 204); // cleanup: diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java index 3c0e10e607e3..16d576e3d66b 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java @@ -839,15 +839,27 @@ public void appendBlockLiveRandomRoundTripDataIntegrity() throws Exception { outFile.deleteOnExit(); try { - try (AsynchronousFileChannel channel - = AsynchronousFileChannel.open(sourceFile.toPath(), StandardOpenOption.READ)) { - FluxUtil.readFile(channel, maxAppendBlockBytes, 0, chosenPayloadSizeBytes).concatMap(bb -> { - AppendBlobAppendBlockOptions appendOptions = new AppendBlobAppendBlockOptions() - .setContentValidationAlgorithm(ContentValidationAlgorithm.CRC64); - return client.appendBlockWithResponse(Flux.just(bb), bb.remaining(), appendOptions); - }).then().block(); - } - StepVerifier.create(client.create().then(blobClient.downloadToFile(outFile.getPath(), true))) + // Append blobs must be created before any block can be appended; otherwise the first + // appendBlock call fails with 404 BlobNotFound. Create, append every block, then download, + // all chained into a single reactive pipeline (no blocking calls in the test). + StepVerifier + .create(client.create() + .thenMany(Flux.using( + () -> AsynchronousFileChannel.open(sourceFile.toPath(), StandardOpenOption.READ), + channel -> FluxUtil.readFile(channel, maxAppendBlockBytes, 0, chosenPayloadSizeBytes) + .concatMap(bb -> { + AppendBlobAppendBlockOptions appendOptions = new AppendBlobAppendBlockOptions() + .setContentValidationAlgorithm(ContentValidationAlgorithm.CRC64); + return client.appendBlockWithResponse(Flux.just(bb), bb.remaining(), appendOptions); + }), + channel -> { + try { + channel.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + })) + .then(blobClient.downloadToFile(outFile.getPath(), true))) .assertNext(Assertions::assertNotNull) .verifyComplete(); assertTrue(compareFiles(sourceFile, outFile, 0, chosenPayloadSizeBytes), prefix); From 51a1ef6bdc671564fd03ed0a70d5b7324fb2f7b8 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Thu, 23 Jul 2026 16:52:23 -0700 Subject: [PATCH 5/9] cleaning up test files eagerly --- ...obContentValidationAsyncDownloadTests.java | 75 ++++++++++++------- .../BlobContentValidationDownloadTests.java | 65 ++++++++++------ .../azure/storage/blob/LargeBlobTests.java | 26 ++++--- .../storage/file/datalake/LargeFileTests.java | 20 +++-- 4 files changed, 118 insertions(+), 68 deletions(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncDownloadTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncDownloadTests.java index ae5edcfba35e..5c3cf7816c9d 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncDownloadTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncDownloadTests.java @@ -624,47 +624,54 @@ private void assertParallelDownloadFuzzyRoundTripAsync(String caseKind, int payl if (payloadBytes >= FUZZY_PARALLEL_DOWNLOAD_FILE_ROUND_TRIP_THRESHOLD_BYTES) { File sourceFile = getRandomFile(payloadBytes); - sourceFile.deleteOnExit(); File outFile = Files.createTempFile("blob-cv-fuzzy-parallel-dl-async", ".bin").toFile(); - outFile.deleteOnExit(); Files.deleteIfExists(outFile.toPath()); - - BlobUploadFromFileOptions uploadOptions - = new BlobUploadFromFileOptions(sourceFile.getAbsolutePath()).setParallelTransferOptions( - new com.azure.storage.blob.models.ParallelTransferOptions().setBlockSizeLong(blockSizeBytes) - .setMaxConcurrency(maxConcurrency)); - - BlobDownloadToFileOptions downloadOptions - = new BlobDownloadToFileOptions(outFile.toPath().toString()).setParallelTransferOptions(parallelOptions) - .setContentValidationAlgorithm(algorithm); - - StepVerifier - .create(client.uploadFromFileWithResponse(uploadOptions) - .then(client.downloadToFileWithResponse(downloadOptions))) - .assertNext(r -> assertNotNull(r.getValue(), assertionMessage)) - .verifyComplete(); - - assertTrue(compareFiles(sourceFile, outFile, 0, payloadBytes), assertionMessage); - } else { - byte[] randomData = getRandomByteArray(payloadBytes); - - if (payloadBytes > blockSizeBytes) { - File outFile = Files.createTempFile("blob-cv-fuzzy-parallel-dl-async-mp", ".bin").toFile(); - outFile.deleteOnExit(); - Files.deleteIfExists(outFile.toPath()); + try { + BlobUploadFromFileOptions uploadOptions + = new BlobUploadFromFileOptions(sourceFile.getAbsolutePath()).setParallelTransferOptions( + new com.azure.storage.blob.models.ParallelTransferOptions().setBlockSizeLong(blockSizeBytes) + .setMaxConcurrency(maxConcurrency)); BlobDownloadToFileOptions downloadOptions = new BlobDownloadToFileOptions(outFile.toPath().toString()) .setParallelTransferOptions(parallelOptions) .setContentValidationAlgorithm(algorithm); StepVerifier - .create(client.upload(BinaryData.fromBytes(randomData), true) + .create(client.uploadFromFileWithResponse(uploadOptions) .then(client.downloadToFileWithResponse(downloadOptions))) .assertNext(r -> assertNotNull(r.getValue(), assertionMessage)) .verifyComplete(); - byte[] downloaded = Files.readAllBytes(outFile.toPath()); - assertArrayEquals(randomData, downloaded, assertionMessage); + assertTrue(compareFiles(sourceFile, outFile, 0, payloadBytes), assertionMessage); + } finally { + // Delete eagerly (not deleteOnExit) so large fuzzy payloads don't accumulate for the whole JVM and + // exhaust agent disk space when many cases run in the same live-test process. + deleteFileQuietly(sourceFile); + deleteFileQuietly(outFile); + } + } else { + byte[] randomData = getRandomByteArray(payloadBytes); + + if (payloadBytes > blockSizeBytes) { + File outFile = Files.createTempFile("blob-cv-fuzzy-parallel-dl-async-mp", ".bin").toFile(); + Files.deleteIfExists(outFile.toPath()); + try { + BlobDownloadToFileOptions downloadOptions + = new BlobDownloadToFileOptions(outFile.toPath().toString()) + .setParallelTransferOptions(parallelOptions) + .setContentValidationAlgorithm(algorithm); + + StepVerifier + .create(client.upload(BinaryData.fromBytes(randomData), true) + .then(client.downloadToFileWithResponse(downloadOptions))) + .assertNext(r -> assertNotNull(r.getValue(), assertionMessage)) + .verifyComplete(); + + byte[] downloaded = Files.readAllBytes(outFile.toPath()); + assertArrayEquals(randomData, downloaded, assertionMessage); + } finally { + deleteFileQuietly(outFile); + } } else { BlobDownloadContentOptions downloadOptions = new BlobDownloadContentOptions().setContentValidationAlgorithm(algorithm); @@ -687,4 +694,14 @@ private void assertParallelDownloadFuzzyRoundTripAsync(String caseKind, int payl assertTrue(hasStructuredMessageDownloadRequestHeaders(recorded, false), assertionMessage); } + /** + * Deletes a temp file eagerly, falling back to {@link File#deleteOnExit()} only if the immediate delete fails. + * Prevents large fuzzy round-trip payloads from accumulating on disk across the many cases that share a JVM. + */ + private static void deleteFileQuietly(File file) { + if (file != null && file.exists() && !file.delete()) { + file.deleteOnExit(); + } + } + } diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationDownloadTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationDownloadTests.java index dc6b1f2e1208..20d01d4185d1 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationDownloadTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationDownloadTests.java @@ -589,42 +589,49 @@ private void assertParallelDownloadFuzzyRoundTrip(String caseKind, int payloadBy if (payloadBytes >= FUZZY_PARALLEL_DOWNLOAD_FILE_ROUND_TRIP_THRESHOLD_BYTES) { File sourceFile = getRandomFile(payloadBytes); - sourceFile.deleteOnExit(); File outFile = Files.createTempFile("blob-cv-fuzzy-parallel-dl", ".bin").toFile(); - outFile.deleteOnExit(); Files.deleteIfExists(outFile.toPath()); + try { + BlobUploadFromFileOptions uploadOptions + = new BlobUploadFromFileOptions(sourceFile.getAbsolutePath()).setParallelTransferOptions( + new com.azure.storage.blob.models.ParallelTransferOptions().setBlockSizeLong(blockSizeBytes) + .setMaxConcurrency(maxConcurrency)); + assertNotNull(client.uploadFromFileWithResponse(uploadOptions, null, Context.NONE).getValue().getETag(), + assertionMessage); - BlobUploadFromFileOptions uploadOptions - = new BlobUploadFromFileOptions(sourceFile.getAbsolutePath()).setParallelTransferOptions( - new com.azure.storage.blob.models.ParallelTransferOptions().setBlockSizeLong(blockSizeBytes) - .setMaxConcurrency(maxConcurrency)); - assertNotNull(client.uploadFromFileWithResponse(uploadOptions, null, Context.NONE).getValue().getETag(), - assertionMessage); - - BlobDownloadToFileOptions downloadOptions - = new BlobDownloadToFileOptions(outFile.toPath().toString()).setParallelTransferOptions(parallelOptions) + BlobDownloadToFileOptions downloadOptions = new BlobDownloadToFileOptions(outFile.toPath().toString()) + .setParallelTransferOptions(parallelOptions) .setContentValidationAlgorithm(algorithm); - assertNotNull(client.downloadToFileWithResponse(downloadOptions, null, Context.NONE).getValue(), - assertionMessage); + assertNotNull(client.downloadToFileWithResponse(downloadOptions, null, Context.NONE).getValue(), + assertionMessage); - assertTrue(compareFiles(sourceFile, outFile, 0, payloadBytes), assertionMessage); + assertTrue(compareFiles(sourceFile, outFile, 0, payloadBytes), assertionMessage); + } finally { + // Delete eagerly (not deleteOnExit) so large fuzzy payloads don't accumulate for the whole JVM and + // exhaust agent disk space when many cases run in the same live-test process. + deleteFileQuietly(sourceFile); + deleteFileQuietly(outFile); + } } else { byte[] randomData = getRandomByteArray(payloadBytes); client.upload(BinaryData.fromBytes(randomData), true); if (payloadBytes > blockSizeBytes) { File outFile = Files.createTempFile("blob-cv-fuzzy-parallel-dl-mp", ".bin").toFile(); - outFile.deleteOnExit(); Files.deleteIfExists(outFile.toPath()); - - BlobDownloadToFileOptions downloadOptions = new BlobDownloadToFileOptions(outFile.toPath().toString()) - .setParallelTransferOptions(parallelOptions) - .setContentValidationAlgorithm(algorithm); - assertNotNull(client.downloadToFileWithResponse(downloadOptions, null, Context.NONE).getValue(), - assertionMessage); - - byte[] downloaded = readAllBytesFromFile(outFile); - assertArrayEquals(randomData, downloaded, assertionMessage); + try { + BlobDownloadToFileOptions downloadOptions + = new BlobDownloadToFileOptions(outFile.toPath().toString()) + .setParallelTransferOptions(parallelOptions) + .setContentValidationAlgorithm(algorithm); + assertNotNull(client.downloadToFileWithResponse(downloadOptions, null, Context.NONE).getValue(), + assertionMessage); + + byte[] downloaded = readAllBytesFromFile(outFile); + assertArrayEquals(randomData, downloaded, assertionMessage); + } finally { + deleteFileQuietly(outFile); + } } else { BlobDownloadContentOptions downloadOptions = new BlobDownloadContentOptions().setContentValidationAlgorithm(algorithm); @@ -648,6 +655,16 @@ private static byte[] readAllBytesFromFile(File file) throws IOException { } } + /** + * Deletes a temp file eagerly, falling back to {@link File#deleteOnExit()} only if the immediate delete fails. + * Prevents large fuzzy round-trip payloads from accumulating on disk across the many cases that share a JVM. + */ + private static void deleteFileQuietly(File file) { + if (file != null && file.exists() && !file.delete()) { + file.deleteOnExit(); + } + } + static Stream channelReadDataSupplier() { return Stream.of(Arguments.of(50, 40, Constants.KB), Arguments.of(Constants.KB + 50, 40, Constants.KB), Arguments.of(null, Constants.MB, FIVE_MB)); diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/LargeBlobTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/LargeBlobTests.java index 37feec1a6b68..82a776ef38c3 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/LargeBlobTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/LargeBlobTests.java @@ -171,15 +171,23 @@ public void uploadLargeInputSyncNoLengthGiven() { public void uploadLargeFile() throws IOException { long tailSize = Constants.MB; File file = getRandomLargeFile(LARGE_BLOCK_SIZE + tailSize); - ParallelTransferOptions parallelTransferOptions - = new ParallelTransferOptions().setBlockSizeLong(LARGE_BLOCK_SIZE); - - blobClient.uploadFromFile(file.toPath().toString(), parallelTransferOptions, null, null, null, null, null); - BlockList blockList = blobClient.getBlockBlobClient().listBlocks(BlockListType.COMMITTED); - - assertEquals(2, blockList.getCommittedBlocks().size()); - assertEquals(LARGE_BLOCK_SIZE, blockList.getCommittedBlocks().get(0).getSizeLong()); - assertEquals(tailSize, blockList.getCommittedBlocks().get(1).getSizeLong()); + try { + ParallelTransferOptions parallelTransferOptions + = new ParallelTransferOptions().setBlockSizeLong(LARGE_BLOCK_SIZE); + + blobClient.uploadFromFile(file.toPath().toString(), parallelTransferOptions, null, null, null, null, null); + BlockList blockList = blobClient.getBlockBlobClient().listBlocks(BlockListType.COMMITTED); + + assertEquals(2, blockList.getCommittedBlocks().size()); + assertEquals(LARGE_BLOCK_SIZE, blockList.getCommittedBlocks().get(0).getSizeLong()); + assertEquals(tailSize, blockList.getCommittedBlocks().get(1).getSizeLong()); + } finally { + // Delete the ~2.5 GiB source eagerly instead of waiting for deleteOnExit() so it doesn't occupy agent + // disk for the remainder of the module's test JVM. + if (!file.delete()) { + file.deleteOnExit(); + } + } } private InputStream createLargeInputStream(long size) { diff --git a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/LargeFileTests.java b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/LargeFileTests.java index 5e244cedd267..4d45d76dabcf 100644 --- a/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/LargeFileTests.java +++ b/sdk/storage/azure-storage-file-datalake/src/test/java/com/azure/storage/file/datalake/LargeFileTests.java @@ -114,12 +114,20 @@ public void uploadLargeFile() { long tail = Constants.MB; File file = getLargeRandomFile(LARGE_BLOCK_SIZE + tail); - fc.uploadFromFile(file.toPath().toString(), new ParallelTransferOptions().setBlockSizeLong(LARGE_BLOCK_SIZE), - null, null, null, null); - - assertEquals(2, countingPolicy.count.get()); - assertTrue(countingPolicy.appendPayloadSizes.contains(LARGE_BLOCK_SIZE)); - assertTrue(countingPolicy.appendPayloadSizes.contains(tail)); + try { + fc.uploadFromFile(file.toPath().toString(), + new ParallelTransferOptions().setBlockSizeLong(LARGE_BLOCK_SIZE), null, null, null, null); + + assertEquals(2, countingPolicy.count.get()); + assertTrue(countingPolicy.appendPayloadSizes.contains(LARGE_BLOCK_SIZE)); + assertTrue(countingPolicy.appendPayloadSizes.contains(tail)); + } finally { + // Delete the ~2.5 GiB source eagerly instead of waiting for deleteOnExit() so it doesn't occupy agent + // disk for the remainder of the module's test JVM. + if (!file.delete()) { + file.deleteOnExit(); + } + } } // This test does not send large payload over the wire From 85d684fb133550ad76adbe406622c778d05d31c5 Mon Sep 17 00:00:00 2001 From: Isabelle <141270045+ibrandes@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:56:02 -0700 Subject: [PATCH 6/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../storage/blob/BlobContentValidationAsyncUploadTests.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java index 16d576e3d66b..af6f934322e5 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java @@ -853,10 +853,8 @@ public void appendBlockLiveRandomRoundTripDataIntegrity() throws Exception { return client.appendBlockWithResponse(Flux.just(bb), bb.remaining(), appendOptions); }), channel -> { - try { - channel.close(); } catch (IOException e) { - throw new RuntimeException(e); + throw new java.io.UncheckedIOException(e); } })) .then(blobClient.downloadToFile(outFile.getPath(), true))) From c549b237129934d6588d857f86de351db2523b2b Mon Sep 17 00:00:00 2001 From: Isabelle Date: Fri, 24 Jul 2026 09:31:19 -0700 Subject: [PATCH 7/9] fixing copilot suggestion by adding try statement to catch --- .../storage/blob/BlobContentValidationAsyncUploadTests.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java index af6f934322e5..3c74d46816d2 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java @@ -853,7 +853,10 @@ public void appendBlockLiveRandomRoundTripDataIntegrity() throws Exception { return client.appendBlockWithResponse(Flux.just(bb), bb.remaining(), appendOptions); }), channel -> { - } catch (IOException e) { + try { + channel.close(); + } + catch (IOException e) { throw new java.io.UncheckedIOException(e); } })) From 94a46ca9faab75dbf90f7bab202d7773ce4448d2 Mon Sep 17 00:00:00 2001 From: Isabelle Date: Fri, 24 Jul 2026 09:50:17 -0700 Subject: [PATCH 8/9] spotless --- .../blob/BlobContentValidationAsyncUploadTests.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java index 3c74d46816d2..382644018489 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobContentValidationAsyncUploadTests.java @@ -842,10 +842,9 @@ public void appendBlockLiveRandomRoundTripDataIntegrity() throws Exception { // Append blobs must be created before any block can be appended; otherwise the first // appendBlock call fails with 404 BlobNotFound. Create, append every block, then download, // all chained into a single reactive pipeline (no blocking calls in the test). - StepVerifier - .create(client.create() - .thenMany(Flux.using( - () -> AsynchronousFileChannel.open(sourceFile.toPath(), StandardOpenOption.READ), + StepVerifier.create(client.create() + .thenMany( + Flux.using(() -> AsynchronousFileChannel.open(sourceFile.toPath(), StandardOpenOption.READ), channel -> FluxUtil.readFile(channel, maxAppendBlockBytes, 0, chosenPayloadSizeBytes) .concatMap(bb -> { AppendBlobAppendBlockOptions appendOptions = new AppendBlobAppendBlockOptions() @@ -855,12 +854,11 @@ public void appendBlockLiveRandomRoundTripDataIntegrity() throws Exception { channel -> { try { channel.close(); - } - catch (IOException e) { + } catch (IOException e) { throw new java.io.UncheckedIOException(e); } })) - .then(blobClient.downloadToFile(outFile.getPath(), true))) + .then(blobClient.downloadToFile(outFile.getPath(), true))) .assertNext(Assertions::assertNotNull) .verifyComplete(); assertTrue(compareFiles(sourceFile, outFile, 0, chosenPayloadSizeBytes), prefix); From 718550f97c7d51909cc3a8c5fa066029d5feeeba Mon Sep 17 00:00:00 2001 From: Isabelle Date: Sun, 26 Jul 2026 10:33:58 -0700 Subject: [PATCH 9/9] reducing size of abort copy payload to prevent surpassing test proxy payload cap --- .../src/test/java/com/azure/storage/blob/BlobApiTests.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java index e42bf74e34e7..44a32b6a420f 100644 --- a/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java +++ b/sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/BlobApiTests.java @@ -2158,7 +2158,12 @@ public void copyDestACFail(OffsetDateTime modified, OffsetDateTime unmodified, S // lands. A larger source widens that window (A1); combined with an assumeTrue guard on the poll status (B), // the tests assert abort behavior when a copy is genuinely pending and skip (rather than fail) if the // service finished the copy first. Server-to-server copy, so the larger size costs only the initial upload. - private static final int ABORT_COPY_SOURCE_SIZE_BYTES = 64 * Constants.MB; + // + // The source is capped just under the test proxy's 30,000,000-byte request-body limit: a body at/above that + // limit cannot be ingested by the proxy in record or playback (it fails with HTTP 500 "Request body too large" + // or a premature connection close, and also destabilizes the shared proxy connection pool for concurrent + // tests). 28 MiB (29,360,128 bytes) stays under the cap while keeping the window wide enough to exercise abort. + private static final int ABORT_COPY_SOURCE_SIZE_BYTES = 28 * Constants.MB; @Test public void abortCopyBaseSimple() {