diff --git a/Directory.Build.props b/Directory.Build.props index 015f0e14cc..c2eb610e0f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ 3.57.0 3.58.0 preview.0 - 3.41.3 + 3.42.0 1.0.0 beta.0 2.0.5 diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index f4fbcfca01..293c93abf8 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -6792,8 +6792,6 @@ private void InitializeDirectConnectivity(IStoreClientFactory storeClientFactory private void CreateStoreModel(bool subscribeRntbdStatus) { - AccountConfigurationProperties accountConfigurationProperties = new (EnableNRegionSynchronousCommit: this.accountServiceConfiguration.AccountProperties.EnableNRegionSynchronousCommit); - //EnableReadRequestsFallback, if not explicity set on the connection policy, //is false if the account's consistency is bounded staleness, //and true otherwise. @@ -6808,8 +6806,7 @@ private void CreateStoreModel(bool subscribeRntbdStatus) this.UseMultipleWriteLocations && (this.accountServiceConfiguration.DefaultConsistencyLevel != Documents.ConsistencyLevel.Strong), true, enableReplicaValidation: this.isReplicaAddressValidationEnabled, - sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions, - accountConfigurationProperties: accountConfigurationProperties); + sessionRetryOptions: this.ConnectionPolicy.SessionRetryOptions); if (subscribeRntbdStatus) { diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosAccountServiceConfiguration.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosAccountServiceConfiguration.cs index 24aad85156..6baa478ecd 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosAccountServiceConfiguration.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/CosmosAccountServiceConfiguration.cs @@ -9,7 +9,7 @@ namespace Microsoft.Azure.Cosmos using System.Threading.Tasks; using Microsoft.Azure.Documents; - internal class CosmosAccountServiceConfiguration : IServiceConfigurationReader + internal class CosmosAccountServiceConfiguration : IServiceConfigurationReaderVnext { private Func> accountPropertiesTaskFunc { get; } @@ -53,6 +53,8 @@ public CosmosAccountServiceConfiguration(Func> accountPr public string SubscriptionId => throw new NotImplementedException(); + public bool EnableNRegionSynchronousCommit => this.AccountProperties.EnableNRegionSynchronousCommit; + public async Task InitializeAsync() { if (this.AccountProperties == null) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientCreateAndInitializeTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientCreateAndInitializeTest.cs index 1813700e05..1444542f9f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientCreateAndInitializeTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ClientCreateAndInitializeTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; + using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; @@ -364,5 +365,105 @@ public async Task CreateAndInitializeAsync_WithValidDatabaseAndInvalidContainer_ Assert.IsNotNull(ce); Assert.AreEqual(HttpStatusCode.NotFound, ce.StatusCode); } + + /// + /// Test to validate write item operations with transport interceptor that modifies + /// GlobalNRegionCommittedGLSN header and HTTP interceptor that sets EnableNRegionSynchronousCommit + /// in AccountProperties response. + /// + [TestMethod] + [Owner("aavasthy")] + public async Task CreateAndInitializeAsync_WriteItemOperationWithNRegionCommitTest() + { + // Custom GlobalNRegionCommittedGLSN value to inject + const long customGlobalNRegionCommittedGLSN = 100; + + // HTTP handler to intercept AccountProperties response and set EnableNRegionSynchronousCommit = true + HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper + { + ResponseIntercepter = async (response, request) => + { + if (request.RequestUri.AbsolutePath.EndsWith("/", StringComparison.OrdinalIgnoreCase) || + request.RequestUri.AbsolutePath.Equals(string.Empty)) + { + if (response.IsSuccessStatusCode) + { + string content = await response.Content.ReadAsStringAsync(); + Newtonsoft.Json.Linq.JObject accountJson = Newtonsoft.Json.Linq.JObject.Parse(content); + + accountJson[Constants.Properties.EnableNRegionSynchronousCommit] = true; + + string modifiedContent = accountJson.ToString(); + response.Content = new StringContent(modifiedContent, System.Text.Encoding.UTF8, "application/json"); + } + } + return response; + } + }; + + await this.TestInit( + customizeClientBuilder: (builder) => + { + builder.WithConnectionModeDirect(); + builder.WithHttpClientFactory(() => new HttpClient(httpClientHandlerHelper)); + builder.WithTransportClientHandlerFactory(transportClient => + new TransportClientHelper.TransportClientWrapper( + client: transportClient, + interceptorAfterResult: (request, storeResponse) => + { + // Override GlobalNRegionCommittedGLSN header in the response for Document operations + if (request.ResourceType == ResourceType.Document) + { + storeResponse.Headers.Set( + WFConstants.BackendHeaders.GlobalNRegionCommittedGLSN, + customGlobalNRegionCommittedGLSN.ToString(CultureInfo.InvariantCulture)); + } + return storeResponse; + })); + }); + + string containerName = "NRegionCommitTestContainer_" + Guid.NewGuid().ToString("N"); + ContainerResponse containerResponse = await this.database.CreateContainerAsync( + new ContainerProperties(id: containerName, partitionKeyPath: PartitionKey), + cancellationToken: this.cancellationToken); + Assert.IsNotNull(containerResponse); + + Container container = containerResponse.Container; + + try + { + for (int i = 0; i < 2; i++) + { + ToDoActivity item = new() + { + id = Guid.NewGuid().ToString(), + pk = "testPartition", + description = $"Test item {i}", + }; + + // Act + ItemResponse writeResponse = await container.CreateItemAsync( + item: item, + partitionKey: new Cosmos.PartitionKey(item.pk)); + + // Assert + Assert.AreEqual(HttpStatusCode.Created, writeResponse.StatusCode); + + // Assert: Verify GlobalNRegionCommittedGLSN is present in diagnostics with value 100 + string diagnostics = writeResponse.Diagnostics.ToString(); + + Assert.IsTrue( + diagnostics.Contains($"\"GlobalNRegionCommittedGLSN\":{customGlobalNRegionCommittedGLSN}") || + diagnostics.Contains($"\"GlobalNRegionCommittedGLSN\": {customGlobalNRegionCommittedGLSN}"), + $"Expected GlobalNRegionCommittedGLSN to be {customGlobalNRegionCommittedGLSN} in diagnostics. Actual diagnostics: {diagnostics}"); + + } + } + finally + { + // Cleanup: Delete the test container + await container.DeleteContainerAsync(); + } + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs index 34a35ccd46..c73d033d61 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/DocumentClientUnitTests.cs @@ -276,113 +276,6 @@ private void TestRetryOnThrottled(int? numberOfRetries) Assert.IsTrue(throttled); } - [TestMethod] - [DataRow(false, DisplayName = "NRegion Synchronous commit is disabled for the account")] - [DataRow(true, DisplayName = "NRegion Synchronous commit is enabled for the account")] - public void EnableNRegionSynchronousCommit_PassedToStoreClient(bool nRegionCommitEnabled) - { - - StoreClient storeClient = new StoreClient( - new Mock().Object, - new SessionContainer(string.Empty), - new Mock().Object, - new Mock().Object, - Protocol.Tcp, - new Mock().Object); - // Arrange - Mock mockStoreClientFactory = new Mock(); - mockStoreClientFactory.Setup(f => f.CreateStoreClient( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny() - )).Returns(storeClient); - - DocumentClient documentClient = new DocumentClient( - new Uri("https://localhost:8081"), - new Mock().Object, - new EventHandler((s, e) => { }), - new ConnectionPolicy(), - null, // desiredConsistencyLevel - null, // serializerSettings - ApiType.None, - new EventHandler((s, e) => { }), - null, // handler - new Mock().Object, - null, // enableCpuMonitor - new Func(tc => tc), - mockStoreClientFactory.Object, - false, // isLocalQuorumConsistency - "testClientId", - new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) => true), - new Mock().Object, - new Mock().Object, - true // enableAsyncCacheExceptionNoSharing - ); - - AccountProperties accountProperties = new AccountProperties - { - // Set the property to true for test - EnableNRegionSynchronousCommit = nRegionCommitEnabled, - }; - - AccountConsistency ac = new AccountConsistency(); - ac.DefaultConsistencyLevel = (Cosmos.ConsistencyLevel) ConsistencyLevel.Session; - accountProperties.Consistency = ac; - - Func> getDatabaseAccountFn = () => - // When called with any Uri, return the expected AccountProperties - Task.FromResult(accountProperties); - - CosmosAccountServiceConfiguration accountServiceConfiguration = new CosmosAccountServiceConfiguration( - getDatabaseAccountFn); - - typeof(CosmosAccountServiceConfiguration) - .GetProperty("AccountProperties", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) - .SetValue(accountServiceConfiguration, accountProperties); - - //Inject the accountServiceConfiguration into the DocumentClient via reflection. - typeof(DocumentClient) - .GetProperty("accountServiceConfiguration", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) - .SetValue(documentClient, accountServiceConfiguration); - - - typeof(DocumentClient) - .GetField("storeClientFactory", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) - .SetValue(documentClient, mockStoreClientFactory.Object); - - // Act: Call the private method via reflection - typeof(DocumentClient) - .GetMethod("CreateStoreModel", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) - .Invoke(documentClient, new object[] { true }); - - // Assert: Verify the correct value was passed - mockStoreClientFactory.Verify(f => - f.CreateStoreClient( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.Is(config => config.EnableNRegionSynchronousCommit == accountProperties.EnableNRegionSynchronousCommit), - It.IsAny()), - Times.Once, - "EnableNRegionSynchronousCommit was not passed correctly to AccountConfigurationProperties and StoreClient."); - } - private DocumentClientException CreateTooManyRequestException(int retryAfterInMilliseconds) { HttpResponseMessage responseMessage = new HttpResponseMessage(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs index 01beceec22..b595711070 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ClientRetryPolicyTests.cs @@ -568,8 +568,7 @@ private async Task ValidateConnectTimeoutTriggersClientRetryPolicyAsync( useMultipleWriteLocations: useMultipleWriteLocations, detectClientConnectivityIssues: true, disableRetryWithRetryPolicy: false, - enableReplicaValidation: false, - accountConfigurationProperties: null); + enableReplicaValidation: false); // Reducing retry timeout to avoid long-running tests replicatedResourceClient.GoneAndRetryWithRetryTimeoutInSecondsOverride = 1; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs index 8f5521c2a2..a8c5c75087 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/StoreReaderTest.cs @@ -600,13 +600,13 @@ public void GlobalStrongConsistentWriteMockTest() { TransportClient mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, false, false); StoreReader storeReader = new StoreReader(mockTransportClient, addressSelector, new AddressEnumerator(), sessionContainer, false); - ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); StoreResponse response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); //globalCommittedLsn never catches up in this case mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, true, false, false); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); try { response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; @@ -617,17 +617,17 @@ public void GlobalStrongConsistentWriteMockTest() } mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, true, false); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, true, true); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); mockTransportClient = this.GetMockTransportClientForGlobalStrongWrites(addressInformation, i, false, false, true); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: null); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(30)), false).Result; Assert.AreEqual(100, response.LSN); } @@ -960,8 +960,9 @@ public void TestWhenNRegionSynchronousCommitEnabledThenDoBarrierHead() ISessionContainer sessionContainer = new SessionContainer(string.Empty); - Mock mockServiceConfigReader = new Mock(); + Mock mockServiceConfigReader = new Mock(); mockServiceConfigReader.Setup(reader => reader.DefaultConsistencyLevel).Returns(Documents.ConsistencyLevel.Session); + mockServiceConfigReader.Setup(reader => reader.EnableNRegionSynchronousCommit).Returns(true); Mock mockAuthorizationTokenProvider = new Mock(); mockAuthorizationTokenProvider.Setup(provider => provider.AddSystemAuthorizationHeaderAsync( @@ -971,9 +972,8 @@ public void TestWhenNRegionSynchronousCommitEnabledThenDoBarrierHead() TransportClient mockTransportClient = this.GetMockTransportClientForNRegionSynchronousWrites(addressInformation, false); StoreReader storeReader = new StoreReader(mockTransportClient, addressSelector, new AddressEnumerator(), sessionContainer, false); - AccountConfigurationProperties accountConfigurationProperties = new AccountConfigurationProperties(true); - ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: accountConfigurationProperties); + ConsistencyWriter consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); StoreResponse response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(3000)), false).Result; Assert.AreEqual(100, response.LSN); @@ -983,7 +983,7 @@ public void TestWhenNRegionSynchronousCommitEnabledThenDoBarrierHead() mockTransportClient = this.GetMockTransportClientForNRegionSynchronousWrites(addressInformation, true); storeReader = new StoreReader(mockTransportClient, addressSelector, new AddressEnumerator(), sessionContainer, false); - consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false, accountConfigurationProperties: accountConfigurationProperties); + consistencyWriter = new ConsistencyWriter(addressSelector, sessionContainer, mockTransportClient, mockServiceConfigReader.Object, mockAuthorizationTokenProvider.Object, false, false); response = consistencyWriter.WriteAsync(entity, new TimeoutHelper(TimeSpan.FromSeconds(3000)), false).Result; Assert.Fail(); }