From 09e9f582f5673fdfa45cacd0eeaf323dd061c395 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Thu, 4 Dec 2025 10:05:21 -0500 Subject: [PATCH 01/13] Intercept 404-0s to remap to 404-1003s when parent resource is not found. --- .../com/azure/cosmos/CosmosNotFoundTests.java | 279 ++++++++++++++++++ .../implementation/RxGatewayStoreModel.java | 8 +- .../StaleResourceRetryPolicy.java | 15 + .../caches/RxCollectionCache.java | 48 ++- 4 files changed, 347 insertions(+), 3 deletions(-) create mode 100644 sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java new file mode 100644 index 000000000000..a0e8f2c8271c --- /dev/null +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java @@ -0,0 +1,279 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.cosmos; + +import com.azure.cosmos.implementation.HttpConstants; +import com.azure.cosmos.implementation.InternalObjectNode; +import com.azure.cosmos.models.CosmosContainerProperties; +import com.azure.cosmos.models.CosmosItemRequestOptions; +import com.azure.cosmos.models.PartitionKey; +import com.azure.cosmos.models.ThroughputProperties; +import com.azure.cosmos.rx.TestSuiteBase; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +public class CosmosNotFoundTests extends TestSuiteBase { + + private CosmosClient clientDirect; + private CosmosClient clientGateway; + private CosmosContainer existingContainer; + private CosmosDatabase testDatabase; + private String createdItemId; + private String createdItemPk; + + @Factory(dataProvider = "clientBuildersWithDirect") + public CosmosNotFoundTests(CosmosClientBuilder clientBuilder) { + super(clientBuilder); + } + + @DataProvider(name = "connectionModeProvider") + public static Object[][] connectionModeProvider() { + return new Object[][] { + { ConnectionMode.DIRECT, "Direct Connection Mode" }, +// { ConnectionMode.GATEWAY, "Gateway Connection Mode" } + }; + } + + @BeforeClass(groups = {"fast"}, timeOut = SETUP_TIMEOUT) + public void before_CosmosNotFoundTests() { + // Create Direct mode client + assertThat(this.clientDirect).isNull(); + this.clientDirect = getClientBuilder() + .directMode() + .buildClient(); + + // Create Gateway mode client + assertThat(this.clientGateway).isNull(); + this.clientGateway = getClientBuilder() + .gatewayMode() + .buildClient(); + + // Get shared container and create an item in it + CosmosAsyncContainer asyncContainer = getSharedMultiPartitionCosmosContainer(this.clientDirect.asyncClient()); + existingContainer = clientDirect.getDatabase(asyncContainer.getDatabase().getId()) + .getContainer(asyncContainer.getId()); + + // Get/create test database for this test class + CosmosAsyncDatabase asyncDatabase = getSharedCosmosDatabase(this.clientDirect.asyncClient()); + testDatabase = clientDirect.getDatabase(asyncDatabase.getId()); + + // Create a test document + this.createdItemId = UUID.randomUUID().toString(); + this.createdItemPk = UUID.randomUUID().toString(); + InternalObjectNode properties = getDocumentDefinition(createdItemId, createdItemPk); + existingContainer.createItem(properties); + } + + @AfterClass(groups = {"fast"}, timeOut = SHUTDOWN_TIMEOUT, alwaysRun = true) + public void afterClass() { + if (this.clientDirect != null) { + this.clientDirect.close(); + } + if (this.clientGateway != null) { + this.clientGateway.close(); + } + } + + @Test(groups = {"fast"}, dataProvider = "connectionModeProvider", timeOut = TIMEOUT) + public void readItemFromNonExistentContainer(ConnectionMode connectionMode, String testDescription) { + logger.info("Running test: {}", testDescription); + + CosmosClient client = connectionMode == ConnectionMode.DIRECT ? clientDirect : clientGateway; + + // Try to read the item from a non-existent container + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosContainer nonExistentContainer = client + .getDatabase(existingContainer.asyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); + + try { + nonExistentContainer.readItem( + createdItemId, + new PartitionKey(createdItemPk), + new CosmosItemRequestOptions(), + InternalObjectNode.class + ); + fail("Expected CosmosException to be thrown when reading from non-existent container"); + } catch (CosmosException e) { + logger.info("CosmosException caught for {}: StatusCode={}, SubStatusCode={}", + testDescription, e.getStatusCode(), e.getSubStatusCode()); + + // Verify status code is 404 (Not Found) + assertThat(e.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + // Verify sub-status code is either 0 or 1003 + assertThat(e.getSubStatusCode()) + .as("Sub-status code should be 0 or 1003") + .isIn( + HttpConstants.SubStatusCodes.UNKNOWN, + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS + ); + + // Log the diagnostics for debugging purposes + logger.info("Diagnostics: {}", e.getDiagnostics()); + } + } + + @Test(groups = {"fast"}, timeOut = TIMEOUT) + public void readItemFromNonExistentContainerDirectMode() { + logger.info("Running test: Read item from non-existent container in Direct Connection Mode"); + + // Try to read the item from a non-existent container using Direct mode client + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosContainer nonExistentContainer = clientDirect + .getDatabase(existingContainer.asyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); + + try { + nonExistentContainer.readItem( + createdItemId, + new PartitionKey(createdItemPk), + new CosmosItemRequestOptions(), + InternalObjectNode.class + ); + fail("Expected CosmosException to be thrown when reading from non-existent container"); + } catch (CosmosException e) { + logger.info("CosmosException caught (Direct): StatusCode={}, SubStatusCode={}", + e.getStatusCode(), e.getSubStatusCode()); + + assertThat(e.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + assertThat(e.getSubStatusCode()) + .as("Sub-status code should be 0 or 1003") + .isIn( + HttpConstants.SubStatusCodes.UNKNOWN, + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS + ); + + logger.info("Diagnostics: {}", e.getDiagnostics()); + } + } + + @Test(groups = {"fast"}, timeOut = TIMEOUT) + public void readItemFromNonExistentContainerGatewayMode() { + logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); + + // Try to read the item from a non-existent container using Gateway mode client + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosContainer nonExistentContainer = clientGateway + .getDatabase(existingContainer.asyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); + + try { + nonExistentContainer.readItem( + createdItemId, + new PartitionKey(createdItemPk), + new CosmosItemRequestOptions(), + InternalObjectNode.class + ); + fail("Expected CosmosException to be thrown when reading from non-existent container"); + } catch (CosmosException e) { + logger.info("CosmosException caught (Gateway): StatusCode={}, SubStatusCode={}", + e.getStatusCode(), e.getSubStatusCode()); + + assertThat(e.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + assertThat(e.getSubStatusCode()) + .as("Sub-status code should be 0 or 1003") + .isIn( + HttpConstants.SubStatusCodes.UNKNOWN, + 1003 + ); + + logger.info("Diagnostics: {}", e.getDiagnostics()); + } + } + + @Test(groups = {"fast"}, dataProvider = "connectionModeProvider", timeOut = TIMEOUT) + public void readItemFromDeletedContainer(ConnectionMode connectionMode, String testDescription) throws InterruptedException { + logger.info("Running test: Read item from deleted container - {}", testDescription); + + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)); + + CosmosClient clientToUse = connectionMode == ConnectionMode.DIRECT ? clientDirect : clientGateway; + CosmosContainer testContainer = clientToUse.getDatabase(testDatabase.getId()).getContainer(testContainerId); + + Thread.sleep(5000); + + // Create an item in the container + String itemId = UUID.randomUUID().toString(); + String itemPk = UUID.randomUUID().toString(); + InternalObjectNode doc = getDocumentDefinition(itemId, itemPk); + testContainer.createItem(doc); + + // Create a different client instance to delete the container + CosmosClient deletingClient = connectionMode == ConnectionMode.DIRECT + ? getClientBuilder().directMode().buildClient() + : getClientBuilder().gatewayMode().buildClient(); + + try { + // Delete the container using the different client instance + CosmosContainer containerToDelete = deletingClient.getDatabase(testDatabase.getId()).getContainer(testContainerId); + containerToDelete.delete(); + + Thread.sleep(5000); + + // Try to read the item from the deleted container using the original client + try { + testContainer.readItem( + itemId, + new PartitionKey(itemPk), + new CosmosItemRequestOptions(), + InternalObjectNode.class + ); + fail("Expected CosmosException to be thrown when reading from deleted container"); + } catch (CosmosException e) { + logger.info("CosmosException caught for deleted container test ({}): StatusCode={}, SubStatusCode={}", + testDescription, e.getStatusCode(), e.getSubStatusCode()); + + // Verify status code is 404 (Not Found) + assertThat(e.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + // Verify sub-status code is either 0 or 1003 + assertThat(e.getSubStatusCode()) + .as("Sub-status code should be 0 or 1003") + .isIn( + HttpConstants.SubStatusCodes.UNKNOWN, + 1003 + ); + + logger.info("Diagnostics: {}", e.getDiagnostics()); + } + } finally { + deletingClient.close(); + } + } + + private InternalObjectNode getDocumentDefinition(String documentId, String pkId) { + final String uuid = UUID.randomUUID().toString(); + final InternalObjectNode properties = new InternalObjectNode(String.format( + "{ " + + "\"id\": \"%s\", " + + "\"mypk\": \"%s\", " + + "\"sgmts\": [[6519456, 1471916863], [2498434, 1455671440]], " + + "\"prop\": \"%s\"" + + "}" + , documentId, pkId, uuid)); + return properties; + } +} diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java index 906fc518f2f7..db9bcdc5fd99 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java @@ -316,7 +316,13 @@ private Mono performRequestInternalCore(RxDocumentSer .getEffectiveHttpTransportSerializer(this) .wrapInHttpRequest(request, requestUri); - Mono httpResponseMono = this.httpClient.send(httpRequest, request.getResponseTimeout()); + Mono httpResponseMono = null; + + if (ResourceType.DocumentCollection.equals(request.getResourceType())) { + httpResponseMono = this.httpClient.send(httpRequest, request.getResponseTimeout()); + } else { + httpResponseMono = this.httpClient.send(httpRequest, request.getResponseTimeout()); + } if (this.gatewayServerErrorInjector != null) { httpResponseMono = this.gatewayServerErrorInjector.injectGatewayErrors(request.getResponseTimeout(), diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java index 090f119abbca..0daeaf718259 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java @@ -120,6 +120,21 @@ public Mono shouldRetry(Exception e) { } return Mono.just(ShouldRetryResult.retryAfter(Duration.ZERO)); + }) + .onErrorMap(throwable -> { + + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + + if (!ResourceType.DocumentCollection.equals(this.request.getResourceType()) && Exceptions.isNotFound(cosmosException)) { + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + return cosmosException; + } + + return throwable; }); } else { diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index b8db3cad7400..129961ae2ed6 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -3,14 +3,17 @@ package com.azure.cosmos.implementation.caches; import com.azure.cosmos.BridgeInternal; +import com.azure.cosmos.CosmosException; import com.azure.cosmos.implementation.CosmosClientMetadataCachesSnapshot; import com.azure.cosmos.implementation.DocumentCollection; +import com.azure.cosmos.implementation.HttpConstants; import com.azure.cosmos.implementation.InvalidPartitionException; import com.azure.cosmos.implementation.MetadataDiagnosticsContext; import com.azure.cosmos.implementation.NotFoundException; import com.azure.cosmos.implementation.PathsHelper; import com.azure.cosmos.implementation.RMResources; import com.azure.cosmos.implementation.ResourceId; +import com.azure.cosmos.implementation.ResourceType; import com.azure.cosmos.implementation.RxDocumentServiceRequest; import com.azure.cosmos.implementation.Utils; import com.azure.cosmos.implementation.apachecommons.lang.StringUtils; @@ -85,9 +88,36 @@ public Mono> resolveCollectionAsync( request.requestContext.resolvedCollectionRid = collection.getResourceId(); return Mono.just(new Utils.ValueHolder<>(collection)); - }); + }).onErrorMap(throwable -> { + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + + if (ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + return cosmosException; + } + + return throwable; + }); } else { - return this.resolveByRidAsync(metaDataDiagnosticsContext, request.requestContext.resolvedCollectionRid, request.properties); + return this.resolveByRidAsync(metaDataDiagnosticsContext, request.requestContext.resolvedCollectionRid, request.properties) + .onErrorMap(throwable -> { + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + + if (ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + return cosmosException; + } + + return throwable; + }); } }); } else { @@ -99,6 +129,20 @@ public Mono> resolveCollectionAsync( } return this.resolveByRidAsync(metaDataDiagnosticsContext, request.getResourceAddress(), request.properties); + }) + .onErrorMap(throwable -> { + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + + if (ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + return cosmosException; + } + + return throwable; }); } } From 6174bccf57c587ef50353b02da7774690d8691f9 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Fri, 26 Dec 2025 01:54:49 -0500 Subject: [PATCH 02/13] Intercept 404-0s to remap to 404-1003s when parent resource is not found. --- .../caches/RxCollectionCache.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index 129961ae2ed6..54129a9f0429 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -93,7 +93,7 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } @@ -109,7 +109,7 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } @@ -135,7 +135,7 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } @@ -200,7 +200,9 @@ public Mono> resolveByRidAsync( Mono async = this.collectionInfoByIdCache.getAsync( collectionResourceId, null, - () -> this.getByRidAsync(metaDataDiagnosticsContext, collectionResourceId, properties)); + () -> this.getByRidAsync(metaDataDiagnosticsContext, collectionResourceId, properties).onErrorMap(throwable -> { + return throwable; + })); return async.map(Utils.ValueHolder::new); } @@ -224,7 +226,9 @@ public Mono resolveByNameAsync( () -> { Mono collectionObs = this.getByNameAsync( metaDataDiagnosticsContext, resourceFullName, properties); - return collectionObs.doOnSuccess(collection -> this.collectionInfoByIdCache.set( + return collectionObs.onErrorMap(throwable -> { + return throwable; + }).doOnSuccess(collection -> this.collectionInfoByIdCache.set( collection.getResourceId(), collection)); }); @@ -246,7 +250,9 @@ public Mono refreshAsync(MetadataDiagnosticsContext metaDataDiagnosticsCon obsoleteValue, () -> { Mono collectionObs = this.getByNameAsync(metaDataDiagnosticsContext, resourceFullName, request.properties); - return collectionObs.doOnSuccess(collection -> { + return collectionObs.onErrorMap(throwable -> { + return throwable; + }).doOnSuccess(collection -> { this.collectionInfoByIdCache.set(collection.getResourceId(), collection); }); }).then(); From 049e70754d60bf8cced2d1d474972daa176a3535 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 10:35:17 +0400 Subject: [PATCH 03/13] Intercept 404-0s to remap to 404-1003s when parent resource is not found. --- .../com/azure/cosmos/CosmosNotFoundTests.java | 363 +++++++++--------- .../FaultInjectionTestBase.java | 76 +++- ...StaleResourceExceptionRetryPolicyTest.java | 12 +- .../implementation/RxDocumentClientImpl.java | 60 ++- .../StaleResourceRetryPolicy.java | 19 +- .../query/QueryPlanRetriever.java | 19 +- 6 files changed, 350 insertions(+), 199 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java index a0e8f2c8271c..cd9a5d4a1ae5 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java @@ -3,13 +3,13 @@ package com.azure.cosmos; +import com.azure.cosmos.faultinjection.FaultInjectionTestBase; import com.azure.cosmos.implementation.HttpConstants; -import com.azure.cosmos.implementation.InternalObjectNode; +import com.azure.cosmos.implementation.ImplementationBridgeHelpers; +import com.azure.cosmos.implementation.OperationType; +import com.azure.cosmos.implementation.TestConfigurations; import com.azure.cosmos.models.CosmosContainerProperties; -import com.azure.cosmos.models.CosmosItemRequestOptions; -import com.azure.cosmos.models.PartitionKey; import com.azure.cosmos.models.ThroughputProperties; -import com.azure.cosmos.rx.TestSuiteBase; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; @@ -19,261 +19,280 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; -public class CosmosNotFoundTests extends TestSuiteBase { +public class CosmosNotFoundTests extends FaultInjectionTestBase { - private CosmosClient clientDirect; - private CosmosClient clientGateway; - private CosmosContainer existingContainer; - private CosmosDatabase testDatabase; - private String createdItemId; + private static final ImplementationBridgeHelpers.CosmosAsyncClientHelper.CosmosAsyncClientAccessor accessor = + ImplementationBridgeHelpers.CosmosAsyncClientHelper.getCosmosAsyncClientAccessor(); + + private CosmosAsyncClient commonAsyncClient; + private CosmosAsyncContainer existingAsyncContainer; + private CosmosAsyncDatabase testAsyncDatabase; private String createdItemPk; + private TestObject objectToCreate; - @Factory(dataProvider = "clientBuildersWithDirect") + @Factory(dataProvider = "simpleClientBuildersWithoutRetryOnThrottledRequests") public CosmosNotFoundTests(CosmosClientBuilder clientBuilder) { super(clientBuilder); } - @DataProvider(name = "connectionModeProvider") - public static Object[][] connectionModeProvider() { - return new Object[][] { - { ConnectionMode.DIRECT, "Direct Connection Mode" }, -// { ConnectionMode.GATEWAY, "Gateway Connection Mode" } - }; - } - @BeforeClass(groups = {"fast"}, timeOut = SETUP_TIMEOUT) public void before_CosmosNotFoundTests() { - // Create Direct mode client - assertThat(this.clientDirect).isNull(); - this.clientDirect = getClientBuilder() - .directMode() - .buildClient(); - - // Create Gateway mode client - assertThat(this.clientGateway).isNull(); - this.clientGateway = getClientBuilder() - .gatewayMode() - .buildClient(); + this.commonAsyncClient = getClientBuilder().buildAsyncClient(); // Get shared container and create an item in it - CosmosAsyncContainer asyncContainer = getSharedMultiPartitionCosmosContainer(this.clientDirect.asyncClient()); - existingContainer = clientDirect.getDatabase(asyncContainer.getDatabase().getId()) + CosmosAsyncContainer asyncContainer = getSharedMultiPartitionCosmosContainer(this.commonAsyncClient); + this.existingAsyncContainer = this.commonAsyncClient.getDatabase(asyncContainer.getDatabase().getId()) .getContainer(asyncContainer.getId()); // Get/create test database for this test class - CosmosAsyncDatabase asyncDatabase = getSharedCosmosDatabase(this.clientDirect.asyncClient()); - testDatabase = clientDirect.getDatabase(asyncDatabase.getId()); + CosmosAsyncDatabase asyncDatabase = getSharedCosmosDatabase(this.commonAsyncClient); + this.testAsyncDatabase = this.commonAsyncClient.getDatabase(asyncDatabase.getId()); // Create a test document - this.createdItemId = UUID.randomUUID().toString(); this.createdItemPk = UUID.randomUUID().toString(); - InternalObjectNode properties = getDocumentDefinition(createdItemId, createdItemPk); - existingContainer.createItem(properties); + + TestObject testObject = TestObject.create(this.createdItemPk); + + this.existingAsyncContainer.createItem(testObject).block(); + this.objectToCreate = testObject; + } + + @DataProvider(name = "operationTypeProvider") + public static Object[][] operationTypeProvider() { + return new Object[][]{ + {OperationType.Read}, + {OperationType.Replace}, + {OperationType.Query}, + {OperationType.ReadFeed} + }; } @AfterClass(groups = {"fast"}, timeOut = SHUTDOWN_TIMEOUT, alwaysRun = true) public void afterClass() { - if (this.clientDirect != null) { - this.clientDirect.close(); - } - if (this.clientGateway != null) { - this.clientGateway.close(); - } + safeClose(this.commonAsyncClient); } - @Test(groups = {"fast"}, dataProvider = "connectionModeProvider", timeOut = TIMEOUT) - public void readItemFromNonExistentContainer(ConnectionMode connectionMode, String testDescription) { - logger.info("Running test: {}", testDescription); + @Test(groups = {"fast"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) + public void performDocumentOperationOnNonExistentContainer(OperationType operationType) { - CosmosClient client = connectionMode == ConnectionMode.DIRECT ? clientDirect : clientGateway; + CosmosAsyncClient asyncClientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); // Try to read the item from a non-existent container String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosContainer nonExistentContainer = client - .getDatabase(existingContainer.asyncContainer.getDatabase().getId()) + CosmosAsyncContainer nonExistentContainer = asyncClientToUse + .getDatabase(existingAsyncContainer.getDatabase().getId()) .getContainer(nonExistentContainerId); try { - nonExistentContainer.readItem( - createdItemId, - new PartitionKey(createdItemPk), - new CosmosItemRequestOptions(), - InternalObjectNode.class - ); - fail("Expected CosmosException to be thrown when reading from non-existent container"); - } catch (CosmosException e) { - logger.info("CosmosException caught for {}: StatusCode={}, SubStatusCode={}", - testDescription, e.getStatusCode(), e.getSubStatusCode()); + + CosmosDiagnostics cosmosDiagnostics = this.performDocumentOperation(nonExistentContainer, operationType, this.objectToCreate, false, false, true); + + assertThat(cosmosDiagnostics).isNotNull(); + assertThat(cosmosDiagnostics.getDiagnosticsContext()).isNotNull(); + + CosmosDiagnosticsContext diagnosticsContext = cosmosDiagnostics.getDiagnosticsContext(); // Verify status code is 404 (Not Found) - assertThat(e.getStatusCode()) + assertThat(diagnosticsContext.getStatusCode()) .as("Status code should be 404 (Not Found)") .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); // Verify sub-status code is either 0 or 1003 - assertThat(e.getSubStatusCode()) - .as("Sub-status code should be 0 or 1003") + assertThat(diagnosticsContext.getSubStatusCode()) + .as("Sub-status code should be 1003") .isIn( - HttpConstants.SubStatusCodes.UNKNOWN, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS ); - // Log the diagnostics for debugging purposes - logger.info("Diagnostics: {}", e.getDiagnostics()); + } finally { + safeClose(asyncClientToUse); } } - @Test(groups = {"fast"}, timeOut = TIMEOUT) - public void readItemFromNonExistentContainerDirectMode() { - logger.info("Running test: Read item from non-existent container in Direct Connection Mode"); + @Test(groups = {"thin-client-multi-region"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) + public void performDocumentOperationOnNonExistentContainerGatewayModeV2(OperationType operationType) { + logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); - // Try to read the item from a non-existent container using Direct mode client + // Try to read the item from a non-existent container using Gateway mode client String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosContainer nonExistentContainer = clientDirect - .getDatabase(existingContainer.asyncContainer.getDatabase().getId()) + + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + + CosmosAsyncClient v2GatewayAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(new GatewayConnectionConfig().setHttp2ConnectionConfig(new Http2ConnectionConfig().setEnabled(true))) + .buildAsyncClient(); + + CosmosAsyncContainer nonExistentContainer = v2GatewayAsyncClient + .getDatabase(existingAsyncContainer.getDatabase().getId()) .getContainer(nonExistentContainerId); try { - nonExistentContainer.readItem( - createdItemId, - new PartitionKey(createdItemPk), - new CosmosItemRequestOptions(), - InternalObjectNode.class - ); - fail("Expected CosmosException to be thrown when reading from non-existent container"); - } catch (CosmosException e) { - logger.info("CosmosException caught (Direct): StatusCode={}, SubStatusCode={}", - e.getStatusCode(), e.getSubStatusCode()); - - assertThat(e.getStatusCode()) + CosmosDiagnostics cosmosDiagnostics = this.performDocumentOperation(nonExistentContainer, operationType, this.objectToCreate, false, false, true); + + assertThat(cosmosDiagnostics).isNotNull(); + assertThat(cosmosDiagnostics.getDiagnosticsContext()).isNotNull(); + + CosmosDiagnosticsContext diagnosticsContext = cosmosDiagnostics.getDiagnosticsContext(); + + // Verify status code is 404 (Not Found) + assertThat(diagnosticsContext.getStatusCode()) .as("Status code should be 404 (Not Found)") .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); - assertThat(e.getSubStatusCode()) - .as("Sub-status code should be 0 or 1003") + // Verify sub-status code is 1003 + assertThat(diagnosticsContext.getSubStatusCode()) + .as("Sub-status code should be 1003") .isIn( - HttpConstants.SubStatusCodes.UNKNOWN, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS ); - - logger.info("Diagnostics: {}", e.getDiagnostics()); + } finally { + safeClose(v2GatewayAsyncClient); + System.clearProperty("COSMOS.THINCLIENT_ENABLED"); } } - @Test(groups = {"fast"}, timeOut = TIMEOUT) - public void readItemFromNonExistentContainerGatewayMode() { - logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); + @Test(groups = {"fast"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) + public void performDocumentOperationOnDeletedContainer(OperationType operationType) throws InterruptedException { - // Try to read the item from a non-existent container using Gateway mode client - String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosContainer nonExistentContainer = clientGateway - .getDatabase(existingContainer.asyncContainer.getDatabase().getId()) - .getContainer(nonExistentContainerId); + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + + CosmosAsyncClient clientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); + + CosmosAsyncContainer testContainer = clientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + + Thread.sleep(5000); + + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + testContainer.createItem(testObject).block(); + + // Create a different client instance to delete the container + CosmosAsyncClient deletingAsyncClient = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); try { - nonExistentContainer.readItem( - createdItemId, - new PartitionKey(createdItemPk), - new CosmosItemRequestOptions(), - InternalObjectNode.class - ); - fail("Expected CosmosException to be thrown when reading from non-existent container"); - } catch (CosmosException e) { - logger.info("CosmosException caught (Gateway): StatusCode={}, SubStatusCode={}", - e.getStatusCode(), e.getSubStatusCode()); - - assertThat(e.getStatusCode()) + // Delete the container using the different client instance + CosmosAsyncContainer containerToDelete = deletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + containerToDelete.delete().block(); + + Thread.sleep(5000); + + // Try to read the item from the deleted container using the original client + + CosmosDiagnostics cosmosDiagnostics = this.performDocumentOperation(testContainer, operationType, this.objectToCreate, false, false, true); + + assertThat(cosmosDiagnostics).isNotNull(); + assertThat(cosmosDiagnostics.getDiagnosticsContext()).isNotNull(); + + CosmosDiagnosticsContext diagnosticsContext = cosmosDiagnostics.getDiagnosticsContext(); + + // Verify status code is 404 (Not Found) + assertThat(diagnosticsContext.getStatusCode()) .as("Status code should be 404 (Not Found)") .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); - assertThat(e.getSubStatusCode()) - .as("Sub-status code should be 0 or 1003") - .isIn( - HttpConstants.SubStatusCodes.UNKNOWN, - 1003 - ); + // Verify sub-status code is either 0 or 1003 - logger.info("Diagnostics: {}", e.getDiagnostics()); + if (ConnectionMode.DIRECT.name().equals(accessor.getConnectionMode(clientToUse))) { + assertThat(diagnosticsContext.getSubStatusCode()) + .as("Sub-status code should be 1003") + .isIn( + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS + ); + } + + if (ConnectionMode.GATEWAY.name().equals(accessor.getConnectionMode(clientToUse))) { + assertThat(diagnosticsContext.getSubStatusCode()) + .as("Sub-status code should be 0") + .isIn( + HttpConstants.SubStatusCodes.UNKNOWN + ); + } + } finally { + safeClose(clientToUse); + safeClose(deletingAsyncClient); } } - @Test(groups = {"fast"}, dataProvider = "connectionModeProvider", timeOut = TIMEOUT) - public void readItemFromDeletedContainer(ConnectionMode connectionMode, String testDescription) throws InterruptedException { - logger.info("Running test: Read item from deleted container - {}", testDescription); + @Test(groups = {"thin-client-multi-region"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) + public void performDocumentOperationOnDeletedContainerWithGatewayV2(OperationType operationType) throws InterruptedException { + logger.info("Running test: Read item from deleted container - Gateway V2 Connection Mode"); // Create a dedicated container for this test String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); - testDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + Http2ConnectionConfig http2ConnectionConfig = new Http2ConnectionConfig().setEnabled(true); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + gatewayConnectionConfig.setHttp2ConnectionConfig(http2ConnectionConfig); + + CosmosAsyncClient gatewayV2AsyncClientToUse = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(gatewayConnectionConfig) + .buildAsyncClient(); + CosmosAsyncClient containerDeletingAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode() + .buildAsyncClient(); - CosmosClient clientToUse = connectionMode == ConnectionMode.DIRECT ? clientDirect : clientGateway; - CosmosContainer testContainer = clientToUse.getDatabase(testDatabase.getId()).getContainer(testContainerId); + CosmosAsyncContainer testContainer = gatewayV2AsyncClientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); Thread.sleep(5000); // Create an item in the container - String itemId = UUID.randomUUID().toString(); - String itemPk = UUID.randomUUID().toString(); - InternalObjectNode doc = getDocumentDefinition(itemId, itemPk); - testContainer.createItem(doc); - - // Create a different client instance to delete the container - CosmosClient deletingClient = connectionMode == ConnectionMode.DIRECT - ? getClientBuilder().directMode().buildClient() - : getClientBuilder().gatewayMode().buildClient(); + TestObject testObject = TestObject.create(this.createdItemPk); + testContainer.createItem(testObject).block(); try { // Delete the container using the different client instance - CosmosContainer containerToDelete = deletingClient.getDatabase(testDatabase.getId()).getContainer(testContainerId); - containerToDelete.delete(); + CosmosAsyncContainer asyncContainerToDelete = containerDeletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + asyncContainerToDelete.delete().block(); Thread.sleep(5000); // Try to read the item from the deleted container using the original client - try { - testContainer.readItem( - itemId, - new PartitionKey(itemPk), - new CosmosItemRequestOptions(), - InternalObjectNode.class + CosmosDiagnostics cosmosDiagnostics = this.performDocumentOperation(testContainer, operationType, this.objectToCreate, false, false, true); + + assertThat(cosmosDiagnostics).isNotNull(); + assertThat(cosmosDiagnostics.getDiagnosticsContext()).isNotNull(); + + CosmosDiagnosticsContext diagnosticsContext = cosmosDiagnostics.getDiagnosticsContext(); + + // Verify status code is 404 (Not Found) + assertThat(diagnosticsContext.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + // Verify sub-status code is either 0 or 1003 + assertThat(diagnosticsContext.getSubStatusCode()) + .as("Sub-status code should be 0 or 1003") + .isIn( + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS ); - fail("Expected CosmosException to be thrown when reading from deleted container"); - } catch (CosmosException e) { - logger.info("CosmosException caught for deleted container test ({}): StatusCode={}, SubStatusCode={}", - testDescription, e.getStatusCode(), e.getSubStatusCode()); - - // Verify status code is 404 (Not Found) - assertThat(e.getStatusCode()) - .as("Status code should be 404 (Not Found)") - .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); - - // Verify sub-status code is either 0 or 1003 - assertThat(e.getSubStatusCode()) - .as("Sub-status code should be 0 or 1003") - .isIn( - HttpConstants.SubStatusCodes.UNKNOWN, - 1003 - ); - logger.info("Diagnostics: {}", e.getDiagnostics()); - } } finally { - deletingClient.close(); - } - } + safeClose(gatewayV2AsyncClientToUse); + safeClose(containerDeletingAsyncClient); - private InternalObjectNode getDocumentDefinition(String documentId, String pkId) { - final String uuid = UUID.randomUUID().toString(); - final InternalObjectNode properties = new InternalObjectNode(String.format( - "{ " - + "\"id\": \"%s\", " - + "\"mypk\": \"%s\", " - + "\"sgmts\": [[6519456, 1471916863], [2498434, 1455671440]], " - + "\"prop\": \"%s\"" - + "}" - , documentId, pkId, uuid)); - return properties; + System.clearProperty("COSMOS.THINCLIENT_ENABLED"); + } } } diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/faultinjection/FaultInjectionTestBase.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/faultinjection/FaultInjectionTestBase.java index 33f14591ea69..74a710fdaead 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/faultinjection/FaultInjectionTestBase.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/faultinjection/FaultInjectionTestBase.java @@ -10,18 +10,25 @@ import com.azure.cosmos.TestObject; import com.azure.cosmos.implementation.OperationType; import com.azure.cosmos.models.CosmosBatch; +import com.azure.cosmos.models.CosmosBulkOperationResponse; +import com.azure.cosmos.models.CosmosBulkOperations; import com.azure.cosmos.models.CosmosChangeFeedRequestOptions; import com.azure.cosmos.models.CosmosItemIdentity; +import com.azure.cosmos.models.CosmosItemOperation; import com.azure.cosmos.models.CosmosPatchOperations; import com.azure.cosmos.models.CosmosQueryRequestOptions; import com.azure.cosmos.models.FeedRange; import com.azure.cosmos.models.FeedResponse; import com.azure.cosmos.models.PartitionKey; import com.azure.cosmos.rx.TestSuiteBase; +import reactor.core.publisher.Flux; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + public abstract class FaultInjectionTestBase extends TestSuiteBase { public FaultInjectionTestBase(CosmosClientBuilder cosmosClientBuilder) { super(cosmosClientBuilder); @@ -32,6 +39,24 @@ protected CosmosDiagnostics performDocumentOperation( OperationType operationType, TestObject createdItem, boolean isReadMany) { + + return performDocumentOperation( + cosmosAsyncContainer, + operationType, + createdItem, + isReadMany, + true, + false); + } + + protected CosmosDiagnostics performDocumentOperation( + CosmosAsyncContainer cosmosAsyncContainer, + OperationType operationType, + TestObject createdItem, + boolean isReadMany, + boolean fetchFeedRangesBeforehandForChangeFeed, + boolean isBulkOperation) { + try { if (operationType == OperationType.Query && !isReadMany) { CosmosQueryRequestOptions queryRequestOptions = new CosmosQueryRequestOptions(); @@ -87,24 +112,61 @@ protected CosmosDiagnostics performDocumentOperation( } if (operationType == OperationType.Batch) { - CosmosBatch batch = CosmosBatch.createCosmosBatch(new PartitionKey(createdItem.getId())); - batch.upsertItemOperation(createdItem); - batch.readItemOperation(createdItem.getId()); + if (isBulkOperation) { + + List cosmosItemOperations = new ArrayList<>(); + + CosmosItemOperation cosmosItemOperation = CosmosBulkOperations.getReadItemOperation( + createdItem.getId(), + new PartitionKey(createdItem.getId()), + TestObject.class); + + cosmosItemOperations.add(cosmosItemOperation); - return cosmosAsyncContainer.executeCosmosBatch(batch).block().getDiagnostics(); + Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); + + CosmosBulkOperationResponse response = cosmosAsyncContainer.executeBulkOperations(operationsFlux).blockLast(); + + assertThat(response).isNotNull(); + + return response.getResponse().getCosmosDiagnostics(); + } else { + CosmosBatch batch = CosmosBatch.createCosmosBatch(new PartitionKey(createdItem.getId())); + + batch.upsertItemOperation(createdItem); + batch.readItemOperation(createdItem.getId()); + + return cosmosAsyncContainer.executeCosmosBatch(batch).block().getDiagnostics(); + } } } if (operationType == OperationType.ReadFeed) { - List feedRanges = cosmosAsyncContainer.getFeedRanges().block(); + + if (fetchFeedRangesBeforehandForChangeFeed) { + List feedRanges = cosmosAsyncContainer.getFeedRanges().block(); + + assertThat(feedRanges).isNotNull(); + assertThat(feedRanges.size()).isGreaterThan(0); + + CosmosChangeFeedRequestOptions changeFeedRequestOptions = + CosmosChangeFeedRequestOptions.createForProcessingFromBeginning(feedRanges.get(0)); + + FeedResponse firstPage = cosmosAsyncContainer + .queryChangeFeed(changeFeedRequestOptions, TestObject.class) + .byPage() + .blockLast(); + return firstPage.getCosmosDiagnostics(); + } + CosmosChangeFeedRequestOptions changeFeedRequestOptions = - CosmosChangeFeedRequestOptions.createForProcessingFromBeginning(feedRanges.get(0)); + CosmosChangeFeedRequestOptions.createForProcessingFromBeginning(FeedRange.forFullRange()); FeedResponse firstPage = cosmosAsyncContainer .queryChangeFeed(changeFeedRequestOptions, TestObject.class) .byPage() - .blockFirst(); + .blockLast(); return firstPage.getCosmosDiagnostics(); } diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java index ee7afbe8ac41..032ecd7752e8 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java @@ -51,7 +51,9 @@ public void staledException(int statusCode, int subStatusCode, boolean expectRet null, null, sessionContainer, - TestUtils.mockDiagnosticsClientContext() + TestUtils.mockDiagnosticsClientContext(), + null, + null ); CosmosException exception = BridgeInternal.createCosmosException(statusCode); @@ -88,7 +90,9 @@ public void suppressRetryForExternalCollectionRid() { null, customHeaders, sessionContainer, - TestUtils.mockDiagnosticsClientContext() + TestUtils.mockDiagnosticsClientContext(), + null, + null ); InvalidPartitionException invalidPartitionException = new InvalidPartitionException(); @@ -125,7 +129,9 @@ public void cleanSessionToken() { null, null, sessionContainer, - TestUtils.mockDiagnosticsClientContext() + TestUtils.mockDiagnosticsClientContext(), + null, + null ); InvalidPartitionException invalidPartitionException = new InvalidPartitionException(); diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java index 2acea4e9db57..0748b119f593 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java @@ -1272,7 +1272,9 @@ private Flux> createQuery( qryOptAccessor.getProperties(nonNullQueryOptions), qryOptAccessor.getHeaders(nonNullQueryOptions), this.sessionContainer, - diagnosticsFactory); + diagnosticsFactory, + ResourceType.Document, + OperationType.Query); return ObservableHelper.fluxInlineIfPossibleAsObs( @@ -2605,7 +2607,8 @@ private Mono> createDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - collectionLink); + collectionLink, + OperationType.Create); AtomicReference requestReference = new AtomicReference<>(); @@ -2992,7 +2995,8 @@ private Mono> upsertDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - collectionLink);; + collectionLink, + OperationType.Upsert); AtomicReference requestReference = new AtomicReference<>(); Consumer gwModeE2ETimeoutDiagnosticHandler @@ -3134,7 +3138,8 @@ private Mono> replaceDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink)); + Utils.getCollectionName(documentLink), + OperationType.Replace); AtomicReference requestReference = new AtomicReference<>(); Consumer gwModeE2ETimeoutDiagnosticHandler @@ -3467,7 +3472,8 @@ private Mono> patchDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink)); + Utils.getCollectionName(documentLink), + OperationType.Patch); AtomicReference requestReference = new AtomicReference<>(); @@ -3680,7 +3686,8 @@ private Mono> deleteDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink)); + Utils.getCollectionName(documentLink), + OperationType.Delete); AtomicReference requestReference = new AtomicReference<>(); @@ -3872,7 +3879,8 @@ private Mono> readDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink)); + Utils.getCollectionName(documentLink), + OperationType.Read); AtomicReference requestReference = new AtomicReference<>(); @@ -4021,7 +4029,9 @@ public Mono> readMany( qryOptAccessor.getProperties(state.getQueryOptions()), qryOptAccessor.getHeaders(state.getQueryOptions()), this.sessionContainer, - diagnosticsFactory); + diagnosticsFactory, + ResourceType.Document, + OperationType.Query); return ObservableHelper .inlineIfPossibleAsObs( @@ -4718,7 +4728,9 @@ public Flux> queryDocumentChangeFeedFromPagedFlux( changeFeedOptionsAccessor.getProperties(state.getChangeFeedOptions()), changeFeedOptionsAccessor.getHeaders(state.getChangeFeedOptions()), this.sessionContainer, - diagnosticsFactory); + diagnosticsFactory, + ResourceType.Document, + OperationType.ReadFeed); return ObservableHelper .fluxInlineIfPossibleAsObs( @@ -4743,6 +4755,17 @@ private Flux> queryDocumentChangeFeedFromPagedFluxInternal( return this.getCollectionCache() .resolveByNameAsync(null, collectionLink, null) + .onErrorMap(throwable -> { + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + + return cosmosException; + } + + return throwable; + }) .flatMapMany(collection -> { if (collection == null) { throw new IllegalStateException("Collection can not be null"); @@ -4863,7 +4886,9 @@ public Flux> readAllDocuments( qryOptAccessor.getProperties(effectiveOptions), qryOptAccessor.getHeaders(effectiveOptions), this.sessionContainer, - diagnosticsFactory); + diagnosticsFactory, + ResourceType.Document, + OperationType.Query); Flux> innerFlux = ObservableHelper.fluxInlineIfPossibleAsObs( () -> { @@ -5192,7 +5217,9 @@ public Mono executeBatchRequest(String collectionLink, nonNullRequestOptions.getProperties(), nonNullRequestOptions.getHeaders(), this.sessionContainer, - scopedDiagnosticsFactory); + scopedDiagnosticsFactory, + ResourceType.Document, + OperationType.Batch); } final DocumentClientRetryPolicy finalRetryPolicy = documentClientRetryPolicy; @@ -6629,7 +6656,9 @@ public Mono> getFeedRanges(String collectionLink, boolean forceR new HashMap<>(), new HashMap<>(), this.sessionContainer, - null); + null, + ResourceType.PartitionKeyRange, + OperationType.ReadFeed); RxDocumentServiceRequest request = RxDocumentServiceRequest.create( this, @@ -8000,7 +8029,8 @@ private boolean useThinClientStoreModel(RxDocumentServiceRequest request) { private DocumentClientRetryPolicy getRetryPolicyForPointOperation( DiagnosticsClientContext diagnosticsClientContext, RequestOptions requestOptions, - String collectionLink) { + String collectionLink, + OperationType operationType) { checkNotNull(requestOptions, "Argument 'requestOptions' can not be null"); @@ -8016,7 +8046,9 @@ private DocumentClientRetryPolicy getRetryPolicyForPointOperation( requestOptions.getProperties(), requestOptions.getHeaders(), this.sessionContainer, - diagnosticsClientContext); + diagnosticsClientContext, + ResourceType.Document, + operationType); return requestRetryPolicy; } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java index 0daeaf718259..aa10ae52998c 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java @@ -33,6 +33,8 @@ public class StaleResourceRetryPolicy extends DocumentClientRetryPolicy { private RxDocumentServiceRequest request; private final DiagnosticsClientContext diagnosticsClientContext; private final AtomicReference cosmosDiagnosticsHolder; + private final ResourceType enclosingOperationTargetResourceType; + private final OperationType enclosingOperationType; private volatile boolean retried = false; @@ -43,7 +45,9 @@ public StaleResourceRetryPolicy( Map requestOptionProperties, Map requestCustomHeaders, ISessionContainer sessionContainer, - DiagnosticsClientContext diagnosticsClientContext) { + DiagnosticsClientContext diagnosticsClientContext, + ResourceType enclosingOperationTargetResourceType, + OperationType enclosingOperationType) { this.clientCollectionCache = collectionCache; this.nextPolicy = nextPolicy; @@ -56,6 +60,9 @@ public StaleResourceRetryPolicy( this.diagnosticsClientContext = diagnosticsClientContext; this.cosmosDiagnosticsHolder = new AtomicReference<>(null); // will only create one if no request is bound to the retry policy + + this.enclosingOperationTargetResourceType = enclosingOperationTargetResourceType; + this.enclosingOperationType = enclosingOperationType; } @Override @@ -127,8 +134,16 @@ public Mono shouldRetry(Exception e) { CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (!ResourceType.DocumentCollection.equals(this.request.getResourceType()) && Exceptions.isNotFound(cosmosException)) { + if (this.request != null && !ResourceType.DocumentCollection.equals(this.request.getResourceType()) && Exceptions.isNotFound(cosmosException)) { + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + + return cosmosException; + } + + if (this.enclosingOperationTargetResourceType != null && !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType)) { BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + + return cosmosException; } return cosmosException; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java index bb3499548968..e199ae63a268 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java @@ -5,10 +5,12 @@ import com.azure.cosmos.BridgeInternal; import com.azure.cosmos.CosmosEndToEndOperationLatencyPolicyConfig; +import com.azure.cosmos.CosmosException; import com.azure.cosmos.implementation.Configs; import com.azure.cosmos.implementation.DiagnosticsClientContext; import com.azure.cosmos.implementation.ImplementationBridgeHelpers; import com.azure.cosmos.implementation.PathsHelper; +import com.azure.cosmos.implementation.Utils; import com.azure.cosmos.implementation.routing.PartitionKeyInternal; import com.azure.cosmos.models.CosmosQueryRequestOptions; import com.azure.cosmos.models.ModelBridgeInternal; @@ -139,6 +141,21 @@ static Mono getQueryPlanThroughGatewayAsync(Diagn () -> queryClient.getResetSessionTokenRetryPolicy().getRequestPolicy(diagnosticsClientContext), queryPlanRequest, executeFunc, - PathsHelper.getCollectionPath(resourceLink)); + PathsHelper.getCollectionPath(resourceLink)) + .onErrorMap(throwable -> { + + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + + if (HttpConstants.StatusCodes.NOTFOUND == (cosmosException.getStatusCode())) { + BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + return cosmosException; + } + + return throwable; + }); } } From 0a22036c06e67a917966c7b9d4ada02bdde1f949 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 11:23:19 +0400 Subject: [PATCH 04/13] Intercept 404-0s to remap to 404-1003s when parent resource for bulk operations. --- .../com/azure/cosmos/CosmosNotFoundTests.java | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java index cd9a5d4a1ae5..8a8dd1f22f04 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java @@ -8,14 +8,20 @@ import com.azure.cosmos.implementation.ImplementationBridgeHelpers; import com.azure.cosmos.implementation.OperationType; import com.azure.cosmos.implementation.TestConfigurations; +import com.azure.cosmos.models.CosmosBulkOperations; import com.azure.cosmos.models.CosmosContainerProperties; +import com.azure.cosmos.models.CosmosItemOperation; +import com.azure.cosmos.models.PartitionKey; import com.azure.cosmos.models.ThroughputProperties; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Factory; import org.testng.annotations.Test; +import reactor.core.publisher.Flux; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -113,6 +119,52 @@ public void performDocumentOperationOnNonExistentContainer(OperationType operati } } + @Test(groups = {"fast"}, timeOut = TIMEOUT) + public void performBulkOnNonExistentContainer() { + + CosmosAsyncClient asyncClientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); + + // Try to read the item from a non-existent container + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosAsyncContainer nonExistentContainer = asyncClientToUse + .getDatabase(existingAsyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); + + try { + + List cosmosItemOperations = new ArrayList<>(); + + CosmosItemOperation cosmosItemOperation = CosmosBulkOperations.getReadItemOperation( + this.objectToCreate.getId(), + new PartitionKey(this.createdItemPk), + TestObject.class); + + cosmosItemOperations.add(cosmosItemOperation); + + Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); + + nonExistentContainer.executeBulkOperations(operationsFlux).blockLast(); + } catch (CosmosException ce) { + + // Verify status code is 404 (Not Found) + assertThat(ce.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + // Verify sub-status code is either 0 or 1003 + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 1003") + .isIn( + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS + ); + } finally { + safeClose(asyncClientToUse); + } + } + @Test(groups = {"thin-client-multi-region"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) public void performDocumentOperationOnNonExistentContainerGatewayModeV2(OperationType operationType) { logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); @@ -157,6 +209,58 @@ public void performDocumentOperationOnNonExistentContainerGatewayModeV2(Operatio } } + @Test(groups = {"thin-client-multi-region"}, timeOut = TIMEOUT) + public void performBulkOnNonExistentContainerGatewayModeV2() { + logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); + + // Try to read the item from a non-existent container using Gateway mode client + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + + CosmosAsyncClient v2GatewayAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(new GatewayConnectionConfig().setHttp2ConnectionConfig(new Http2ConnectionConfig().setEnabled(true))) + .buildAsyncClient(); + + CosmosAsyncContainer nonExistentContainer = v2GatewayAsyncClient + .getDatabase(existingAsyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); + + try { + + List cosmosItemOperations = new ArrayList<>(); + + CosmosItemOperation cosmosItemOperation = CosmosBulkOperations.getReadItemOperation( + this.objectToCreate.getId(), + new PartitionKey(this.createdItemPk), + TestObject.class); + + cosmosItemOperations.add(cosmosItemOperation); + + Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); + + nonExistentContainer.executeBulkOperations(operationsFlux).blockLast(); + } catch (CosmosException ce) { + + // Verify status code is 404 (Not Found) + assertThat(ce.getStatusCode()) + .as("Status code should be 404 (Not Found)") + .isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + // Verify sub-status code is either 0 or 1003 + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 1003") + .isIn( + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS + ); + } finally { + safeClose(v2GatewayAsyncClient); + System.clearProperty("COSMOS.THINCLIENT_ENABLED"); + } + } + @Test(groups = {"fast"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) public void performDocumentOperationOnDeletedContainer(OperationType operationType) throws InterruptedException { @@ -228,6 +332,64 @@ public void performDocumentOperationOnDeletedContainer(OperationType operationTy } } + @Test(groups = {"fast"}, timeOut = TIMEOUT) + public void performBulkOnDeletedContainer() throws InterruptedException { + + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + + CosmosAsyncClient clientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); + + CosmosAsyncContainer containerToUse = clientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + + Thread.sleep(5000); + + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + containerToUse.createItem(testObject).block(); + + // Create a different client instance to delete the container + CosmosAsyncClient deletingAsyncClient = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); + + try { + // Delete the container using the different client instance + CosmosAsyncContainer containerToDelete = deletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + containerToDelete.delete().block(); + + Thread.sleep(5000); + + // Try to read the item from the deleted container using the original client + + List cosmosItemOperations = new ArrayList<>(); + + CosmosItemOperation cosmosItemOperation = CosmosBulkOperations.getReadItemOperation( + this.objectToCreate.getId(), + new PartitionKey(this.createdItemPk), + TestObject.class); + + cosmosItemOperations.add(cosmosItemOperation); + + Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); + + containerToUse.executeBulkOperations(operationsFlux).blockLast(); + } catch (CosmosException ce) { + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 0") + .isIn(HttpConstants.SubStatusCodes.UNKNOWN); + } finally { + safeClose(clientToUse); + safeClose(deletingAsyncClient); + } + } + @Test(groups = {"thin-client-multi-region"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) public void performDocumentOperationOnDeletedContainerWithGatewayV2(OperationType operationType) throws InterruptedException { logger.info("Running test: Read item from deleted container - Gateway V2 Connection Mode"); @@ -295,4 +457,72 @@ public void performDocumentOperationOnDeletedContainerWithGatewayV2(OperationTyp System.clearProperty("COSMOS.THINCLIENT_ENABLED"); } } + + @Test(groups = {"thin-client-multi-region"}, timeOut = TIMEOUT) + public void performBulkOnDeletedContainerWithGatewayV2() throws InterruptedException { + logger.info("Running test: Read item from deleted container - Gateway V2 Connection Mode"); + + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + Http2ConnectionConfig http2ConnectionConfig = new Http2ConnectionConfig().setEnabled(true); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + gatewayConnectionConfig.setHttp2ConnectionConfig(http2ConnectionConfig); + + CosmosAsyncClient gatewayV2AsyncClientToUse = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(gatewayConnectionConfig) + .buildAsyncClient(); + CosmosAsyncClient containerDeletingAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode() + .buildAsyncClient(); + + CosmosAsyncContainer containerToUse = gatewayV2AsyncClientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + + Thread.sleep(5000); + + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + containerToUse.createItem(testObject).block(); + + try { + // Delete the container using the different client instance + CosmosAsyncContainer asyncContainerToDelete = containerDeletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + asyncContainerToDelete.delete().block(); + + Thread.sleep(5000); + + // Try to read the item from the deleted container using the original client + + List cosmosItemOperations = new ArrayList<>(); + + CosmosItemOperation cosmosItemOperation = CosmosBulkOperations.getReadItemOperation( + this.objectToCreate.getId(), + new PartitionKey(this.createdItemPk), + TestObject.class); + + cosmosItemOperations.add(cosmosItemOperation); + + Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); + + containerToUse.executeBulkOperations(operationsFlux).blockLast(); + } catch (CosmosException ce) { + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 1003") + .isIn( + HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS + ); + } finally { + safeClose(gatewayV2AsyncClientToUse); + safeClose(containerDeletingAsyncClient); + + System.clearProperty("COSMOS.THINCLIENT_ENABLED"); + } + } } From 120cb57607338a27fb5dd9c7c7e97ce70c958bef Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 11:38:55 +0400 Subject: [PATCH 05/13] Force client closure in finally-block. --- .../com/azure/cosmos/CosmosNotFoundTests.java | 283 ++++++++++-------- 1 file changed, 150 insertions(+), 133 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java index 8a8dd1f22f04..61129bb10a28 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java @@ -82,18 +82,19 @@ public void afterClass() { @Test(groups = {"fast"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) public void performDocumentOperationOnNonExistentContainer(OperationType operationType) { - CosmosAsyncClient asyncClientToUse = getClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .buildAsyncClient(); - - // Try to read the item from a non-existent container - String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosAsyncContainer nonExistentContainer = asyncClientToUse - .getDatabase(existingAsyncContainer.getDatabase().getId()) - .getContainer(nonExistentContainerId); + CosmosAsyncClient asyncClientToUse = null; try { + asyncClientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); + + // Try to read the item from a non-existent container + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosAsyncContainer nonExistentContainer = asyncClientToUse + .getDatabase(existingAsyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); CosmosDiagnostics cosmosDiagnostics = this.performDocumentOperation(nonExistentContainer, operationType, this.objectToCreate, false, false, true); @@ -122,18 +123,18 @@ public void performDocumentOperationOnNonExistentContainer(OperationType operati @Test(groups = {"fast"}, timeOut = TIMEOUT) public void performBulkOnNonExistentContainer() { - CosmosAsyncClient asyncClientToUse = getClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .buildAsyncClient(); - - // Try to read the item from a non-existent container - String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosAsyncContainer nonExistentContainer = asyncClientToUse - .getDatabase(existingAsyncContainer.getDatabase().getId()) - .getContainer(nonExistentContainerId); - + CosmosAsyncClient asyncClientToUse = null; try { + asyncClientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); + + // Try to read the item from a non-existent container + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosAsyncContainer nonExistentContainer = asyncClientToUse + .getDatabase(existingAsyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); List cosmosItemOperations = new ArrayList<>(); @@ -169,22 +170,25 @@ public void performBulkOnNonExistentContainer() { public void performDocumentOperationOnNonExistentContainerGatewayModeV2(OperationType operationType) { logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); - // Try to read the item from a non-existent container using Gateway mode client - String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosAsyncClient v2GatewayAsyncClient = null; - System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + try { - CosmosAsyncClient v2GatewayAsyncClient = new CosmosClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .gatewayMode(new GatewayConnectionConfig().setHttp2ConnectionConfig(new Http2ConnectionConfig().setEnabled(true))) - .buildAsyncClient(); + // Try to read the item from a non-existent container using Gateway mode client + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosAsyncContainer nonExistentContainer = v2GatewayAsyncClient - .getDatabase(existingAsyncContainer.getDatabase().getId()) - .getContainer(nonExistentContainerId); + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + + v2GatewayAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(new GatewayConnectionConfig().setHttp2ConnectionConfig(new Http2ConnectionConfig().setEnabled(true))) + .buildAsyncClient(); + + CosmosAsyncContainer nonExistentContainer = v2GatewayAsyncClient + .getDatabase(existingAsyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); - try { CosmosDiagnostics cosmosDiagnostics = this.performDocumentOperation(nonExistentContainer, operationType, this.objectToCreate, false, false, true); assertThat(cosmosDiagnostics).isNotNull(); @@ -213,22 +217,24 @@ public void performDocumentOperationOnNonExistentContainerGatewayModeV2(Operatio public void performBulkOnNonExistentContainerGatewayModeV2() { logger.info("Running test: Read item from non-existent container in Gateway Connection Mode"); - // Try to read the item from a non-existent container using Gateway mode client - String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); + CosmosAsyncClient v2GatewayAsyncClient = null; - System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + try { - CosmosAsyncClient v2GatewayAsyncClient = new CosmosClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .gatewayMode(new GatewayConnectionConfig().setHttp2ConnectionConfig(new Http2ConnectionConfig().setEnabled(true))) - .buildAsyncClient(); + // Try to read the item from a non-existent container using Gateway mode client + String nonExistentContainerId = "NonExistentContainer_" + UUID.randomUUID(); - CosmosAsyncContainer nonExistentContainer = v2GatewayAsyncClient - .getDatabase(existingAsyncContainer.getDatabase().getId()) - .getContainer(nonExistentContainerId); + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); - try { + v2GatewayAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(new GatewayConnectionConfig().setHttp2ConnectionConfig(new Http2ConnectionConfig().setEnabled(true))) + .buildAsyncClient(); + + CosmosAsyncContainer nonExistentContainer = v2GatewayAsyncClient + .getDatabase(existingAsyncContainer.getDatabase().getId()) + .getContainer(nonExistentContainerId); List cosmosItemOperations = new ArrayList<>(); @@ -264,31 +270,33 @@ public void performBulkOnNonExistentContainerGatewayModeV2() { @Test(groups = {"fast"}, dataProvider = "operationTypeProvider", timeOut = TIMEOUT) public void performDocumentOperationOnDeletedContainer(OperationType operationType) throws InterruptedException { - // Create a dedicated container for this test - String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); - CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); - testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + CosmosAsyncClient clientToUse = null, deletingAsyncClient = null; - CosmosAsyncClient clientToUse = getClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .buildAsyncClient(); + try { + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); - CosmosAsyncContainer testContainer = clientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + clientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); - Thread.sleep(5000); + CosmosAsyncContainer testContainer = clientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); - // Create an item in the container - TestObject testObject = TestObject.create(this.createdItemPk); - testContainer.createItem(testObject).block(); + Thread.sleep(5000); - // Create a different client instance to delete the container - CosmosAsyncClient deletingAsyncClient = getClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .buildAsyncClient(); + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + testContainer.createItem(testObject).block(); + + // Create a different client instance to delete the container + deletingAsyncClient = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); - try { // Delete the container using the different client instance CosmosAsyncContainer containerToDelete = deletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); containerToDelete.delete().block(); @@ -335,31 +343,33 @@ public void performDocumentOperationOnDeletedContainer(OperationType operationTy @Test(groups = {"fast"}, timeOut = TIMEOUT) public void performBulkOnDeletedContainer() throws InterruptedException { - // Create a dedicated container for this test - String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); - CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); - testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + CosmosAsyncClient clientToUse = null, deletingAsyncClient = null; - CosmosAsyncClient clientToUse = getClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .buildAsyncClient(); + try { + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); - CosmosAsyncContainer containerToUse = clientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + clientToUse = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); - Thread.sleep(5000); + CosmosAsyncContainer containerToUse = clientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); - // Create an item in the container - TestObject testObject = TestObject.create(this.createdItemPk); - containerToUse.createItem(testObject).block(); + Thread.sleep(5000); - // Create a different client instance to delete the container - CosmosAsyncClient deletingAsyncClient = getClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .buildAsyncClient(); + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + containerToUse.createItem(testObject).block(); + + // Create a different client instance to delete the container + deletingAsyncClient = getClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .buildAsyncClient(); - try { // Delete the container using the different client instance CosmosAsyncContainer containerToDelete = deletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); containerToDelete.delete().block(); @@ -394,36 +404,39 @@ public void performBulkOnDeletedContainer() throws InterruptedException { public void performDocumentOperationOnDeletedContainerWithGatewayV2(OperationType operationType) throws InterruptedException { logger.info("Running test: Read item from deleted container - Gateway V2 Connection Mode"); - // Create a dedicated container for this test - String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); - CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); - testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); - - System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); - Http2ConnectionConfig http2ConnectionConfig = new Http2ConnectionConfig().setEnabled(true); - GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); - gatewayConnectionConfig.setHttp2ConnectionConfig(http2ConnectionConfig); + CosmosAsyncClient gatewayV2AsyncClientToUse = null, containerDeletingAsyncClient = null; - CosmosAsyncClient gatewayV2AsyncClientToUse = new CosmosClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .gatewayMode(gatewayConnectionConfig) - .buildAsyncClient(); - CosmosAsyncClient containerDeletingAsyncClient = new CosmosClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .gatewayMode() - .buildAsyncClient(); - - CosmosAsyncContainer testContainer = gatewayV2AsyncClientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + try { + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + Http2ConnectionConfig http2ConnectionConfig = new Http2ConnectionConfig().setEnabled(true); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + gatewayConnectionConfig.setHttp2ConnectionConfig(http2ConnectionConfig); + + gatewayV2AsyncClientToUse = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(gatewayConnectionConfig) + .buildAsyncClient(); + + containerDeletingAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode() + .buildAsyncClient(); + + CosmosAsyncContainer testContainer = gatewayV2AsyncClientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); - Thread.sleep(5000); + Thread.sleep(5000); - // Create an item in the container - TestObject testObject = TestObject.create(this.createdItemPk); - testContainer.createItem(testObject).block(); + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + testContainer.createItem(testObject).block(); - try { // Delete the container using the different client instance CosmosAsyncContainer asyncContainerToDelete = containerDeletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); asyncContainerToDelete.delete().block(); @@ -462,36 +475,40 @@ public void performDocumentOperationOnDeletedContainerWithGatewayV2(OperationTyp public void performBulkOnDeletedContainerWithGatewayV2() throws InterruptedException { logger.info("Running test: Read item from deleted container - Gateway V2 Connection Mode"); - // Create a dedicated container for this test - String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); - CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); - testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); + CosmosAsyncClient gatewayV2AsyncClientToUse = null, containerDeletingAsyncClient = null; - System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); - Http2ConnectionConfig http2ConnectionConfig = new Http2ConnectionConfig().setEnabled(true); - GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); - gatewayConnectionConfig.setHttp2ConnectionConfig(http2ConnectionConfig); + try { - CosmosAsyncClient gatewayV2AsyncClientToUse = new CosmosClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .gatewayMode(gatewayConnectionConfig) - .buildAsyncClient(); - CosmosAsyncClient containerDeletingAsyncClient = new CosmosClientBuilder() - .endpoint(TestConfigurations.HOST) - .key(TestConfigurations.MASTER_KEY) - .gatewayMode() - .buildAsyncClient(); + // Create a dedicated container for this test + String testContainerId = "CosmosNotFoundTestsContainer_" + UUID.randomUUID(); + CosmosContainerProperties containerProperties = new CosmosContainerProperties(testContainerId, "/mypk"); + testAsyncDatabase.createContainer(containerProperties, ThroughputProperties.createManualThroughput(400)).block(); - CosmosAsyncContainer containerToUse = gatewayV2AsyncClientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + System.setProperty("COSMOS.THINCLIENT_ENABLED", "true"); + Http2ConnectionConfig http2ConnectionConfig = new Http2ConnectionConfig().setEnabled(true); + GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); + gatewayConnectionConfig.setHttp2ConnectionConfig(http2ConnectionConfig); - Thread.sleep(5000); + gatewayV2AsyncClientToUse = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode(gatewayConnectionConfig) + .buildAsyncClient(); - // Create an item in the container - TestObject testObject = TestObject.create(this.createdItemPk); - containerToUse.createItem(testObject).block(); + containerDeletingAsyncClient = new CosmosClientBuilder() + .endpoint(TestConfigurations.HOST) + .key(TestConfigurations.MASTER_KEY) + .gatewayMode() + .buildAsyncClient(); + + CosmosAsyncContainer containerToUse = gatewayV2AsyncClientToUse.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); + + Thread.sleep(5000); + + // Create an item in the container + TestObject testObject = TestObject.create(this.createdItemPk); + containerToUse.createItem(testObject).block(); - try { // Delete the container using the different client instance CosmosAsyncContainer asyncContainerToDelete = containerDeletingAsyncClient.getDatabase(testAsyncDatabase.getId()).getContainer(testContainerId); asyncContainerToDelete.delete().block(); From 2a50fa08309d4c7c8878ba8d44950eb9c3380968 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 11:49:35 +0400 Subject: [PATCH 06/13] Clean up --- .../azure/cosmos/implementation/RxGatewayStoreModel.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java index db9bcdc5fd99..906fc518f2f7 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java @@ -316,13 +316,7 @@ private Mono performRequestInternalCore(RxDocumentSer .getEffectiveHttpTransportSerializer(this) .wrapInHttpRequest(request, requestUri); - Mono httpResponseMono = null; - - if (ResourceType.DocumentCollection.equals(request.getResourceType())) { - httpResponseMono = this.httpClient.send(httpRequest, request.getResponseTimeout()); - } else { - httpResponseMono = this.httpClient.send(httpRequest, request.getResponseTimeout()); - } + Mono httpResponseMono = this.httpClient.send(httpRequest, request.getResponseTimeout()); if (this.gatewayServerErrorInjector != null) { httpResponseMono = this.gatewayServerErrorInjector.injectGatewayErrors(request.getResponseTimeout(), From 3e8d85a395a4bc47161adf5ab5079d00542fda79 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 12:03:19 +0400 Subject: [PATCH 07/13] Reduce BridgeInternal#setSubstatusCode dependency. --- ...StaleResourceExceptionRetryPolicyTest.java | 3 - .../com/azure/cosmos/CosmosException.java | 7 +++ .../ImplementationBridgeHelpers.java | 1 + .../implementation/RxDocumentClientImpl.java | 55 +++++++++---------- .../StaleResourceRetryPolicy.java | 12 ++-- .../caches/RxCollectionCache.java | 10 +++- .../query/QueryPlanRetriever.java | 6 +- 7 files changed, 53 insertions(+), 41 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java index 032ecd7752e8..49a41d5081be 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java @@ -52,7 +52,6 @@ public void staledException(int statusCode, int subStatusCode, boolean expectRet null, sessionContainer, TestUtils.mockDiagnosticsClientContext(), - null, null ); @@ -91,7 +90,6 @@ public void suppressRetryForExternalCollectionRid() { customHeaders, sessionContainer, TestUtils.mockDiagnosticsClientContext(), - null, null ); @@ -130,7 +128,6 @@ public void cleanSessionToken() { null, sessionContainer, TestUtils.mockDiagnosticsClientContext(), - null, null ); diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java index 5d4731cef122..6bcacd61589d 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java @@ -674,6 +674,13 @@ public void setRequestUri(CosmosException cosmosException, Uri requestUri) { public Uri getRequestUri(CosmosException cosmosException) { return cosmosException.getRequestUri(); } + + @Override + public void setSubStatusCode(CosmosException cosmosException, int subStatusCode) { + cosmosException.setSubStatusCode(subStatusCode); + } + + }); } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java index d2f5cde47bac..03264db9c745 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java @@ -1596,6 +1596,7 @@ public interface CosmosExceptionAccessor { List getFaultInjectionEvaluationResults(CosmosException cosmosException); void setRequestUri(CosmosException cosmosException, Uri requestUri); Uri getRequestUri(CosmosException cosmosException); + void setSubStatusCode(CosmosException cosmosException, int subStatusCode); } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java index 0748b119f593..b02bcc391e1e 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java @@ -1273,8 +1273,8 @@ private Flux> createQuery( qryOptAccessor.getHeaders(nonNullQueryOptions), this.sessionContainer, diagnosticsFactory, - ResourceType.Document, - OperationType.Query); + ResourceType.Document + ); return ObservableHelper.fluxInlineIfPossibleAsObs( @@ -2607,8 +2607,8 @@ private Mono> createDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - collectionLink, - OperationType.Create); + collectionLink + ); AtomicReference requestReference = new AtomicReference<>(); @@ -2995,8 +2995,8 @@ private Mono> upsertDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - collectionLink, - OperationType.Upsert); + collectionLink + ); AtomicReference requestReference = new AtomicReference<>(); Consumer gwModeE2ETimeoutDiagnosticHandler @@ -3138,8 +3138,8 @@ private Mono> replaceDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink), - OperationType.Replace); + Utils.getCollectionName(documentLink) + ); AtomicReference requestReference = new AtomicReference<>(); Consumer gwModeE2ETimeoutDiagnosticHandler @@ -3472,8 +3472,8 @@ private Mono> patchDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink), - OperationType.Patch); + Utils.getCollectionName(documentLink) + ); AtomicReference requestReference = new AtomicReference<>(); @@ -3686,8 +3686,8 @@ private Mono> deleteDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink), - OperationType.Delete); + Utils.getCollectionName(documentLink) + ); AtomicReference requestReference = new AtomicReference<>(); @@ -3879,8 +3879,8 @@ private Mono> readDocumentCore( this.getRetryPolicyForPointOperation( scopedDiagnosticsFactory, nonNullRequestOptions, - Utils.getCollectionName(documentLink), - OperationType.Read); + Utils.getCollectionName(documentLink) + ); AtomicReference requestReference = new AtomicReference<>(); @@ -4030,8 +4030,8 @@ public Mono> readMany( qryOptAccessor.getHeaders(state.getQueryOptions()), this.sessionContainer, diagnosticsFactory, - ResourceType.Document, - OperationType.Query); + ResourceType.Document + ); return ObservableHelper .inlineIfPossibleAsObs( @@ -4729,8 +4729,8 @@ public Flux> queryDocumentChangeFeedFromPagedFlux( changeFeedOptionsAccessor.getHeaders(state.getChangeFeedOptions()), this.sessionContainer, diagnosticsFactory, - ResourceType.Document, - OperationType.ReadFeed); + ResourceType.Document + ); return ObservableHelper .fluxInlineIfPossibleAsObs( @@ -4887,8 +4887,8 @@ public Flux> readAllDocuments( qryOptAccessor.getHeaders(effectiveOptions), this.sessionContainer, diagnosticsFactory, - ResourceType.Document, - OperationType.Query); + ResourceType.Document + ); Flux> innerFlux = ObservableHelper.fluxInlineIfPossibleAsObs( () -> { @@ -5218,8 +5218,8 @@ public Mono executeBatchRequest(String collectionLink, nonNullRequestOptions.getHeaders(), this.sessionContainer, scopedDiagnosticsFactory, - ResourceType.Document, - OperationType.Batch); + ResourceType.Document + ); } final DocumentClientRetryPolicy finalRetryPolicy = documentClientRetryPolicy; @@ -6657,8 +6657,8 @@ public Mono> getFeedRanges(String collectionLink, boolean forceR new HashMap<>(), this.sessionContainer, null, - ResourceType.PartitionKeyRange, - OperationType.ReadFeed); + ResourceType.PartitionKeyRange + ); RxDocumentServiceRequest request = RxDocumentServiceRequest.create( this, @@ -8029,8 +8029,7 @@ private boolean useThinClientStoreModel(RxDocumentServiceRequest request) { private DocumentClientRetryPolicy getRetryPolicyForPointOperation( DiagnosticsClientContext diagnosticsClientContext, RequestOptions requestOptions, - String collectionLink, - OperationType operationType) { + String collectionLink) { checkNotNull(requestOptions, "Argument 'requestOptions' can not be null"); @@ -8047,8 +8046,8 @@ private DocumentClientRetryPolicy getRetryPolicyForPointOperation( requestOptions.getHeaders(), this.sessionContainer, diagnosticsClientContext, - ResourceType.Document, - operationType); + ResourceType.Document + ); return requestRetryPolicy; } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java index aa10ae52998c..7b96fe193212 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java @@ -24,6 +24,9 @@ public class StaleResourceRetryPolicy extends DocumentClientRetryPolicy { private final static Logger logger = LoggerFactory.getLogger(StaleResourceRetryPolicy.class); + private final static ImplementationBridgeHelpers.CosmosExceptionHelper.CosmosExceptionAccessor cosmosExceptionAccessor = + ImplementationBridgeHelpers.CosmosExceptionHelper.getCosmosExceptionAccessor(); + private final RxCollectionCache clientCollectionCache; private final DocumentClientRetryPolicy nextPolicy; private final String collectionLink; @@ -34,7 +37,6 @@ public class StaleResourceRetryPolicy extends DocumentClientRetryPolicy { private final DiagnosticsClientContext diagnosticsClientContext; private final AtomicReference cosmosDiagnosticsHolder; private final ResourceType enclosingOperationTargetResourceType; - private final OperationType enclosingOperationType; private volatile boolean retried = false; @@ -46,8 +48,7 @@ public StaleResourceRetryPolicy( Map requestCustomHeaders, ISessionContainer sessionContainer, DiagnosticsClientContext diagnosticsClientContext, - ResourceType enclosingOperationTargetResourceType, - OperationType enclosingOperationType) { + ResourceType enclosingOperationTargetResourceType) { this.clientCollectionCache = collectionCache; this.nextPolicy = nextPolicy; @@ -62,7 +63,6 @@ public StaleResourceRetryPolicy( this.cosmosDiagnosticsHolder = new AtomicReference<>(null); // will only create one if no request is bound to the retry policy this.enclosingOperationTargetResourceType = enclosingOperationTargetResourceType; - this.enclosingOperationType = enclosingOperationType; } @Override @@ -135,13 +135,13 @@ public Mono shouldRetry(Exception e) { CosmosException cosmosException = Utils.as(throwable, CosmosException.class); if (this.request != null && !ResourceType.DocumentCollection.equals(this.request.getResourceType()) && Exceptions.isNotFound(cosmosException)) { - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); return cosmosException; } if (this.enclosingOperationTargetResourceType != null && !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType)) { - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); return cosmosException; } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index 54129a9f0429..4841327dd4af 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -7,6 +7,7 @@ import com.azure.cosmos.implementation.CosmosClientMetadataCachesSnapshot; import com.azure.cosmos.implementation.DocumentCollection; import com.azure.cosmos.implementation.HttpConstants; +import com.azure.cosmos.implementation.ImplementationBridgeHelpers; import com.azure.cosmos.implementation.InvalidPartitionException; import com.azure.cosmos.implementation.MetadataDiagnosticsContext; import com.azure.cosmos.implementation.NotFoundException; @@ -29,6 +30,9 @@ */ public abstract class RxCollectionCache { + private final static ImplementationBridgeHelpers.CosmosExceptionHelper.CosmosExceptionAccessor cosmosExceptionAccessor = + ImplementationBridgeHelpers.CosmosExceptionHelper.getCosmosExceptionAccessor(); + private final AsyncCache collectionInfoByNameCache; private final AsyncCache collectionInfoByIdCache; @@ -94,7 +98,7 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } return cosmosException; @@ -110,7 +114,7 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } return cosmosException; @@ -136,7 +140,7 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } return cosmosException; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java index e199ae63a268..b783c1624d6e 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/query/QueryPlanRetriever.java @@ -37,6 +37,10 @@ class QueryPlanRetriever { ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.CosmosQueryRequestOptionsAccessor qryOptAccessor = ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.getCosmosQueryRequestOptionsAccessor(); + private final static + ImplementationBridgeHelpers.CosmosExceptionHelper.CosmosExceptionAccessor cosmosExceptionAccessor = + ImplementationBridgeHelpers.CosmosExceptionHelper.getCosmosExceptionAccessor(); + private static final String TRUE = "True"; // For a limited time, if the query runs against a region or emulator that has not yet been updated with the @@ -149,7 +153,7 @@ static Mono getQueryPlanThroughGatewayAsync(Diagn CosmosException cosmosException = Utils.as(throwable, CosmosException.class); if (HttpConstants.StatusCodes.NOTFOUND == (cosmosException.getStatusCode())) { - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } return cosmosException; From 16bdc0b122705898bf1cfe1aa31da1e0740bb597 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 18:25:19 +0400 Subject: [PATCH 08/13] Address review comments. --- .../com/azure/cosmos/CosmosNotFoundTests.java | 47 +++++++++++++++++++ .../com/azure/cosmos/CosmosException.java | 3 ++ .../implementation/RxDocumentClientImpl.java | 12 +++-- .../StaleResourceRetryPolicy.java | 2 +- .../caches/RxCollectionCache.java | 8 +--- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java index 61129bb10a28..a9e6e98ba835 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java @@ -13,6 +13,9 @@ import com.azure.cosmos.models.CosmosItemOperation; import com.azure.cosmos.models.PartitionKey; import com.azure.cosmos.models.ThroughputProperties; +import org.assertj.core.api.AssertionsForClassTypes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; @@ -21,13 +24,18 @@ import reactor.core.publisher.Flux; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; public class CosmosNotFoundTests extends FaultInjectionTestBase { + private static final Logger logger = LoggerFactory.getLogger(CosmosNotFoundTests.class); + + private static final String thinClientEndpointIndicator = ":10250/"; private static final ImplementationBridgeHelpers.CosmosAsyncClientHelper.CosmosAsyncClientAccessor accessor = ImplementationBridgeHelpers.CosmosAsyncClientHelper.getCosmosAsyncClientAccessor(); @@ -148,6 +156,8 @@ public void performBulkOnNonExistentContainer() { Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); nonExistentContainer.executeBulkOperations(operationsFlux).blockLast(); + + fail("Bulk operation on non-existent container should have failed."); } catch (CosmosException ce) { // Verify status code is 404 (Not Found) @@ -248,6 +258,8 @@ public void performBulkOnNonExistentContainerGatewayModeV2() { Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); nonExistentContainer.executeBulkOperations(operationsFlux).blockLast(); + + fail("Bulk operation on non-existent container should have failed."); } catch (CosmosException ce) { // Verify status code is 404 (Not Found) @@ -390,6 +402,8 @@ public void performBulkOnDeletedContainer() throws InterruptedException { Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); containerToUse.executeBulkOperations(operationsFlux).blockLast(); + + fail("Bulk operation on deleted container should have failed."); } catch (CosmosException ce) { assertThat(ce.getSubStatusCode()) .as("Sub-status code should be 0") @@ -463,6 +477,9 @@ public void performDocumentOperationOnDeletedContainerWithGatewayV2(OperationTyp HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS ); + if (!OperationType.Query.equals(operationType)) { + assertThinClientEndpointUsed(cosmosDiagnostics); + } } finally { safeClose(gatewayV2AsyncClientToUse); safeClose(containerDeletingAsyncClient); @@ -529,12 +546,16 @@ public void performBulkOnDeletedContainerWithGatewayV2() throws InterruptedExcep Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); containerToUse.executeBulkOperations(operationsFlux).blockLast(); + + fail("Bulk operation on deleted container should have failed."); } catch (CosmosException ce) { assertThat(ce.getSubStatusCode()) .as("Sub-status code should be 1003") .isIn( HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS ); + + assertThinClientEndpointUsed(ce.getDiagnostics()); } finally { safeClose(gatewayV2AsyncClientToUse); safeClose(containerDeletingAsyncClient); @@ -542,4 +563,30 @@ public void performBulkOnDeletedContainerWithGatewayV2() throws InterruptedExcep System.clearProperty("COSMOS.THINCLIENT_ENABLED"); } } + + private static void assertThinClientEndpointUsed(CosmosDiagnostics diagnostics) { + AssertionsForClassTypes.assertThat(diagnostics).isNotNull(); + + CosmosDiagnosticsContext ctx = diagnostics.getDiagnosticsContext(); + AssertionsForClassTypes.assertThat(ctx).isNotNull(); + + Collection requests = ctx.getRequestInfo(); + AssertionsForClassTypes.assertThat(requests).isNotNull(); + AssertionsForClassTypes.assertThat(requests.size()).isPositive(); + + for (CosmosDiagnosticsRequestInfo requestInfo : requests) { + logger.info( + "Endpoint: {}, RequestType: {}, Partition: {}/{}, ActivityId: {}", + requestInfo.getEndpoint(), + requestInfo.getRequestType(), + requestInfo.getPartitionId(), + requestInfo.getPartitionKeyRangeId(), + requestInfo.getActivityId()); + if (requestInfo.getEndpoint().contains(thinClientEndpointIndicator)) { + return; + } + } + + fail("No request targeting thin client proxy endpoint."); + } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java index 6bcacd61589d..f0f993ec9580 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosException.java @@ -677,6 +677,9 @@ public Uri getRequestUri(CosmosException cosmosException) { @Override public void setSubStatusCode(CosmosException cosmosException, int subStatusCode) { + Map responseHeaders = cosmosException.getResponseHeaders(); + + responseHeaders.put(HttpConstants.HttpHeaders.SUB_STATUS, String.valueOf(subStatusCode)); cosmosException.setSubStatusCode(subStatusCode); } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java index b02bcc391e1e..077bdcf49b1d 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxDocumentClientImpl.java @@ -191,6 +191,9 @@ public class RxDocumentClientImpl implements AsyncDocumentClient, IAuthorization private static final ImplementationBridgeHelpers.CosmosBulkExecutionOptionsHelper.CosmosBulkExecutionOptionsAccessor bulkExecutionOptionsAccessor = ImplementationBridgeHelpers.CosmosBulkExecutionOptionsHelper.getCosmosBulkExecutionOptionsAccessor(); + private static final ImplementationBridgeHelpers.CosmosExceptionHelper.CosmosExceptionAccessor cosmosExceptionAccessor = + ImplementationBridgeHelpers.CosmosExceptionHelper.getCosmosExceptionAccessor(); + private static final String tempMachineId = "uuid:" + UUIDs.nonBlockingRandomUUID(); private static final AtomicInteger activeClientsCnt = new AtomicInteger(0); private static final Map clientMap = new ConcurrentHashMap<>(); @@ -4758,10 +4761,13 @@ private Flux> queryDocumentChangeFeedFromPagedFluxInternal( .onErrorMap(throwable -> { if (throwable instanceof CosmosException) { - CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - BridgeInternal.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + CosmosException ce = Utils.as(throwable, CosmosException.class); - return cosmosException; + if (HttpConstants.StatusCodes.NOTFOUND == ce.getStatusCode() && HttpConstants.SubStatusCodes.UNKNOWN == ce.getSubStatusCode()) { + cosmosExceptionAccessor.setSubStatusCode(ce, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + return ce; } return throwable; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java index 7b96fe193212..00437a46ccfa 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java @@ -140,7 +140,7 @@ public Mono shouldRetry(Exception e) { return cosmosException; } - if (this.enclosingOperationTargetResourceType != null && !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType)) { + if (this.enclosingOperationTargetResourceType != null && !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType) && Exceptions.isNotFound(cosmosException)) { cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); return cosmosException; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index 4841327dd4af..5d65a0991058 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -204,9 +204,7 @@ public Mono> resolveByRidAsync( Mono async = this.collectionInfoByIdCache.getAsync( collectionResourceId, null, - () -> this.getByRidAsync(metaDataDiagnosticsContext, collectionResourceId, properties).onErrorMap(throwable -> { - return throwable; - })); + () -> this.getByRidAsync(metaDataDiagnosticsContext, collectionResourceId, properties)); return async.map(Utils.ValueHolder::new); } @@ -230,9 +228,7 @@ public Mono resolveByNameAsync( () -> { Mono collectionObs = this.getByNameAsync( metaDataDiagnosticsContext, resourceFullName, properties); - return collectionObs.onErrorMap(throwable -> { - return throwable; - }).doOnSuccess(collection -> this.collectionInfoByIdCache.set( + return collectionObs.doOnSuccess(collection -> this.collectionInfoByIdCache.set( collection.getResourceId(), collection)); }); From 90c0ffca8c1bb4bc1a891883f6c2cf90f3660f29 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Mon, 29 Dec 2025 19:35:11 +0400 Subject: [PATCH 09/13] Updated CHANGELOG.md --- sdk/cosmos/azure-cosmos/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/cosmos/azure-cosmos/CHANGELOG.md b/sdk/cosmos/azure-cosmos/CHANGELOG.md index ce3b76a5d722..726856c40be4 100644 --- a/sdk/cosmos/azure-cosmos/CHANGELOG.md +++ b/sdk/cosmos/azure-cosmos/CHANGELOG.md @@ -9,6 +9,7 @@ #### Bugs Fixed #### Other Changes +* Remaps sub-status to 1003 for requests to child resources against non-existent container. - [PR 47604](https://github.com/Azure/azure-sdk-for-java/pull/47604) ### 4.76.0 (2025-12-09) From f52e657de13aeb0ea745dc713e93de1de552ab3f Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Tue, 30 Dec 2025 12:23:07 +0400 Subject: [PATCH 10/13] Addressing review comments. --- .../com/azure/cosmos/implementation/RxGatewayStoreModel.java | 2 -- .../azure/cosmos/implementation/caches/RxCollectionCache.java | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java index 906fc518f2f7..271c6038d203 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java @@ -25,8 +25,6 @@ import com.azure.cosmos.implementation.http.HttpTransportSerializer; import com.azure.cosmos.implementation.http.ReactorNettyRequestRecord; import com.azure.cosmos.implementation.interceptor.ITransportClientInterceptor; -import com.azure.cosmos.implementation.perPartitionCircuitBreaker.GlobalPartitionEndpointManagerForPerPartitionCircuitBreaker; -import com.azure.cosmos.implementation.perPartitionCircuitBreaker.LocationSpecificHealthContext; import com.azure.cosmos.implementation.routing.PartitionKeyInternal; import com.azure.cosmos.implementation.routing.PartitionKeyInternalHelper; import com.azure.cosmos.implementation.routing.PartitionKeyRangeIdentity; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index 5d65a0991058..c8c7a17aa0bd 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -250,9 +250,7 @@ public Mono refreshAsync(MetadataDiagnosticsContext metaDataDiagnosticsCon obsoleteValue, () -> { Mono collectionObs = this.getByNameAsync(metaDataDiagnosticsContext, resourceFullName, request.properties); - return collectionObs.onErrorMap(throwable -> { - return throwable; - }).doOnSuccess(collection -> { + return collectionObs.doOnSuccess(collection -> { this.collectionInfoByIdCache.set(collection.getResourceId(), collection); }); }).then(); From f80f5063734a15d3feb151d3481cb90532705326 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Tue, 30 Dec 2025 12:40:42 +0400 Subject: [PATCH 11/13] Addressing review comments. --- .../implementation/StaleResourceRetryPolicy.java | 12 ++++++++++-- .../implementation/caches/RxCollectionCache.java | 13 ++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java index 00437a46ccfa..eca67d8948a6 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/StaleResourceRetryPolicy.java @@ -134,13 +134,21 @@ public Mono shouldRetry(Exception e) { CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (this.request != null && !ResourceType.DocumentCollection.equals(this.request.getResourceType()) && Exceptions.isNotFound(cosmosException)) { + if (this.request != null && + !ResourceType.DocumentCollection.equals(this.request.getResourceType()) && + Exceptions.isNotFound(cosmosException) && + Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); return cosmosException; } - if (this.enclosingOperationTargetResourceType != null && !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType) && Exceptions.isNotFound(cosmosException)) { + if (this.enclosingOperationTargetResourceType != null && + !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType) && + Exceptions.isNotFound(cosmosException) && + Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); return cosmosException; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index c8c7a17aa0bd..a74814b37c14 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -97,7 +97,10 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && + com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException) && + com.azure.cosmos.implementation.Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } @@ -113,7 +116,9 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && + com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException) && + com.azure.cosmos.implementation.Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } @@ -139,7 +144,9 @@ public Mono> resolveCollectionAsync( CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException)) { + if (!ResourceType.DocumentCollection.equals(request.getResourceType()) && + com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException) && + com.azure.cosmos.implementation.Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } From 986a4df7a5c97d822127ab057618ba52dabf9890 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Tue, 30 Dec 2025 18:53:03 +0400 Subject: [PATCH 12/13] Addressing review comments. --- .../com/azure/cosmos/CosmosNotFoundTests.java | 52 +++++++++++++++++-- .../implementation/RxGatewayStoreModel.java | 2 +- .../StaleResourceRetryPolicy.java | 39 ++------------ .../batch/BulkExecutorUtil.java | 5 +- .../caches/RxCollectionCache.java | 46 +++++++++++++++- 5 files changed, 101 insertions(+), 43 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java index a9e6e98ba835..b5cb23602e30 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/CosmosNotFoundTests.java @@ -8,6 +8,8 @@ import com.azure.cosmos.implementation.ImplementationBridgeHelpers; import com.azure.cosmos.implementation.OperationType; import com.azure.cosmos.implementation.TestConfigurations; +import com.azure.cosmos.implementation.Utils; +import com.azure.cosmos.models.CosmosBulkOperationResponse; import com.azure.cosmos.models.CosmosBulkOperations; import com.azure.cosmos.models.CosmosContainerProperties; import com.azure.cosmos.models.CosmosItemOperation; @@ -401,13 +403,43 @@ public void performBulkOnDeletedContainer() throws InterruptedException { Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); - containerToUse.executeBulkOperations(operationsFlux).blockLast(); + CosmosBulkOperationResponse response = containerToUse.executeBulkOperations(operationsFlux).blockLast(); - fail("Bulk operation on deleted container should have failed."); + assertThat(response).isNotNull(); + assertThat(response.getException()).isNotNull(); + + Exception e = response.getException(); + + assertThat(e).isInstanceOf(CosmosException.class); + + CosmosException ce = Utils.as(e, CosmosException.class); + + if (ConnectionMode.DIRECT.name().equals(accessor.getConnectionMode(clientToUse))) { + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 1003") + .isIn(HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + if (ConnectionMode.GATEWAY.name().equals(accessor.getConnectionMode(clientToUse))) { + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 0") + .isIn(HttpConstants.SubStatusCodes.UNKNOWN); + } } catch (CosmosException ce) { - assertThat(ce.getSubStatusCode()) + + assertThat(ce.getStatusCode()).isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + + if (ConnectionMode.DIRECT.name().equals(accessor.getConnectionMode(clientToUse))) { + assertThat(ce.getSubStatusCode()) + .as("Sub-status code should be 1003") + .isIn(HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + } + + if (ConnectionMode.GATEWAY.name().equals(accessor.getConnectionMode(clientToUse))) { + assertThat(ce.getSubStatusCode()) .as("Sub-status code should be 0") .isIn(HttpConstants.SubStatusCodes.UNKNOWN); + } } finally { safeClose(clientToUse); safeClose(deletingAsyncClient); @@ -545,9 +577,19 @@ public void performBulkOnDeletedContainerWithGatewayV2() throws InterruptedExcep Flux operationsFlux = Flux.fromIterable(cosmosItemOperations); - containerToUse.executeBulkOperations(operationsFlux).blockLast(); + CosmosBulkOperationResponse response = containerToUse.executeBulkOperations(operationsFlux).blockLast(); + + assertThat(response).isNotNull(); + assertThat(response.getException()).isNotNull(); + + Exception ce = response.getException(); + + assertThat(ce).isInstanceOf(CosmosException.class); + + CosmosException cosmosException = Utils.as(ce, CosmosException.class); - fail("Bulk operation on deleted container should have failed."); + assertThat(cosmosException.getStatusCode()).isEqualTo(HttpConstants.StatusCodes.NOTFOUND); + assertThat(cosmosException.getSubStatusCode()).isEqualTo(HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); } catch (CosmosException ce) { assertThat(ce.getSubStatusCode()) .as("Sub-status code should be 1003") diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java index 271c6038d203..c7b7e0b3aa7f 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/RxGatewayStoreModel.java @@ -633,7 +633,7 @@ private Mono toDocumentServiceResponse(Mono shouldRetry(Exception e) { // 2. If the collection rid has changed, then also clean up session container for old containerRid AtomicReference oldCollectionRid = new AtomicReference<>(); return this.clientCollectionCache - .resolveByNameAsync(this.getMetadataDiagnosticsContext(), collectionLink, requestOptionProperties) + .resolveByNameAsync(this.getMetadataDiagnosticsContext(), collectionLink, requestOptionProperties, null, this.request, this.enclosingOperationTargetResourceType) .flatMap(collectionInCache -> { oldCollectionRid.set(collectionInCache.getResourceId()); @@ -109,8 +109,10 @@ public Mono shouldRetry(Exception e) { .resolveByNameAsync( this.getMetadataDiagnosticsContext(), collectionLink, - requestOptionProperties - ) + requestOptionProperties, + null, + this.request, + this.enclosingOperationTargetResourceType) .map(DocumentCollection :: getResourceId); } @@ -127,37 +129,6 @@ public Mono shouldRetry(Exception e) { } return Mono.just(ShouldRetryResult.retryAfter(Duration.ZERO)); - }) - .onErrorMap(throwable -> { - - if (throwable instanceof CosmosException) { - - CosmosException cosmosException = Utils.as(throwable, CosmosException.class); - - if (this.request != null && - !ResourceType.DocumentCollection.equals(this.request.getResourceType()) && - Exceptions.isNotFound(cosmosException) && - Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { - - cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); - - return cosmosException; - } - - if (this.enclosingOperationTargetResourceType != null && - !ResourceType.DocumentCollection.equals(this.enclosingOperationTargetResourceType) && - Exceptions.isNotFound(cosmosException) && - Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { - - cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); - - return cosmosException; - } - - return cosmosException; - } - - return throwable; }); } else { diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/batch/BulkExecutorUtil.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/batch/BulkExecutorUtil.java index 159444cdcae0..7f4cd9e31cce 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/batch/BulkExecutorUtil.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/batch/BulkExecutorUtil.java @@ -12,6 +12,7 @@ import com.azure.cosmos.implementation.DocumentCollection; import com.azure.cosmos.implementation.HttpConstants; import com.azure.cosmos.implementation.ResourceThrottleRetryPolicy; +import com.azure.cosmos.implementation.ResourceType; import com.azure.cosmos.implementation.Utils; import com.azure.cosmos.implementation.caches.RxClientCollectionCache; import com.azure.cosmos.implementation.routing.CollectionRoutingMap; @@ -189,7 +190,9 @@ private static Mono getCollectionInfoAsync( null, resourceAddress, null, - obsoleteValue); + obsoleteValue, + null, + ResourceType.Document); } static boolean isWriteOperation(CosmosItemOperationType cosmosItemOperationType) { diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java index a74814b37c14..7204b708265a 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/caches/RxCollectionCache.java @@ -225,7 +225,9 @@ public Mono resolveByNameAsync( MetadataDiagnosticsContext metaDataDiagnosticsContext, String resourceAddress, Map properties, - DocumentCollection obsoleteValue) { + DocumentCollection obsoleteValue, + RxDocumentServiceRequest encapsulatingRequest, + ResourceType encapsulatingOperationResourceType) { String resourceFullName = PathsHelper.getCollectionPath(resourceAddress); @@ -234,13 +236,53 @@ public Mono resolveByNameAsync( obsoleteValue, () -> { Mono collectionObs = this.getByNameAsync( - metaDataDiagnosticsContext, resourceFullName, properties); + metaDataDiagnosticsContext, resourceFullName, properties) + .onErrorMap(throwable -> { + + if (throwable instanceof CosmosException) { + + CosmosException cosmosException = Utils.as(throwable, CosmosException.class); + + if (encapsulatingRequest != null && + !ResourceType.DocumentCollection.equals(encapsulatingRequest.getResourceType()) && + com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException) && + com.azure.cosmos.implementation.Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { + + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + + return cosmosException; + } + + if (encapsulatingOperationResourceType != null && + !ResourceType.DocumentCollection.equals(encapsulatingOperationResourceType) && + com.azure.cosmos.implementation.Exceptions.isNotFound(cosmosException) && + com.azure.cosmos.implementation.Exceptions.isSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.UNKNOWN)) { + + cosmosExceptionAccessor.setSubStatusCode(cosmosException, HttpConstants.SubStatusCodes.OWNER_RESOURCE_NOT_EXISTS); + + return cosmosException; + } + + return cosmosException; + } + + return throwable; + }); return collectionObs.doOnSuccess(collection -> this.collectionInfoByIdCache.set( collection.getResourceId(), collection)); }); } + public Mono resolveByNameAsync( + MetadataDiagnosticsContext metaDataDiagnosticsContext, + String resourceAddress, + Map properties, + DocumentCollection obsoleteValue) { + + return this.resolveByNameAsync(metaDataDiagnosticsContext, resourceAddress, properties, obsoleteValue, null, null); + } + public Mono refreshAsync(MetadataDiagnosticsContext metaDataDiagnosticsContext, RxDocumentServiceRequest request) { // TODO System.Diagnostics.Debug.Assert(request.IsNameBased); From 3b045a165c385c0ad202931eb4b0f0e13c803e01 Mon Sep 17 00:00:00 2001 From: Abhijeet Mohanty Date: Tue, 30 Dec 2025 20:18:39 +0400 Subject: [PATCH 13/13] Addressing review comments. --- .../StaleResourceExceptionRetryPolicyTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java index 49a41d5081be..2ad9cb016d9c 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/StaleResourceExceptionRetryPolicyTest.java @@ -38,7 +38,7 @@ public void staledException(int statusCode, int subStatusCode, boolean expectRet RxCollectionCache rxCollectionCache = Mockito.mock(RxCollectionCache.class); Mockito - .when(rxCollectionCache.resolveByNameAsync(Mockito.any(), Mockito.any(), Mockito.isNull())) + .when(rxCollectionCache.resolveByNameAsync(Mockito.any(), Mockito.any(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull())) .thenReturn(Mono.just(documentCollection)); doNothing().when(rxCollectionCache).refresh(Mockito.any(), Mockito.any(), Mockito.isNull()); @@ -73,7 +73,7 @@ public void suppressRetryForExternalCollectionRid() { RxCollectionCache rxCollectionCache = Mockito.mock(RxCollectionCache.class); Mockito - .when(rxCollectionCache.resolveByNameAsync(Mockito.any(), Mockito.any(), Mockito.isNull())) + .when(rxCollectionCache.resolveByNameAsync(Mockito.any(), Mockito.any(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull())) .thenReturn(Mono.just(documentCollection)); doNothing().when(rxCollectionCache).refresh(Mockito.any(), Mockito.any(), Mockito.isNull()); @@ -111,7 +111,7 @@ public void cleanSessionToken() { RxCollectionCache rxCollectionCache = Mockito.mock(RxCollectionCache.class); Mockito - .when(rxCollectionCache.resolveByNameAsync(Mockito.any(), Mockito.any(), Mockito.isNull())) + .when(rxCollectionCache.resolveByNameAsync(Mockito.any(), Mockito.any(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull())) .thenReturn(Mono.just(documentCollection)) .thenReturn(Mono.just(documentCollectionAfterRefresh));