Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ddabe92
Add ReadConsistencyStrategy RNTBD header and enable for all connectio…
jeet1995 Apr 16, 2026
faa9ccf
Fix: prevent x-ms-consistency-level rewrite in gateway mode when RCS …
jeet1995 Apr 16, 2026
6c0913c
Fix: strip x-ms-consistency-level when ReadConsistencyStrategy is set…
jeet1995 Apr 16, 2026
a6c0b50
Fix: change ReadConsistencyStrategy RNTBD token type from String to B…
jeet1995 Apr 16, 2026
db3ee0c
Update E2E tests: dynamic database/container creation, use TestConfig…
jeet1995 Apr 16, 2026
3f80dac
Fix E2E tests for serverless accounts: remove throughput parameter (#…
jeet1995 Apr 16, 2026
3fe5064
Comprehensive E2E tests for ReadConsistencyStrategy across all reques…
jeet1995 Apr 16, 2026
adc17bd
Fix test-resources.json: bump Cosmos DB API version to 2023-04-15
jeet1995 Apr 16, 2026
8e262cb
Test: revert to 2022-08-15 with EnableNoSQLVectorSearch removed
jeet1995 Apr 16, 2026
6e84b99
Fix test-resources.json: bump API version to 2023-04-15 for EnableNoS…
jeet1995 Apr 16, 2026
7991337
Test: re-verify 2022-08-15 + EnableNoSQLVectorSearch (expect 400)
jeet1995 Apr 16, 2026
e00f5bb
Centralize consistency flag contention resolution in RxGatewayStoreModel
jeet1995 Apr 16, 2026
6aa54a5
Add spy-wire tests to verify ReadConsistencyStrategy headers on the w…
jeet1995 Apr 21, 2026
ce56e3e
Fix RNTBD header tests for Byte type + add ThinClient RNTBD spy-wire …
jeet1995 Apr 21, 2026
df06da1
Extract RCS HTTP spy-wire tests into standalone serverless-safe class…
jeet1995 Apr 23, 2026
7e542aa
Rename RCS abbreviation to readConsistencyStrategy throughout (#48094)
jeet1995 Apr 23, 2026
d4357e3
Add spy-wire, mock-pipeline, and thin-client GLOBAL_STRONG tests (#48…
jeet1995 Apr 23, 2026
4458158
Fix unused strategy parameter in RntbdReadConsistencyStrategyHeaderTe…
jeet1995 Apr 23, 2026
e3f4a84
Remove test-output build artifacts from tracking (#48094)
jeet1995 Apr 23, 2026
486347e
Replace FQN GatewayConnectionConfig with import (#48094)
jeet1995 Apr 23, 2026
879552b
Replace FQN HttpRequest with import in RntbdReadConsistencyStrategyHe…
jeet1995 Apr 23, 2026
f05b3c3
Eliminate simulated resolveEffectiveConsistencyHeaders in tests (#48094)
jeet1995 Apr 23, 2026
c2df4d4
Add operation policy E2E tests and document RntbdReadConsistencyStrat…
jeet1995 Apr 24, 2026
8a5b477
Add thin client operation policy E2E tests for readConsistencyStrateg…
jeet1995 Apr 24, 2026
2ecdbd8
Fix RxGatewayStoreModelTest mock verification for retry behavior (#48…
jeet1995 Apr 24, 2026
9bbbcb9
Use Gateway V1/V2 terminology in customer-facing docs (#48094)
jeet1995 Apr 24, 2026
f078479
Fix CHANGELOG/Javadoc: Direct mode was already supported before this …
jeet1995 Apr 24, 2026
83fc115
Add readAllItems E2E tests for readConsistencyStrategy on Gateway V1 …
jeet1995 Apr 24, 2026
ff18029
Replace HashMap copy in applySessionToken with pure isEffectiveSessio…
jeet1995 Apr 24, 2026
8454f70
Refactoring
jeet1995 Apr 27, 2026
d6ba570
Consolidate ReadConsistencyStrategy test coverage into unified spy wi…
jeet1995 Apr 27, 2026
e5418f1
Refactoring
jeet1995 Apr 27, 2026
a275926
Fix unified E2E test: standalone pattern without TestSuiteBase inheri…
jeet1995 Apr 27, 2026
3f21d1d
Move ReadConsistencyStrategy test files to com.azure.cosmos.rx package
jeet1995 Apr 27, 2026
3db0af8
Remove v2Available/gatewayV2Available gates and probe logic
jeet1995 Apr 27, 2026
f9865c5
Rename E2E and spy wire tests to call out gateway scope
jeet1995 Apr 27, 2026
b0ed241
Replace brute-force RNTBD byte scanners with RntbdRequest.decode in u…
jeet1995 Apr 27, 2026
a81fbf3
Refactoring
jeet1995 Apr 27, 2026
4584597
Improve ReadConsistencyStrategy javadoc for customer readability
jeet1995 Apr 27, 2026
b0f6d2b
Refactoring
jeet1995 Apr 27, 2026
52da3ae
Merge remote-tracking branch 'upstream/main' into squad/48094-readcon…
jeet1995 Apr 27, 2026
0b7f706
Add Direct mode to unified E2E test for ReadConsistencyStrategy
jeet1995 Apr 27, 2026
3bbd63f
Address review: treat DEFAULT as transparent, add SESSION E2E tests
jeet1995 Apr 28, 2026
8e5013e
Refactoring
jeet1995 Apr 29, 2026
f0dfc5c
Refactoring
jeet1995 Apr 29, 2026
6fcefa0
Fixing tests.
jeet1995 Apr 29, 2026
af4b7c5
Revert ReadConsistencyStrategy.java javadoc to match Azure/main
jeet1995 Apr 29, 2026
b26d5d9
Merge remote-tracking branch 'upstream/main' into squad/48094-readcon…
jeet1995 May 3, 2026
48c4d3a
Add SYNC NOTE linking resolveEffectiveConsistencyHeaders and isEffect…
jeet1995 May 3, 2026
c8a66f3
Address review: enum-switch, session-consistency tests, javadoc fixes…
jeet1995 May 3, 2026
3bbe9ea
Extract shared resolveEffectiveReadConsistencyStrategy to eliminate d…
jeet1995 May 3, 2026
6b5b813
Merge branch 'main' into squad/48094-readconsistency-header-propagation
jeet1995 Jun 1, 2026
6301061
Merge branch 'main' into squad/48094-readconsistency-header-propagation
jeet1995 Jun 1, 2026
3d07122
Validate ReadConsistencyStrategy regardless of header vs requestConte…
jeet1995 Jun 1, 2026
ff4a43a
Add LATEST_COMMITTED and GLOBAL_STRONG coverage to applySessionToken …
jeet1995 Jun 1, 2026
7b99d9c
Soften thread-safety javadoc on resolveEffectiveConsistencyHeaders
jeet1995 Jun 1, 2026
a71e67d
Deep-copy headers map in RxDocumentServiceRequest.clone() to make hed…
jeet1995 Jun 1, 2026
0e5adae
Merge remote-tracking branch 'upstream/main' into squad/48094-readcon…
jeet1995 Jun 2, 2026
27951d2
Reformat resolveEffectiveConsistencyHeaders javadoc with proper HTML …
jeet1995 Jun 2, 2026
013d5da
Reorder RCS resolution methods caller->callee; tighten helper to priv…
jeet1995 Jun 2, 2026
60c8ea9
Add focused unit test for resolveEffectiveConsistencyHeaders DEFAULT …
jeet1995 Jun 2, 2026
bcc734e
Split GLOBAL_STRONG client-side validation into Breaking Changes sect…
jeet1995 Jun 2, 2026
c88bd76
Revert RequestHelper validation relocation to keep this PR focused on…
jeet1995 Jun 2, 2026
644daf8
Add unit test for RxDocumentServiceRequest.clone() header isolation
jeet1995 Jun 2, 2026
f4ed5ed
Update ReadConsistencyStrategy Javadoc to reflect cross-mode support
jeet1995 Jun 2, 2026
a184efa
Move GLOBAL_STRONG fast-fail validation from Breaking Changes to Othe…
jeet1995 Jun 2, 2026
aa8a9c9
Trim GLOBAL_STRONG CHANGELOG entry wording
jeet1995 Jun 2, 2026
3c0eacb
Cosmetic: drop redundant toString and relocate Logger field
jeet1995 Jun 3, 2026
67499c7
Merge remote-tracking branch 'upstream/main' into squad/48094-readcon…
jeet1995 Jun 3, 2026
2700393
Address PR #48787 review: CHANGELOG placement, RNTBD invariant test, …
jeet1995 Jun 4, 2026
dce3a8b
Extend GatewayReadConsistencyStrategySpyWireTest with Query, ChangeFe…
jeet1995 Jun 4, 2026
b8bf7ef
Fix spy-wire test setup bugs
jeet1995 Jun 4, 2026
48d6204
Pin Http2ConnectionConfig.enabled in V1 spy to make CI deterministic
jeet1995 Jun 4, 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 @@ -417,6 +417,30 @@ public void isValidAddress(String documentUrlWithId, String documentUrlWithName,
assertThat(request.isValidAddress(ResourceType.Unknown)).isTrue();
}

@Test(groups = { "unit" })
public void cloneProducesIndependentHeaderMap() {
RxDocumentServiceRequest original = RxDocumentServiceRequest.createFromName(
mockDiagnosticsClientContext(),
OperationType.Read,
"/dbs/db/colls/coll/docs/doc",
ResourceType.Document);
original.getHeaders().put("x-test-header", "original-value");

RxDocumentServiceRequest cloned = original.clone();

// Mutating the cloned request's headers must not affect the original — guards against
// the HashMap-corruption race fixed in clone() where hedged availability-strategy
// clones previously shared the parent's header map and concurrent mutations during
// resolveEffectiveConsistencyHeaders could trigger a resize and lose entries.
cloned.getHeaders().put("x-test-header", "cloned-value");
cloned.getHeaders().put("x-new-header", "new-value");

assertThat(original.getHeaders().get("x-test-header")).isEqualTo("original-value");
assertThat(original.getHeaders().containsKey("x-new-header")).isFalse();
assertThat(cloned.getHeaders().get("x-test-header")).isEqualTo("cloned-value");
assertThat(cloned.getHeaders().get("x-new-header")).isEqualTo("new-value");
}

@Test(groups = { "unit" })
public void createWithInvalidPath() {
try {
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions sdk/cosmos/azure-cosmos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#### Features Added
* Promoted the Full Fidelity Change Feed (AllVersionsAndDeletes) APIs to GA - See [PR 49283](https://github.com/Azure/azure-sdk-for-java/pull/49283)
* Enabled `ReadConsistencyStrategy` for Gateway V1 (compute gateway) and Gateway V2 (thin client proxy). Previously only supported in Direct mode. - See [PR 48787](https://github.com/Azure/azure-sdk-for-java/pull/48787)

#### Breaking Changes

Expand All @@ -15,6 +16,7 @@
* Replaced per-client `Schedulers.newSingle()` schedulers in `GlobalEndpointManager` and `GlobalPartitionEndpointManagerForPerPartitionCircuitBreaker` with shared `BoundedElastic` schedulers in `CosmosSchedulers` to prevent thread count from scaling linearly with client/tenant count. - See [PR 49062](https://github.com/Azure/azure-sdk-for-java/pull/49062)
* Fixed a sporadic `NullPointerException` in `JsonSerializable.getWithMapping` triggered by concurrent first-time calls to `DatabaseAccount.getConsistencyPolicy()` and its sibling lazy getters (`getReplicationPolicy`, `getSystemReplicationPolicy`, `getQueryEngineConfiguration`). The fix makes `JsonSerializable.propertyBag` `final`, closing an unsafe-publication race in the lazy-initialisation pattern. - See [Issue 49256](https://github.com/Azure/azure-sdk-for-java/issues/49256) and [PR #49258](https://github.com/Azure/azure-sdk-for-java/pull/49258)
* Changed 449 (`Retry With`) retries in Gateway V1 and Gateway V2 to be consistently orchestrated client-side. - See [PR 49332](https://github.com/Azure/azure-sdk-for-java/pull/49332)
* Added client-side fast-fail validation for `ReadConsistencyStrategy.GLOBAL_STRONG`: requests that specify `GLOBAL_STRONG` against an account whose default consistency is not `STRONG` are now rejected client-side with a `BadRequestException` (HTTP 400). - See [PR 48787](https://github.com/Azure/azure-sdk-for-java/pull/48787)

### 4.80.0 (2026-05-01)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* in RequestOptions, CosmosClient or the default consistency level for an account unless
* ReadConsistencyStrategy `DEFAULT` is used.
* <p>
* NOTE: The ReadConsistencyStrategy is currently only working when using direct mode
* NOTE: The ReadConsistencyStrategy is honored across all connection modes.
Comment thread
jeet1995 marked this conversation as resolved.
*/
@Beta(value = Beta.SinceVersion.V4_69_0, warningText = Beta.PREVIEW_SUBJECT_TO_CHANGE_WARNING)
public enum ReadConsistencyStrategy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,26 @@ private RxDocumentServiceRequest createDocumentServiceRequest() {

if (this.options.getReadConsistencyStrategy() != null) {
Comment thread
jeet1995 marked this conversation as resolved.

String readConsistencyStrategyName = options.getReadConsistencyStrategy().toString();
this.client.validateAndLogNonDefaultReadConsistencyStrategy(readConsistencyStrategyName);
headers.put(HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY, readConsistencyStrategyName);
this.client.validateReadConsistencyStrategy(options.getReadConsistencyStrategy());

if (this.options.getReadConsistencyStrategy() != ReadConsistencyStrategy.DEFAULT) {
headers.put(
HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY,
options.getReadConsistencyStrategy().toString());
}

consistencyLevelOverrideApplicable =
this.options.getReadConsistencyStrategy() == ReadConsistencyStrategy.DEFAULT;
}

if (consistencyLevelOverrideApplicable && this.client.getReadConsistencyStrategy() != null) {
String readConsistencyStrategyName = this.client.getReadConsistencyStrategy().toString();
this.client.validateAndLogNonDefaultReadConsistencyStrategy(readConsistencyStrategyName);
headers.put(
HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY,
readConsistencyStrategyName);
this.client.validateReadConsistencyStrategy(this.client.getReadConsistencyStrategy());

if (this.client.getReadConsistencyStrategy() != ReadConsistencyStrategy.DEFAULT) {
headers.put(
HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY,
this.client.getReadConsistencyStrategy().toString());
}

consistencyLevelOverrideApplicable =
this.client.getReadConsistencyStrategy() == ReadConsistencyStrategy.DEFAULT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1988,16 +1988,18 @@ private static void validateResource(Resource resource) {
}
}

public void validateAndLogNonDefaultReadConsistencyStrategy(String readConsistencyStrategyName) {
if (this.connectionPolicy.getConnectionMode() != ConnectionMode.DIRECT
&& readConsistencyStrategyName != null
&& ! readConsistencyStrategyName.equalsIgnoreCase(ReadConsistencyStrategy.DEFAULT.toString())) {

logger.warn(
"ReadConsistencyStrategy {} defined in Gateway mode. "
+ "This version of the SDK only supports ReadConsistencyStrategy in DIRECT mode. "
+ "This setting will be ignored.",
readConsistencyStrategyName);
public void validateReadConsistencyStrategy(ReadConsistencyStrategy readConsistencyStrategy) {
if (readConsistencyStrategy == ReadConsistencyStrategy.GLOBAL_STRONG) {
ConsistencyLevel accountConsistency = this.getDefaultConsistencyLevelOfAccount();
if (accountConsistency != ConsistencyLevel.STRONG) {
throw new BadRequestException(
String.format(
RMResources.ReadConsistencyStrategyGlobalStrongOnlyAllowedForGlobalStrongAccount,
readConsistencyStrategy,
HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY,
ConsistencyLevel.STRONG,
accountConsistency));
}
}
}

Expand Down Expand Up @@ -2029,8 +2031,12 @@ private Map<String, String> getRequestHeaders(RequestOptions options, ResourceTy
&& operationType.isReadOnlyOperation()) {

String readConsistencyStrategyName = readConsistencyStrategy.toString();
this.validateAndLogNonDefaultReadConsistencyStrategy(readConsistencyStrategyName);
this.validateReadConsistencyStrategy(readConsistencyStrategy);
headers.put(HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY, readConsistencyStrategyName);
// Compute gateway rejects requests with both x-ms-consistency-level and
// x-ms-cosmos-read-consistency-strategy headers. When readConsistencyStrategy is set, remove
// consistency-level — readConsistencyStrategy takes precedence.
headers.remove(HttpConstants.HttpHeaders.CONSISTENCY_LEVEL);
}

if (options == null) {
Expand Down Expand Up @@ -2074,13 +2080,20 @@ private Map<String, String> getRequestHeaders(RequestOptions options, ResourceTy
&& operationType.isReadOnlyOperation()) {

String readConsistencyStrategyName = options.getReadConsistencyStrategy().toString();
this.validateAndLogNonDefaultReadConsistencyStrategy(readConsistencyStrategyName);
this.validateReadConsistencyStrategy(options.getReadConsistencyStrategy());
headers.put(
HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY,
readConsistencyStrategyName);
// Compute gateway rejects requests with both x-ms-consistency-level and
// x-ms-cosmos-read-consistency-strategy headers. When readConsistencyStrategy is set, remove
// consistency-level — readConsistencyStrategy takes precedence.
headers.remove(HttpConstants.HttpHeaders.CONSISTENCY_LEVEL);
}

if (options.getConsistencyLevel() != null) {
if (options.getConsistencyLevel() != null
&& !headers.containsKey(HttpConstants.HttpHeaders.READ_CONSISTENCY_STRATEGY)) {
// Only set ConsistencyLevel when ReadConsistencyStrategy is NOT already present.
// readConsistencyStrategy takes precedence — setting both causes gateway rejection.
headers.put(HttpConstants.HttpHeaders.CONSISTENCY_LEVEL, options.getConsistencyLevel().toString());
}

Expand Down Expand Up @@ -5498,8 +5511,8 @@ public ConsistencyLevel getConsistencyLevel() {
}

@Override
public void validateAndLogNonDefaultReadConsistencyStrategy(String readConsistencyStrategyName) {
RxDocumentClientImpl.this.validateAndLogNonDefaultReadConsistencyStrategy(readConsistencyStrategyName);
public void validateReadConsistencyStrategy(ReadConsistencyStrategy readConsistencyStrategy) {
RxDocumentClientImpl.this.validateReadConsistencyStrategy(readConsistencyStrategy);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1050,8 +1050,18 @@ public byte[] getContentAsByteArray() {

@Override
public RxDocumentServiceRequest clone() {
// Deep-copy headers so availability-strategy / hedging clones do not share
// the same HashMap reference with their parent. The shared-reference pattern
// was previously safe-by-convention (assumption: no mutation after clone),
// but resolveEffectiveConsistencyHeaders mutates the map on every request,
// which races with concurrent hedged clones. A racing put can trigger HashMap
// resize and corrupt the map (lost inserts, null reads of unrelated keys).
// Same defensive-copy pattern already used for requestContext and
// faultInjectionRequestContext below.
Map<String, String> sourceHeaders = this.getHeaders();
Map<String, String> clonedHeaders = sourceHeaders != null ? new HashMap<>(sourceHeaders) : null;
Comment thread
jeet1995 marked this conversation as resolved.
RxDocumentServiceRequest rxDocumentServiceRequest = RxDocumentServiceRequest.create(this.clientContext, this.getOperationType(),
this.resourceId, this.isNameBased, this.getResourceType(),this.getHeaders());
this.resourceId, this.isNameBased, this.getResourceType(), clonedHeaders);
rxDocumentServiceRequest.setPartitionKeyInternal(this.getPartitionKeyInternal());
rxDocumentServiceRequest.setContentBytes(this.contentAsByteArray);
rxDocumentServiceRequest.setContinuation(this.getContinuation());
Expand Down
Loading
Loading