Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0075cd0
Use BulkExecutor for pre-populating documents in benchmarks
Mar 12, 2026
4df5cde
Use streaming Flux for bulk pre-population to reduce memory pressure
Mar 12, 2026
cd9b3d9
Add createItem fallback retry for failed bulk operations
Mar 12, 2026
403aeab
Reduce Gateway mode per-request CPU overhead
Mar 12, 2026
2f1c8a7
Revert HttpHeaders toLowerCaseIfNeeded optimization
Mar 12, 2026
0d19062
Eliminate URI parsing on Gateway request hot path
Mar 12, 2026
0d8e708
Add HTTP/2 support to benchmark workloads
Mar 12, 2026
7916e5c
Fix http2Enabled/http2MaxConcurrentStreams not applied from tenantDef…
Mar 12, 2026
277cb83
Add test to guard applyField coverage for new config fields
Mar 12, 2026
89339b4
Remove test-output from tracking and add to .gitignore
Mar 12, 2026
c500bdd
R3: Lazy response header access — skip intermediate HttpHeaders copy
Mar 12, 2026
f1ba753
Eliminate Map→String[]→Map header copy chain in response path
Mar 12, 2026
6f29ee3
Remove test-output from tracking
Mar 12, 2026
9c91ec3
Deprecate getResponseHeaderNames/Values, fix toArray garbage
Mar 12, 2026
f7a841d
Add test-output/ to gitignore
Mar 12, 2026
a36bc14
Revert headerMap() — keep HttpHeaders copy, retain StoreResponse Map
Mar 13, 2026
0e28165
R2: Set initialBufferSize(16384) for HTTP response decoder
Mar 13, 2026
e12678a
Revert to v4 (URI elim) + add initialBufferSize(16384)
Mar 13, 2026
5a9e0ea
Revert to v4 — remove initialBufferSize(16384)
Mar 13, 2026
1c15947
Merge branch 'main' into optimizeBenchmarkPreCreateDocFlow
Mar 17, 2026
d4ce345
revert URI elimiation change
Mar 17, 2026
c61bef3
Add CPU cool-down after data ingestion in benchmark orchestrator
Mar 17, 2026
4e8ecf1
Remove redundant ReadLatency, WriteLatency, ReadManyLatency operation…
Mar 17, 2026
54ab5f0
Merge branch 'main' into optimizeBenchmarkPreCreateDocFlow
Mar 17, 2026
0a4c1e5
Address PR review comments for benchmark optimization
Mar 17, 2026
5e820c9
Address follow-up review comments
Mar 17, 2026
91dc0ab
Optimize pre-population: lazy creation + lightweight docsToRead
Mar 17, 2026
68bc0e6
Revert lightweight docsToRead: keep full docs in memory
Mar 17, 2026
4edeef9
Improve retry logic: backoff, status code 449, concurrency 20
Mar 17, 2026
c446d59
Make logger static final in AsyncEncryptionBenchmark
Mar 17, 2026
d4a990d
Make logger static final across all benchmark classes
Mar 17, 2026
8cb463e
Use Math.min(workloadConcurrency, 20) for retry concurrency
Mar 17, 2026
f42ce50
perf(cosmos): use O(1) header lookup in Http2ResponseHeaderCleanerHan…
Mar 17, 2026
39d64df
Merge branch 'main' into optimizeBenchmarkPreCreateDocFlow
FabianMeiswinkel Mar 18, 2026
2680b62
fix(benchmark): use parseable sentinel value in TenantWorkloadConfigA…
Mar 18, 2026
5acb565
Merge branch 'optimizeBenchmarkPreCreateDocFlow' into perf/p1-fix-htt…
Mar 18, 2026
0b8c38a
Merge branch 'main' into perf/p1-fix-http2-header-cleaner-handler
FabianMeiswinkel Mar 18, 2026
80db8dd
Merge branch 'main' into perf/p1-fix-http2-header-cleaner-handler
Mar 18, 2026
7ae9ff7
Merge branch 'perf/p1-fix-http2-header-cleaner-handler' of https://gi…
Mar 18, 2026
bb9f148
Address PR review comments: fix grammar, log messages, step numbering…
Mar 18, 2026
aa0fd25
Fix WorkflowTest CLI tests to use JSON workload config
Mar 18, 2026
ff32986
Fix ConsistencyLevel parsing to handle display names like BoundedStal…
Mar 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,11 @@ private void runLifecycleLoop(BenchmarkConfig config) throws Exception {
runWorkload(benchmarks, cycle, executor);
logger.info("[LIFECYCLE] POST_WORKLOAD cycle={} timestamp={}", cycle, Instant.now());

// 3. Close all clients
// 5. Close all clients
shutdownBenchmarks(benchmarks, cycle);
logger.info("[LIFECYCLE] POST_CLOSE cycle={} timestamp={}", cycle, Instant.now());

// 4. Settle
// 6. Settle
if (config.getSettleTimeMs() > 0) {
logger.info(" Settling for {}ms...", config.getSettleTimeMs());
long halfSettle = config.getSettleTimeMs() / 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,13 @@ protected void init() {
CompletableFuture<PojoizedJson> futureResult = CompletableFuture.supplyAsync(() -> {

int maxRetries = 5;
Exception lastException = null;
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
CosmosItemResponse<PojoizedJson> itemResponse = cosmosContainer.createItem(newDoc);
return toPojoizedJson(itemResponse);
} catch (CosmosException ce) {
lastException = ce;
if (ce.getStatusCode() == 409) {
// conflict — document already exists, read it back
try {
Expand All @@ -214,10 +216,11 @@ protected void init() {
}
throw propagate(ce);
} catch (Exception e) {
lastException = e;
throw propagate(e);
}
}
throw new RuntimeException("Exhausted retries for createItem");
throw new RuntimeException("Exhausted retries for createItem", lastException);

}, executorService);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ public ConnectionMode getConnectionMode() {

public ConsistencyLevel getConsistencyLevel() {
if (consistencyLevel == null) return ConsistencyLevel.SESSION;
for (ConsistencyLevel level : ConsistencyLevel.values()) {
if (level.toString().equalsIgnoreCase(consistencyLevel)
|| level.name().equalsIgnoreCase(consistencyLevel)) {
return level;
}
}
return ConsistencyLevel.valueOf(consistencyLevel.toUpperCase());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ private void createPrePopulatedDocs(int numberOfPreCreatedDocuments) {
} else {
failureCount.incrementAndGet();
failedResponses.add(response);
logger.debug("Error during pre populating item {}",
logger.debug("Error during pre-populating: {}",
response.getException() != null ? response.getException().getMessage() : "unknown error");
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
import com.azure.cosmos.models.IncludedPath;
import com.azure.cosmos.models.IndexingPolicy;
import com.azure.cosmos.models.PartitionKeyDefinition;
import org.apache.commons.lang3.StringUtils;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import reactor.core.scheduler.Schedulers;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
Expand All @@ -32,17 +33,22 @@ public class WorkflowTest {

@Test(groups = "fast", timeOut = TIMEOUT)
public void readMyWritesCLI() throws Exception {
String cmdFormat = "-serviceEndpoint %s -masterKey %s" +
" -databaseId %s -collectionId %s" +
" -consistencyLevel SESSION -concurrency 2 -numberOfOperations 123" +
" -operation ReadMyWrites -connectionMode DIRECT -numberOfPreCreatedDocuments 100";

String cmd = String.format(cmdFormat,
TestConfigurations.HOST,
TestConfigurations.MASTER_KEY,
database.getId(),
collection.getId());
Main.main(StringUtils.split(cmd));
File configFile = createWorkloadConfigFile(
TestConfigurations.HOST,
TestConfigurations.MASTER_KEY,
database.getId(),
collection.getId(),
"ReadMyWrites",
"DIRECT",
"SESSION",
2,
123,
100);
try {
Main.main(new String[]{"-workloadConfig", configFile.getAbsolutePath()});
} finally {
configFile.delete();
}
}

@Test(dataProvider = "collectionLinkTypeArgProvider", groups = "fast", timeOut = TIMEOUT)
Expand Down Expand Up @@ -85,17 +91,22 @@ protected void onSuccess() {

@Test(groups = "fast", timeOut = TIMEOUT)
public void writeThroughputCLI() throws Exception {
String cmdFormat = "-serviceEndpoint %s -masterKey %s" +
" -databaseId %s -collectionId %s" +
" -consistencyLevel SESSION -concurrency 2 -numberOfOperations 1000" +
" -operation WriteThroughput -connectionMode DIRECT";

String cmd = String.format(cmdFormat,
TestConfigurations.HOST,
TestConfigurations.MASTER_KEY,
database.getId(),
collection.getId());
Main.main(StringUtils.split(cmd));
File configFile = createWorkloadConfigFile(
TestConfigurations.HOST,
TestConfigurations.MASTER_KEY,
database.getId(),
collection.getId(),
"WriteThroughput",
"DIRECT",
"SESSION",
2,
1000,
0);
try {
Main.main(new String[]{"-workloadConfig", configFile.getAbsolutePath()});
} finally {
configFile.delete();
}
}

@Test(dataProvider = "collectionLinkTypeArgProvider", groups = "fast", timeOut = TIMEOUT)
Expand Down Expand Up @@ -292,6 +303,42 @@ public void afterClass() {
Utils.safeClose(housekeepingClient);
}

private File createWorkloadConfigFile(
String serviceEndpoint,
String masterKey,
String databaseId,
String containerId,
String operation,
String connectionMode,
String consistencyLevel,
int concurrency,
int numberOfOperations,
int numberOfPreCreatedDocuments) throws Exception {

String json = String.format(
"{ \"tenants\": [{ "
+ "\"serviceEndpoint\": \"%s\", "
+ "\"masterKey\": \"%s\", "
+ "\"databaseId\": \"%s\", "
+ "\"containerId\": \"%s\", "
+ "\"operation\": \"%s\", "
+ "\"connectionMode\": \"%s\", "
+ "\"consistencyLevel\": \"%s\", "
+ "\"concurrency\": %d, "
+ "\"numberOfOperations\": %d, "
+ "\"numberOfPreCreatedDocuments\": %d "
+ "}] }",
serviceEndpoint, masterKey, databaseId, containerId,
operation, connectionMode, consistencyLevel,
concurrency, numberOfOperations, numberOfPreCreatedDocuments);

File tempFile = File.createTempFile("workload-config-", ".json");
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write(json);
}
return tempFile;
}

DocumentCollection getCollectionDefinitionWithRangeRangeIndex() {
PartitionKeyDefinition partitionKeyDef = new PartitionKeyDefinition();
ArrayList<String> paths = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,35 @@
package com.azure.cosmos.implementation.http;

import com.azure.cosmos.implementation.HttpConstants;
import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2HeadersFrame;
import io.netty.handler.codec.http2.Http2SettingsAckFrame;
import io.netty.handler.codec.http2.Http2SettingsFrame;
import io.netty.util.AsciiString;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Http2ResponseHeaderCleanerHandler extends ChannelInboundHandlerAdapter {

private static final Logger logger = LoggerFactory.getLogger(Http2ResponseHeaderCleanerHandler.class);
private static final AsciiString SERVER_VERSION_KEY = AsciiString.of(HttpConstants.HttpHeaders.SERVER_VERSION);

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof Http2HeadersFrame) {
Http2HeadersFrame headersFrame = (Http2HeadersFrame) msg;
Http2Headers headers = headersFrame.headers();

headers.forEach(entry -> {
CharSequence key = entry.getKey();
CharSequence value = entry.getValue();

// Based on the tests, only 'x-ms-serviceversion' header has extra value,
// so only check this specific header here
if (StringUtils.equalsIgnoreCase(key, HttpConstants.HttpHeaders.SERVER_VERSION)) {
// Check for leading whitespace or other prohibited characters
if (StringUtils.isNotEmpty(value) && (value.charAt(0) == ' ' || value.charAt(value.length() - 1) == ' ')) {
// Clean up the header value by trimming or handling as needed
logger.trace("There are extra white space for key {} with value {}", key, value);

// TODO[Http2]: for now just trim the spaces, explore other options for example escape the whitespace
headers.set(key, value.toString().trim());
}
}
});
// Direct O(1) hash lookup instead of O(n) forEach iteration over all headers
CharSequence serverVersion = headers.get(SERVER_VERSION_KEY);
if (serverVersion != null && serverVersion.length() > 0
&& (serverVersion.charAt(0) == ' ' || serverVersion.charAt(serverVersion.length() - 1) == ' ')) {
logger.trace("There is extra whitespace for key {} with value {}", SERVER_VERSION_KEY, serverVersion);
headers.set(SERVER_VERSION_KEY, serverVersion.toString().trim());
}

super.channelRead(ctx, msg);
} else if (msg instanceof Http2SettingsAckFrame) {
Expand Down
Loading