From 444d2e416212a7877f01f5fb2dfd7a043a99c9b6 Mon Sep 17 00:00:00 2001 From: Gunjan Singh Date: Wed, 29 Apr 2026 19:23:27 +0530 Subject: [PATCH 1/6] adding stress tests for content validation decoder --- ...ContentValidationDecoderStressOptions.java | 26 +++++ .../ContentValidationDownloadContent.java | 61 ++++++++++ .../ContentValidationDownloadStream.java | 66 +++++++++++ .../ContentValidationDownloadToFile.java | 104 ++++++++++++++++++ .../ContentValidationOpenInputStream.java | 69 ++++++++++++ ...ValidationOpenSeekableByteChannelRead.java | 72 ++++++++++++ 6 files changed, 398 insertions(+) create mode 100644 sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDecoderStressOptions.java create mode 100644 sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java create mode 100644 sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadStream.java create mode 100644 sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadToFile.java create mode 100644 sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenInputStream.java create mode 100644 sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenSeekableByteChannelRead.java diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDecoderStressOptions.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDecoderStressOptions.java new file mode 100644 index 000000000000..bc16742ac621 --- /dev/null +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDecoderStressOptions.java @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.stress; + +import com.azure.storage.common.ContentValidationAlgorithm; +import com.azure.storage.stress.StorageStressOptions; +import com.beust.jcommander.Parameter; + +/** + * Options for stress scenarios that enable transactional response content validation on downloads + * (CRC64 / structured message). See {@link com.azure.storage.blob.BlobContentValidationDownloadTests}. + */ +public class ContentValidationDecoderStressOptions extends StorageStressOptions { + /** + * Response content validation behavior for download APIs. Use CRC64 or AUTO to exercise content validation. + * NONE disables response validation. + */ + @Parameter(names = { "--contentValidationAlgorithm" }, + description = "CRC64 (default), AUTO, or NONE") + private ContentValidationAlgorithm contentValidationAlgorithm = ContentValidationAlgorithm.CRC64; + + public ContentValidationAlgorithm getContentValidationAlgorithm() { + return contentValidationAlgorithm; + } +} diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java new file mode 100644 index 000000000000..058a080694ec --- /dev/null +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.stress; + +import com.azure.core.util.Context; +import com.azure.storage.blob.BlobAsyncClient; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.options.BlobDownloadContentOptions; +import com.azure.storage.blob.stress.utils.OriginalContent; +import reactor.core.publisher.Mono; + +/** + * Download content with + * {@link BlobDownloadContentOptions#setContentValidationAlgorithm} enabled. + * Verifies the correctness of the download response content via CRC. + */ +public class ContentValidationDownloadContent extends BlobScenarioBase { + private final OriginalContent originalContent = new OriginalContent(); + private final BlobClient syncClient; + private final BlobAsyncClient asyncClient; + private final BlobAsyncClient asyncNoFaultClient; + + public ContentValidationDownloadContent(ContentValidationDecoderStressOptions options) { + super(options); + String blobName = generateBlobName(); + this.asyncNoFaultClient = getAsyncContainerClientNoFault().getBlobAsyncClient(blobName); + this.syncClient = getSyncContainerClient().getBlobClient(blobName); + this.asyncClient = getAsyncContainerClient().getBlobAsyncClient(blobName); + } + + @Override + protected void runInternal(Context span) { + originalContent.checkMatch( + syncClient.downloadContentWithResponse( + new BlobDownloadContentOptions() + .setContentValidationAlgorithm(options.getContentValidationAlgorithm()), + null, span).getValue(), + span).block(); + } + + @Override + protected Mono runInternalAsync(Context span) { + return asyncClient.downloadContentWithResponse( + new BlobDownloadContentOptions() + .setContentValidationAlgorithm(options.getContentValidationAlgorithm())) + .flatMap(r -> originalContent.checkMatch(r.getValue(), span)); + } + + @Override + public Mono setupAsync() { + return super.setupAsync() + .then(originalContent.setupBlob(asyncNoFaultClient, options.getSize())); + } + + @Override + public Mono cleanupAsync() { + return asyncNoFaultClient.deleteIfExists() + .then(super.cleanupAsync()); + } +} diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadStream.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadStream.java new file mode 100644 index 000000000000..1fcaf549657a --- /dev/null +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadStream.java @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.stress; + +import com.azure.core.util.Context; +import com.azure.storage.blob.BlobAsyncClient; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.options.BlobDownloadStreamOptions; +import com.azure.storage.blob.stress.utils.OriginalContent; +import com.azure.storage.stress.CrcOutputStream; +import reactor.core.publisher.Mono; + +import java.io.IOException; + +/** + * Streaming blob download with + * {@link BlobDownloadStreamOptions#setContentValidationAlgorithm} enabled. + * Verifies the correctness of the download response content via CRC. + */ +public class ContentValidationDownloadStream extends BlobScenarioBase { + private final OriginalContent originalContent = new OriginalContent(); + private final BlobClient syncClient; + private final BlobAsyncClient asyncClient; + private final BlobAsyncClient asyncNoFaultClient; + + public ContentValidationDownloadStream(ContentValidationDecoderStressOptions options) { + super(options); + String blobName = generateBlobName(); + this.asyncNoFaultClient = getAsyncContainerClientNoFault().getBlobAsyncClient(blobName); + this.syncClient = getSyncContainerClient().getBlobClient(blobName); + this.asyncClient = getAsyncContainerClient().getBlobAsyncClient(blobName); + } + + @Override + protected void runInternal(Context span) throws IOException { + try (CrcOutputStream outputStream = new CrcOutputStream()) { + syncClient.downloadStreamWithResponse(outputStream, + new BlobDownloadStreamOptions() + .setContentValidationAlgorithm(options.getContentValidationAlgorithm()), + null, span); + outputStream.close(); + originalContent.checkMatch(outputStream.getContentInfo(), span).block(); + } + } + + @Override + protected Mono runInternalAsync(Context span) { + return asyncClient.downloadStreamWithResponse( + new BlobDownloadStreamOptions() + .setContentValidationAlgorithm(options.getContentValidationAlgorithm())) + .flatMap(response -> originalContent.checkMatch(response.getValue(), span)); + } + + @Override + public Mono setupAsync() { + return super.setupAsync() + .then(originalContent.setupBlob(asyncNoFaultClient, options.getSize())); + } + + @Override + public Mono cleanupAsync() { + return asyncNoFaultClient.deleteIfExists() + .then(super.cleanupAsync()); + } +} diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadToFile.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadToFile.java new file mode 100644 index 000000000000..da52199b1339 --- /dev/null +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadToFile.java @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.stress; + +import com.azure.core.util.BinaryData; +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.blob.BlobAsyncClient; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.options.BlobDownloadToFileOptions; +import com.azure.storage.blob.stress.utils.OriginalContent; +import com.azure.storage.common.ParallelTransferOptions; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.UUID; + +/** + * Download to file with + * {@link BlobDownloadToFileOptions#setContentValidationAlgorithm} enabled. + * Verifies the correctness of the download response content via CRC. + */ +public class ContentValidationDownloadToFile extends BlobScenarioBase { + private static final ClientLogger LOGGER = new ClientLogger(ContentValidationDownloadToFile.class); + private final Path directoryPath; + private final OriginalContent originalContent = new OriginalContent(); + private final BlobClient syncClient; + private final BlobAsyncClient asyncClient; + private final BlobAsyncClient asyncNoFaultClient; + private final ParallelTransferOptions parallelTransferOptions; + + public ContentValidationDownloadToFile(ContentValidationDecoderStressOptions options) { + super(options); + this.directoryPath = getTempPath("test"); + String blobName = generateBlobName(); + this.asyncNoFaultClient = getAsyncContainerClientNoFault().getBlobAsyncClient(blobName); + this.syncClient = getSyncContainerClient().getBlobClient(blobName); + this.asyncClient = getAsyncContainerClient().getBlobAsyncClient(blobName); + this.parallelTransferOptions = new ParallelTransferOptions() + .setMaxConcurrency(options.getMaxConcurrency()); + } + + @Override + protected void runInternal(Context span) { + Path downloadPath = directoryPath.resolve(UUID.randomUUID() + ".txt"); + BlobDownloadToFileOptions blobOptions = new BlobDownloadToFileOptions(downloadPath.toString()) + .setParallelTransferOptions(parallelTransferOptions) + .setContentValidationAlgorithm(options.getContentValidationAlgorithm()); + + try { + syncClient.downloadToFileWithResponse(blobOptions, Duration.ofSeconds(options.getDuration()), span); + originalContent.checkMatch(BinaryData.fromFile(downloadPath), span).block(); + } finally { + deleteFile(downloadPath); + } + } + + @Override + protected Mono runInternalAsync(Context span) { + return Mono.using( + () -> directoryPath.resolve(UUID.randomUUID() + ".txt"), + path -> asyncClient.downloadToFileWithResponse( + new BlobDownloadToFileOptions(path.toString()) + .setParallelTransferOptions(parallelTransferOptions) + .setContentValidationAlgorithm(options.getContentValidationAlgorithm())) + .flatMap(ignored -> originalContent.checkMatch(BinaryData.fromFile(path), span)), + ContentValidationDownloadToFile::deleteFile); + } + + @Override + public Mono setupAsync() { + return super.setupAsync() + .then(originalContent.setupBlob(asyncNoFaultClient, options.getSize())); + } + + @Override + public Mono cleanupAsync() { + return asyncNoFaultClient.deleteIfExists() + .then(super.cleanupAsync()); + } + + private Path getTempPath(String prefix) { + try { + return Files.createTempDirectory(prefix); + } catch (IOException e) { + throw LOGGER.logExceptionAsError(new UncheckedIOException(e)); + } + } + + private static void deleteFile(Path path) { + try { + Files.deleteIfExists(path); + } catch (Throwable e) { + LOGGER.atError() + .addKeyValue("path", path) + .log("failed to delete file", e); + } + } +} diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenInputStream.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenInputStream.java new file mode 100644 index 000000000000..5d282c7e9c7a --- /dev/null +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenInputStream.java @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.stress; + +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.blob.BlobAsyncClient; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.options.BlobInputStreamOptions; +import com.azure.storage.blob.stress.utils.OriginalContent; +import com.azure.storage.stress.CrcInputStream; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.io.InputStream; + +import static com.azure.core.util.FluxUtil.monoError; + +/** + * Open input stream with {@link BlobInputStreamOptions#setContentValidationAlgorithm} enabled (sync only). + * Verifies the correctness of the download response content via CRC. + */ +public class ContentValidationOpenInputStream extends BlobScenarioBase { + private static final ClientLogger LOGGER = new ClientLogger(ContentValidationOpenInputStream.class); + private final OriginalContent originalContent = new OriginalContent(); + private final BlobClient syncClient; + private final BlobAsyncClient asyncNoFaultClient; + + public ContentValidationOpenInputStream(ContentValidationDecoderStressOptions options) { + super(options); + String blobName = generateBlobName(); + this.syncClient = getSyncContainerClient().getBlobClient(blobName); + this.asyncNoFaultClient = getAsyncContainerClientNoFault().getBlobAsyncClient(blobName); + } + + @Override + protected void runInternal(Context span) throws IOException { + try (InputStream stream = syncClient.openInputStream( + new BlobInputStreamOptions() + .setContentValidationAlgorithm(options.getContentValidationAlgorithm()), + span)) { + try (CrcInputStream crcStream = new CrcInputStream(stream)) { + byte[] buffer = new byte[8192]; + while (crcStream.read(buffer) != -1) { + // do nothing + } + originalContent.checkMatch(crcStream.getContentInfo(), span).block(); + } + } + } + + @Override + protected Mono runInternalAsync(Context span) { + return monoError(LOGGER, new RuntimeException("openInputStream() does not exist on the async client")); + } + + @Override + public Mono setupAsync() { + return super.setupAsync() + .then(originalContent.setupBlob(asyncNoFaultClient, options.getSize())); + } + + @Override + public Mono cleanupAsync() { + return asyncNoFaultClient.deleteIfExists() + .then(super.cleanupAsync()); + } +} diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenSeekableByteChannelRead.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenSeekableByteChannelRead.java new file mode 100644 index 000000000000..8de4aefd8832 --- /dev/null +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationOpenSeekableByteChannelRead.java @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.storage.blob.stress; + +import com.azure.core.util.Context; +import com.azure.core.util.logging.ClientLogger; +import com.azure.storage.blob.BlobAsyncClient; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.models.BlobSeekableByteChannelReadResult; +import com.azure.storage.blob.options.BlobSeekableByteChannelReadOptions; +import com.azure.storage.blob.stress.utils.OriginalContent; +import com.azure.storage.stress.CrcInputStream; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.nio.channels.Channels; + +import static com.azure.core.util.FluxUtil.monoError; + +/** + * Seekable byte channel read with {@link BlobSeekableByteChannelReadOptions#setContentValidationAlgorithm} + * enabled (sync only). + * Verifies the correctness of the download response content via CRC. + */ +public class ContentValidationOpenSeekableByteChannelRead + extends BlobScenarioBase { + private static final ClientLogger LOGGER = new ClientLogger(ContentValidationOpenSeekableByteChannelRead.class); + private final OriginalContent originalContent = new OriginalContent(); + private final BlobClient syncClient; + private final BlobAsyncClient asyncNoFaultClient; + + public ContentValidationOpenSeekableByteChannelRead(ContentValidationDecoderStressOptions options) { + super(options); + String blobName = generateBlobName(); + this.asyncNoFaultClient = getAsyncContainerClientNoFault().getBlobAsyncClient(blobName); + this.syncClient = getSyncContainerClient().getBlobClient(blobName); + } + + @Override + protected void runInternal(Context span) throws IOException { + BlobSeekableByteChannelReadResult result = syncClient.openSeekableByteChannelRead( + new BlobSeekableByteChannelReadOptions() + .setContentValidationAlgorithm(options.getContentValidationAlgorithm()), + span); + try (CrcInputStream crcStream = new CrcInputStream(Channels.newInputStream(result.getChannel()))) { + byte[] buffer = new byte[8192]; + while (crcStream.read(buffer) != -1) { + // do nothing + } + originalContent.checkMatch(crcStream.getContentInfo(), span).block(); + } + } + + @Override + protected Mono runInternalAsync(Context span) { + return monoError(LOGGER, + new RuntimeException("openSeekableByteChannelRead() does not exist on the async client")); + } + + @Override + public Mono setupAsync() { + return super.setupAsync() + .then(originalContent.setupBlob(asyncNoFaultClient, options.getSize())); + } + + @Override + public Mono cleanupAsync() { + return asyncNoFaultClient.deleteIfExists() + .then(super.cleanupAsync()); + } +} From 082dd86c1725b4b473d87d794dcaab9faa22cacf Mon Sep 17 00:00:00 2001 From: browndav Date: Wed, 29 Apr 2026 16:38:48 -0400 Subject: [PATCH 2/6] fix TelemetryHelper, add tru to registerOberservers() --- .../src/main/java/com/azure/storage/stress/TelemetryHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java b/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java index 4bf6e523eae1..b633479100b1 100644 --- a/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java +++ b/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java @@ -124,7 +124,7 @@ public String getDescription() { Cpu.registerObservers(otel); MemoryPools.registerObservers(otel); Threads.registerObservers(otel); - GarbageCollector.registerObservers(otel); + GarbageCollector.registerObservers(otel, true); OpenTelemetryAppender.install(otel); return otel; } From 4a9f96531685c6a8622f491524dac7f8e58c8c2f Mon Sep 17 00:00:00 2001 From: browndav-msft Date: Mon, 16 Mar 2026 18:37:14 -0400 Subject: [PATCH 3/6] Storage - Fix Flaky Stress Tests (#48359) * removed enableDeterministic * change .delete() to .deleteIfExists() * remove Sinks.EmitFailureHandler.FAIL_FAST from CrcInputStream - read functions had FAIL_FAST which would throw an error when the stream had reached then end and we wanted to read from the stream again. So we removed from both reads. - refactor code so that the exit criteria is a tthe beginning - refactor the emitContentInfo for dry * prevent crashes on reattempted close on stream - changed emitValue to tryEmitValue - remove Sinks.EmitFailureHandler.FAIL_FAST so that multiple closes does not cause an error to be thrown * fix telemetry so that it doesnt swallow errors * roll back two deps because they were causing failures in the containers - opentelemetry-runtime-telemetry-java8 from 2.24.0-alpha -> 2.15.0-alpha - opentelemetry-logback-appender-1.0 from 2.24.0-alpha -> 2.15.0-alpha * rollback azure-client-sdk-parent linting extensions from 1.0.0-beta.2 t0 beta.1 * revert linting extensions to beta2 * remove comments with old code * add logging for errors * remove catches for double close issue and okay status * recursively delete files then delete the directory * change to sync deletes, refactor for easier reading * restructing share clean up so super calls only once * incorporate copilot suggestions * incorporate copilot suggestions * incorporate copilot suggestions * incorporate copilot suggestions * fix all deletes to make sync and wrap in try-catch * fix tests so that super.globalCleanupAsync() is only called once * change telemetry to loggin only returns final state instead of failed retries when ultimately successful * undo versio downgrade for linting-extensions * Fixing spacing in error messages Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * refactor datalake delete all so that it is easier to read * refactor runAsync in ShareScenarioBase so retry failures dont show as failures upon success --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- sdk/storage/azure-storage-blob-stress/pom.xml | 4 ++-- sdk/storage/azure-storage-file-datalake-stress/pom.xml | 4 ++-- sdk/storage/azure-storage-file-share-stress/pom.xml | 4 ++-- sdk/storage/azure-storage-stress/pom.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/storage/azure-storage-blob-stress/pom.xml b/sdk/storage/azure-storage-blob-stress/pom.xml index f214138d3f8b..a9c400807390 100644 --- a/sdk/storage/azure-storage-blob-stress/pom.xml +++ b/sdk/storage/azure-storage-blob-stress/pom.xml @@ -57,12 +57,12 @@ io.opentelemetry.instrumentation opentelemetry-runtime-telemetry-java8 - 2.24.0-alpha + 2.15.0-alpha io.opentelemetry.instrumentation opentelemetry-logback-appender-1.0 - 2.24.0-alpha + 2.15.0-alpha diff --git a/sdk/storage/azure-storage-file-datalake-stress/pom.xml b/sdk/storage/azure-storage-file-datalake-stress/pom.xml index f9702ed4e174..292efc277a91 100644 --- a/sdk/storage/azure-storage-file-datalake-stress/pom.xml +++ b/sdk/storage/azure-storage-file-datalake-stress/pom.xml @@ -57,12 +57,12 @@ io.opentelemetry.instrumentation opentelemetry-runtime-telemetry-java8 - 2.24.0-alpha + 2.15.0-alpha io.opentelemetry.instrumentation opentelemetry-logback-appender-1.0 - 2.24.0-alpha + 2.15.0-alpha diff --git a/sdk/storage/azure-storage-file-share-stress/pom.xml b/sdk/storage/azure-storage-file-share-stress/pom.xml index 3a6f0b35892c..f174f05bb637 100644 --- a/sdk/storage/azure-storage-file-share-stress/pom.xml +++ b/sdk/storage/azure-storage-file-share-stress/pom.xml @@ -57,12 +57,12 @@ io.opentelemetry.instrumentation opentelemetry-runtime-telemetry-java8 - 2.24.0-alpha + 2.15.0-alpha io.opentelemetry.instrumentation opentelemetry-logback-appender-1.0 - 2.24.0-alpha + 2.15.0-alpha diff --git a/sdk/storage/azure-storage-stress/pom.xml b/sdk/storage/azure-storage-stress/pom.xml index d8907fe67014..9a6cd7caf2a6 100644 --- a/sdk/storage/azure-storage-stress/pom.xml +++ b/sdk/storage/azure-storage-stress/pom.xml @@ -52,12 +52,12 @@ io.opentelemetry.instrumentation opentelemetry-runtime-telemetry-java8 - 2.24.0-alpha + 2.15.0-alpha io.opentelemetry.instrumentation opentelemetry-logback-appender-1.0 - 2.24.0-alpha + 2.15.0-alpha com.azure From cfe3251f7067b833faeeddaabf4a487468cd23bc Mon Sep 17 00:00:00 2001 From: browndav Date: Thu, 30 Apr 2026 20:07:51 -0400 Subject: [PATCH 4/6] changes to cvdownload content, lazyload versus eagerloading --- .../stress/ContentValidationDownloadContent.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java index 058a080694ec..07c549475e33 100644 --- a/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java +++ b/sdk/storage/azure-storage-blob-stress/src/main/java/com/azure/storage/blob/stress/ContentValidationDownloadContent.java @@ -3,10 +3,13 @@ package com.azure.storage.blob.stress; +import com.azure.core.http.HttpHeaderName; +import com.azure.core.util.BinaryData; import com.azure.core.util.Context; import com.azure.storage.blob.BlobAsyncClient; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.options.BlobDownloadContentOptions; +import com.azure.storage.blob.options.BlobDownloadStreamOptions; import com.azure.storage.blob.stress.utils.OriginalContent; import reactor.core.publisher.Mono; @@ -41,10 +44,15 @@ protected void runInternal(Context span) { @Override protected Mono runInternalAsync(Context span) { - return asyncClient.downloadContentWithResponse( - new BlobDownloadContentOptions() + // TODO return downloadContent once it stops buffering. + return asyncClient.downloadStreamWithResponse( + new BlobDownloadStreamOptions() .setContentValidationAlgorithm(options.getContentValidationAlgorithm())) - .flatMap(r -> originalContent.checkMatch(r.getValue(), span)); + .flatMap(response -> { + long contentLength = Long.valueOf(response.getHeaders().getValue(HttpHeaderName.CONTENT_LENGTH)); + return BinaryData.fromFlux(response.getValue(), contentLength, false); + }) + .flatMap(bd -> originalContent.checkMatch(bd, span)); } @Override From 2fbc339c4336961f10f4b6e450c1154a395096dc Mon Sep 17 00:00:00 2001 From: browndav Date: Thu, 30 Apr 2026 20:08:31 -0400 Subject: [PATCH 5/6] update to TelemetryHelper based on previous cherry picked stress tests fixes --- .../src/main/java/com/azure/storage/stress/TelemetryHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java b/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java index b633479100b1..4bf6e523eae1 100644 --- a/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java +++ b/sdk/storage/azure-storage-stress/src/main/java/com/azure/storage/stress/TelemetryHelper.java @@ -124,7 +124,7 @@ public String getDescription() { Cpu.registerObservers(otel); MemoryPools.registerObservers(otel); Threads.registerObservers(otel); - GarbageCollector.registerObservers(otel, true); + GarbageCollector.registerObservers(otel); OpenTelemetryAppender.install(otel); return otel; } From 4bbcf8c118f7c3ebce146dab244135c108eb2570 Mon Sep 17 00:00:00 2001 From: browndav Date: Fri, 1 May 2026 13:21:38 -0400 Subject: [PATCH 6/6] Fix storage stress fault injector certificate trust Export the fault injector certificate as PEM and wait for it before importing it into the Java truststore so storage stress tests fail fast instead of hitting PKIX errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../scripts/fault-injector.sh | 2 +- .../scripts/stress-run.sh | 12 +++++++++++- .../templates/stress-test-job.yaml | 14 ++++++++++++-- .../templates/stress-test-job.yaml | 14 ++++++++++++-- .../templates/stress-test-job.yaml | 14 ++++++++++++-- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/sdk/storage/azure-storage-blob-stress/scripts/fault-injector.sh b/sdk/storage/azure-storage-blob-stress/scripts/fault-injector.sh index ed834fc131d6..2d6b39967ab1 100644 --- a/sdk/storage/azure-storage-blob-stress/scripts/fault-injector.sh +++ b/sdk/storage/azure-storage-blob-stress/scripts/fault-injector.sh @@ -1,4 +1,4 @@ #!/bin/sh set -ex; -dotnet dev-certs https --export-path /mnt/outputs/dev-cert.pfx; +dotnet dev-certs https --export-path /mnt/outputs/dev-cert.crt --format PEM --no-password; /root/.dotnet/tools/http-fault-injector; diff --git a/sdk/storage/azure-storage-blob-stress/scripts/stress-run.sh b/sdk/storage/azure-storage-blob-stress/scripts/stress-run.sh index 20a7669c46e9..7604926a6782 100644 --- a/sdk/storage/azure-storage-blob-stress/scripts/stress-run.sh +++ b/sdk/storage/azure-storage-blob-stress/scripts/stress-run.sh @@ -1,4 +1,14 @@ #!/bin/sh set -ex; set -exa; -keytool -import -alias test -file /mnt/outputs/dev-cert.pfx -keystore ${JAVA_HOME}/lib/security/cacerts -noprompt -keypass changeit -storepass changeit; +attempts=0; +while [ ! -s /mnt/outputs/dev-cert.crt ]; do + attempts=$((attempts + 1)); + if [ "$attempts" -gt 60 ]; then + echo "Timed out waiting for fault injector certificate" >&2; + exit 1; + fi; + sleep 1; +done; +keytool -delete -alias HttpFaultInject -keystore "${JAVA_HOME}/lib/security/cacerts" -storepass changeit >/dev/null 2>&1 || true; +keytool -importcert -trustcacerts -alias HttpFaultInject -file /mnt/outputs/dev-cert.crt -keystore "${JAVA_HOME}/lib/security/cacerts" -noprompt -storepass changeit; diff --git a/sdk/storage/azure-storage-blob-stress/templates/stress-test-job.yaml b/sdk/storage/azure-storage-blob-stress/templates/stress-test-job.yaml index 2a22302d24ed..03a0609beebd 100644 --- a/sdk/storage/azure-storage-blob-stress/templates/stress-test-job.yaml +++ b/sdk/storage/azure-storage-blob-stress/templates/stress-test-job.yaml @@ -16,7 +16,7 @@ spec: args: - | set -ex; - dotnet dev-certs https --export-path /mnt/outputs/dev-cert.pfx; + dotnet dev-certs https --export-path /mnt/outputs/dev-cert.crt --format PEM --no-password; /root/.dotnet/tools/http-fault-injector; resources: limits: @@ -30,7 +30,17 @@ spec: - | set -xa; set -o pipefail; - keytool -import -alias test -file /mnt/outputs/dev-cert.pfx -keystore ${JAVA_HOME}/lib/security/cacerts -noprompt -keypass changeit -storepass changeit; + attempts=0; + while [ ! -s /mnt/outputs/dev-cert.crt ]; do + attempts=$((attempts + 1)); + if [ "$attempts" -gt 60 ]; then + echo "Timed out waiting for fault injector certificate" >&2; + exit 1; + fi; + sleep 1; + done; + keytool -delete -alias HttpFaultInject -keystore "${JAVA_HOME}/lib/security/cacerts" -storepass changeit >/dev/null 2>&1 || true; + keytool -importcert -trustcacerts -alias HttpFaultInject -file /mnt/outputs/dev-cert.crt -keystore "${JAVA_HOME}/lib/security/cacerts" -noprompt -storepass changeit || exit 1; mkdir -p "$DEBUG_SHARE"; . /mnt/outputs/.env; export AZURE_HTTP_CLIENT_IMPLEMENTATION=com.azure.core.http.netty.NettyAsyncHttpClientProvider; diff --git a/sdk/storage/azure-storage-file-datalake-stress/templates/stress-test-job.yaml b/sdk/storage/azure-storage-file-datalake-stress/templates/stress-test-job.yaml index e86f52638947..c10c4ded6d64 100644 --- a/sdk/storage/azure-storage-file-datalake-stress/templates/stress-test-job.yaml +++ b/sdk/storage/azure-storage-file-datalake-stress/templates/stress-test-job.yaml @@ -16,7 +16,7 @@ spec: args: - | set -ex; - dotnet dev-certs https --export-path /mnt/outputs/dev-cert.pfx; + dotnet dev-certs https --export-path /mnt/outputs/dev-cert.crt --format PEM --no-password; /root/.dotnet/tools/http-fault-injector; resources: limits: @@ -30,7 +30,17 @@ spec: - | set -xa; set -o pipefail; - keytool -import -alias test -file /mnt/outputs/dev-cert.pfx -keystore ${JAVA_HOME}/lib/security/cacerts -noprompt -keypass changeit -storepass changeit; + attempts=0; + while [ ! -s /mnt/outputs/dev-cert.crt ]; do + attempts=$((attempts + 1)); + if [ "$attempts" -gt 60 ]; then + echo "Timed out waiting for fault injector certificate" >&2; + exit 1; + fi; + sleep 1; + done; + keytool -delete -alias HttpFaultInject -keystore "${JAVA_HOME}/lib/security/cacerts" -storepass changeit >/dev/null 2>&1 || true; + keytool -importcert -trustcacerts -alias HttpFaultInject -file /mnt/outputs/dev-cert.crt -keystore "${JAVA_HOME}/lib/security/cacerts" -noprompt -storepass changeit || exit 1; mkdir -p "$DEBUG_SHARE"; . /mnt/outputs/.env; export AZURE_HTTP_CLIENT_IMPLEMENTATION=com.azure.core.http.netty.NettyAsyncHttpClientProvider; diff --git a/sdk/storage/azure-storage-file-share-stress/templates/stress-test-job.yaml b/sdk/storage/azure-storage-file-share-stress/templates/stress-test-job.yaml index b558feecdcb8..fe77e9cb6f63 100644 --- a/sdk/storage/azure-storage-file-share-stress/templates/stress-test-job.yaml +++ b/sdk/storage/azure-storage-file-share-stress/templates/stress-test-job.yaml @@ -16,7 +16,7 @@ spec: args: - | set -ex; - dotnet dev-certs https --export-path /mnt/outputs/dev-cert.pfx; + dotnet dev-certs https --export-path /mnt/outputs/dev-cert.crt --format PEM --no-password; /root/.dotnet/tools/http-fault-injector; resources: limits: @@ -30,7 +30,17 @@ spec: - | set -xa; set -o pipefail; - keytool -import -alias test -file /mnt/outputs/dev-cert.pfx -keystore ${JAVA_HOME}/lib/security/cacerts -noprompt -keypass changeit -storepass changeit; + attempts=0; + while [ ! -s /mnt/outputs/dev-cert.crt ]; do + attempts=$((attempts + 1)); + if [ "$attempts" -gt 60 ]; then + echo "Timed out waiting for fault injector certificate" >&2; + exit 1; + fi; + sleep 1; + done; + keytool -delete -alias HttpFaultInject -keystore "${JAVA_HOME}/lib/security/cacerts" -storepass changeit >/dev/null 2>&1 || true; + keytool -importcert -trustcacerts -alias HttpFaultInject -file /mnt/outputs/dev-cert.crt -keystore "${JAVA_HOME}/lib/security/cacerts" -noprompt -storepass changeit || exit 1; mkdir -p "$DEBUG_SHARE"; . /mnt/outputs/.env; export AZURE_HTTP_CLIENT_IMPLEMENTATION=com.azure.core.http.netty.NettyAsyncHttpClientProvider;