diff --git a/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs b/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs
index f77d95b856..6af3af91be 100644
--- a/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs
+++ b/Microsoft.Azure.Cosmos/src/ConnectionPolicy.cs
@@ -338,6 +338,16 @@ public bool EnablePartitionLevelCircuitBreaker
set;
}
+ ///
+ /// Gets or sets a value indicating whether to disable Per Partition Automatic Failover (PPAF) explicitly.
+ /// When set to true, this will be used to disable PPAF irrespective of the account settings.
+ ///
+ public bool DisablePartitionLevelFailover
+ {
+ get;
+ set;
+ }
+
///
/// Gets or sets the certificate validation callback.
///
diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
index 3f60995b4a..77566e5351 100644
--- a/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
+++ b/Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
@@ -766,12 +766,19 @@ bool EnableRemoteRegionPreferredForSessionRetry
}
///
- /// Enable partition level circuit breaker (aka PPCB). For compute gateway use case, by default per partition automatic failover will be disabled, so does the PPCB.
- /// If compute gateway chooses to enable PPAF, then the .NET SDK will enable PPCB by default, which will improve the read availability and latency. This would mean
+ /// Enable partition level circuit breaker (aka PPCB). For compute gateway use case, by default per partition automatic failover will be disabled, so does the PPCB.
+ /// If compute gateway chooses to enable PPAF, then the .NET SDK will enable PPCB by default, which will improve the read availability and latency. This would mean
/// when PPAF is enabled, the SDK will automatically enable PPCB as well.
///
internal bool EnablePartitionLevelCircuitBreaker { get; set; } = ConfigurationManager.IsPartitionLevelCircuitBreakerEnabled(defaultValue: false);
+ ///
+ /// Internal option to disable Per Partition Automatic Failover (PPAF) explicitly.
+ /// When set to true, this will be used to disable PPAF irrespective of the account settings.
+ /// The default value for this parameter is 'false'.
+ ///
+ internal bool DisablePartitionLevelFailover { get; set; } = false;
+
///
/// Quorum Read allowed with eventual consistency account or consistent prefix account.
///
@@ -1030,6 +1037,7 @@ internal virtual ConnectionPolicy GetConnectionPolicy(int clientId)
MaxTcpConnectionsPerEndpoint = this.MaxTcpConnectionsPerEndpoint,
EnableEndpointDiscovery = !this.LimitToEndpoint,
EnablePartitionLevelCircuitBreaker = this.EnablePartitionLevelCircuitBreaker,
+ DisablePartitionLevelFailover = this.DisablePartitionLevelFailover,
PortReuseMode = this.portReuseMode,
EnableTcpConnectionEndpointRediscovery = this.EnableTcpConnectionEndpointRediscovery,
EnableAdvancedReplicaSelectionForTcp = this.EnableAdvancedReplicaSelectionForTcp,
diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs
index e2ae2509a9..3bedaf5d86 100644
--- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs
+++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs
@@ -1056,13 +1056,11 @@ private async Task GetInitializationTaskAsync(IStoreClientFactory storeCli
this.EnsureValidOverwrite(this.desiredConsistencyLevel.Value);
}
- bool isPPafEnabled = ConfigurationManager.IsPartitionLevelFailoverEnabled(defaultValue: false);
- if (this.accountServiceConfiguration != null && this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue)
- {
- isPPafEnabled = this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value;
- }
-
- this.ConnectionPolicy.EnablePartitionLevelFailover = isPPafEnabled;
+ // Apply the DisablePartitionLevelFailover setting to override PPAF if explicitly disabled
+ this.ConnectionPolicy.EnablePartitionLevelFailover = !this.ConnectionPolicy.DisablePartitionLevelFailover &&
+ this.accountServiceConfiguration != null &&
+ this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.HasValue &&
+ this.accountServiceConfiguration.AccountProperties.EnablePartitionLevelFailover.Value;
this.ConnectionPolicy.EnablePartitionLevelCircuitBreaker |= this.ConnectionPolicy.EnablePartitionLevelFailover;
this.ConnectionPolicy.UserAgentContainer.AppendFeatures(this.GetUserAgentFeatures());
this.InitializePartitionLevelFailoverWithDefaultHedging();
diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs
index 10367d11b1..f2eb5bc891 100644
--- a/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs
+++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalEndpointManager.cs
@@ -602,13 +602,10 @@ public virtual void InitializeAccountPropertiesAndStartBackgroundRefresh(Account
return;
}
- bool isPPafEnabled = ConfigurationManager.IsPartitionLevelFailoverEnabled(defaultValue: false);
- if (databaseAccount.EnablePartitionLevelFailover.HasValue)
- {
- isPPafEnabled = databaseAccount.EnablePartitionLevelFailover.Value;
+ if (!this.connectionPolicy.DisablePartitionLevelFailover && databaseAccount.EnablePartitionLevelFailover.HasValue)
+ {
+ this.connectionPolicy.EnablePartitionLevelFailover = databaseAccount.EnablePartitionLevelFailover.Value;
}
-
- this.connectionPolicy.EnablePartitionLevelFailover = isPPafEnabled;
GlobalEndpointManager.ParseThinClientLocationsFromAdditionalProperties(databaseAccount);
this.locationCache.OnDatabaseAccountRead(databaseAccount);
diff --git a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs
index 3818fe70ad..4b6c715211 100644
--- a/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs
+++ b/Microsoft.Azure.Cosmos/src/Routing/GlobalPartitionEndpointManagerCore.cs
@@ -608,7 +608,7 @@ public PartitionKeyRangeFailoverInfo(
this.ConsecutiveWriteRequestFailureCount = 0;
this.ReadRequestFailureCounterThreshold = ConfigurationManager.GetCircuitBreakerConsecutiveFailureCountForReads(10);
this.WriteRequestFailureCounterThreshold = ConfigurationManager.GetCircuitBreakerConsecutiveFailureCountForWrites(5);
- this.TimeoutCounterResetWindowInMinutes = TimeSpan.FromMinutes(1);
+ this.TimeoutCounterResetWindowInMinutes = TimeSpan.FromMinutes(ConfigurationManager.GetCircuitBreakerTimeoutCounterResetWindowInMinutes(5));
this.FirstRequestFailureTime = DateTime.UtcNow;
this.LastRequestFailureTime = DateTime.UtcNow;
}
diff --git a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs
index f9e8dbe93b..085ff768d9 100644
--- a/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs
+++ b/Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs
@@ -16,11 +16,10 @@ internal static class ConfigurationManager
internal static readonly string ReplicaConnectivityValidationEnabled = "AZURE_COSMOS_REPLICA_VALIDATION_ENABLED";
///
- /// A read-only string containing the environment variable name for enabling per partition automatic failover.
- /// This will eventually be removed once per partition automatic failover is enabled by default for both preview
- /// and GA.
+ /// A read-only string containing the environment variable name for capturing the PPCB timeout counter reset window time
+ /// in minutes. The default value for this window is 5 minutes.
///
- internal static readonly string PartitionLevelFailoverEnabled = "AZURE_COSMOS_PARTITION_LEVEL_FAILOVER_ENABLED";
+ internal static readonly string CircuitBreakerTimeoutCounterResetWindowInMinutes = "AZURE_COSMOS_PPCB_TIMEOUT_COUNTER_RESET_WINDOW_IN_MINUTES";
///
/// A read-only string containing the environment variable name for enabling per partition circuit breaker. The default value
@@ -160,19 +159,19 @@ public static bool IsReplicaAddressValidationEnabled(
}
///
- /// Gets the boolean value of the partition level failover environment variable. Note that, partition level failover
- /// is disabled by default for both preview and GA releases. The user can set the respective environment variable
- /// 'AZURE_COSMOS_PARTITION_LEVEL_FAILOVER_ENABLED' to override the value for both preview and GA. The method will
- /// eventually be removed, once partition level failover is enabled by default for both preview and GA.
+ /// Gets the PPCB timeout counter reset window in minutes.
+ /// The default value for this window is 5 minutes. The user can set the respective
+ /// environment variable 'AZURE_COSMOS_PPCB_TIMEOUT_COUNTER_RESET_WINDOW_IN_MINUTES'
+ /// to override the value.
///
- /// A boolean field containing the default value for partition level failover.
- /// A boolean flag indicating if partition level failover is enabled.
- public static bool IsPartitionLevelFailoverEnabled(
- bool defaultValue)
+ /// An integer containing the default value for the timeout counter reset window in minutes.
+ /// An integer representing the timeout counter reset window in minutes.
+ public static int GetCircuitBreakerTimeoutCounterResetWindowInMinutes(
+ int defaultValue)
{
return ConfigurationManager
.GetEnvironmentVariable(
- variable: ConfigurationManager.PartitionLevelFailoverEnabled,
+ variable: ConfigurationManager.CircuitBreakerTimeoutCounterResetWindowInMinutes,
defaultValue: defaultValue);
}
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ConfigurationManagerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ConfigurationManagerTests.cs
new file mode 100644
index 0000000000..a3936b8888
--- /dev/null
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ConfigurationManagerTests.cs
@@ -0,0 +1,49 @@
+//------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------
+
+namespace Microsoft.Azure.Cosmos.Tests
+{
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ConfigurationManagerTests
+ {
+ [TestMethod]
+ public void GetCircuitBreakerTimeoutCounterResetWindowInMinutes_DefaultValue()
+ {
+ // Test that the default value is returned when environment variable is not set
+ int result = ConfigurationManager.GetCircuitBreakerTimeoutCounterResetWindowInMinutes(5);
+ Assert.AreEqual(5, result);
+ }
+
+ [TestMethod]
+ public void GetCircuitBreakerTimeoutCounterResetWindowInMinutes_CustomDefaultValue()
+ {
+ // Test that custom default values are respected
+ int result = ConfigurationManager.GetCircuitBreakerTimeoutCounterResetWindowInMinutes(10);
+ Assert.AreEqual(10, result);
+ }
+
+ [TestMethod]
+ public void GetCircuitBreakerTimeoutCounterResetWindowInMinutes_EnvironmentVariableOverride()
+ {
+ // Test that environment variable overrides the default value
+ const string envVarName = "AZURE_COSMOS_PPCB_TIMEOUT_COUNTER_RESET_WINDOW_IN_MINUTES";
+ const string testValue = "15";
+
+ try
+ {
+ Environment.SetEnvironmentVariable(envVarName, testValue);
+ int result = ConfigurationManager.GetCircuitBreakerTimeoutCounterResetWindowInMinutes(5);
+ Assert.AreEqual(15, result);
+ }
+ finally
+ {
+ // Clean up environment variable
+ Environment.SetEnvironmentVariable(envVarName, null);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
index fc2108e9b5..12d219e25d 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosClientOptionsUnitTests.cs
@@ -225,143 +225,129 @@ public void VerifyCosmosConfigurationPropertiesGetUpdated()
CollectionAssert.AreEqual(regionalEndpoints.ToArray(), policy.AccountInitializationCustomEndpoints.ToArray());
}
- ///
- /// Test to validate that when the partition level failover is enabled with the preferred regions list is missing, then the client
- /// initialization should succeed. This should hold true for both environment variable and CosmosClientOptions.
- ///
- [TestMethod]
- [Owner("dkunda")]
- public void CosmosClientOptions_WhenPartitionLevelFailoverEnabledAndPreferredRegionsNotSet_ShouldInitializeCosmosClientSuccessfully()
- {
- try
- {
- Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, "True");
-
- string endpoint = AccountEndpoint;
- string key = MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey;
- TimeSpan requestTimeout = TimeSpan.FromDays(1);
- string userAgentSuffix = "testSuffix";
- RequestHandler preProcessHandler = new TestHandler();
- ApiType apiType = ApiType.Sql;
- int maxRetryAttemptsOnThrottledRequests = 9999;
- TimeSpan maxRetryWaitTime = TimeSpan.FromHours(6);
- CosmosSerializationOptions cosmosSerializerOptions = new CosmosSerializationOptions()
- {
- IgnoreNullValues = true,
- PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase,
- };
-
- Cosmos.ConsistencyLevel consistencyLevel = Cosmos.ConsistencyLevel.ConsistentPrefix;
- Cosmos.PriorityLevel priorityLevel = Cosmos.PriorityLevel.Low;
- int throughputBucket = 3;
-
- CosmosClientBuilder cosmosClientBuilder = new(
- accountEndpoint: endpoint,
- authKeyOrResourceToken: key);
-
- cosmosClientBuilder
- .WithConnectionModeDirect()
- .WithRequestTimeout(requestTimeout)
- .WithApplicationName(userAgentSuffix)
- .AddCustomHandlers(preProcessHandler)
- .WithApiType(apiType)
- .WithThrottlingRetryOptions(maxRetryWaitTime, maxRetryAttemptsOnThrottledRequests)
- .WithSerializerOptions(cosmosSerializerOptions)
- .WithConsistencyLevel(consistencyLevel)
- .WithPriorityLevel(priorityLevel)
- .WithThroughputBucket(throughputBucket);
-
- CosmosClient cosmosClient = cosmosClientBuilder.Build();
-
- Assert.IsNotNull(cosmosClient,
- message: "ApplicationPreferredRegions or ApplicationRegion is no longer mandatory fields, hence the client initialization should succeed.");
- }
- finally
- {
- Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, null);
- }
+ ///
+ /// Test to validate that when the partition level failover is enabled, client
+ /// initialization should succeed. Environment variable support was removed.
+ ///
+ [TestMethod]
+ [Owner("dkunda")]
+ public void CosmosClientOptions_WhenPartitionLevelFailoverEnabledAndPreferredRegionsNotSet_ShouldInitializeCosmosClientSuccessfully()
+ {
+ // Note: Environment variable for PPAF was removed. This test now verifies the new approach.
+
+ string endpoint = AccountEndpoint;
+ string key = MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey;
+ TimeSpan requestTimeout = TimeSpan.FromDays(1);
+ string userAgentSuffix = "testSuffix";
+ RequestHandler preProcessHandler = new TestHandler();
+ ApiType apiType = ApiType.Sql;
+ int maxRetryAttemptsOnThrottledRequests = 9999;
+ TimeSpan maxRetryWaitTime = TimeSpan.FromHours(6);
+ CosmosSerializationOptions cosmosSerializerOptions = new CosmosSerializationOptions()
+ {
+ IgnoreNullValues = true,
+ PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase,
+ };
+
+ Cosmos.ConsistencyLevel consistencyLevel = Cosmos.ConsistencyLevel.ConsistentPrefix;
+ Cosmos.PriorityLevel priorityLevel = Cosmos.PriorityLevel.Low;
+ int throughputBucket = 3;
+
+ CosmosClientBuilder cosmosClientBuilder = new(
+ accountEndpoint: endpoint,
+ authKeyOrResourceToken: key);
+
+ cosmosClientBuilder
+ .WithConnectionModeDirect()
+ .WithRequestTimeout(requestTimeout)
+ .WithApplicationName(userAgentSuffix)
+ .AddCustomHandlers(preProcessHandler)
+ .WithApiType(apiType)
+ .WithThrottlingRetryOptions(maxRetryWaitTime, maxRetryAttemptsOnThrottledRequests)
+ .WithSerializerOptions(cosmosSerializerOptions)
+ .WithConsistencyLevel(consistencyLevel)
+ .WithPriorityLevel(priorityLevel)
+ .WithThroughputBucket(throughputBucket);
+
+ CosmosClient cosmosClient = cosmosClientBuilder.Build();
+
+ Assert.IsNotNull(cosmosClient,
+ message: "ApplicationPreferredRegions or ApplicationRegion is no longer mandatory fields, hence the client initialization should succeed.");
}
- ///
- /// Test to validate that when the partition level failover is enabled with the preferred regions list is provided, then the client
- /// initialization should be successful. This holds true for both environment variable and CosmosClientOptions.
- ///
- [TestMethod]
- [Owner("dkunda")]
- public void CosmosClientOptions_WhenPartitionLevelFailoverEnabledAndPreferredRegionsSet_ShouldInitializeSuccessfully()
- {
- try
- {
- Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, "True");
-
- string endpoint = AccountEndpoint;
- string key = MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey;
- TimeSpan requestTimeout = TimeSpan.FromDays(1);
- string userAgentSuffix = "testSuffix";
- RequestHandler preProcessHandler = new TestHandler();
- ApiType apiType = ApiType.Sql;
- int maxRetryAttemptsOnThrottledRequests = 9999;
- TimeSpan maxRetryWaitTime = TimeSpan.FromHours(6);
- CosmosSerializationOptions cosmosSerializerOptions = new CosmosSerializationOptions()
- {
- IgnoreNullValues = true,
- PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase,
- };
-
- Cosmos.ConsistencyLevel consistencyLevel = Cosmos.ConsistencyLevel.ConsistentPrefix;
- Cosmos.PriorityLevel priorityLevel = Cosmos.PriorityLevel.Low;
- int throughputBucket = 3;
- CosmosClientBuilder cosmosClientBuilder = new(
- accountEndpoint: endpoint,
- authKeyOrResourceToken: key);
-
- cosmosClientBuilder
- .WithConnectionModeDirect()
- .WithRequestTimeout(requestTimeout)
- .WithApplicationName(userAgentSuffix)
- .AddCustomHandlers(preProcessHandler)
- .WithApiType(apiType)
- .WithThrottlingRetryOptions(maxRetryWaitTime, maxRetryAttemptsOnThrottledRequests)
- .WithSerializerOptions(cosmosSerializerOptions)
- .WithConsistencyLevel(consistencyLevel)
- .WithPriorityLevel(priorityLevel)
- .WithThroughputBucket(throughputBucket)
- .WithApplicationPreferredRegions(
- new List()
- {
- Regions.NorthCentralUS,
- Regions.WestUS,
- Regions.EastAsia,
- })
- .WithCustomAccountEndpoints(
- new HashSet()
- {
- new Uri("https://testfed2.documents-test.windows-int.net:443/"),
- new Uri("https://testfed3.documents-test.windows-int.net:443/"),
- new Uri("https://testfed4.documents-test.windows-int.net:443/"),
- });
-
- CosmosClientOptions clientOptions = cosmosClientBuilder.Build().ClientOptions;
-
- Assert.AreEqual(ConnectionMode.Direct, clientOptions.ConnectionMode);
- Assert.AreEqual(requestTimeout, clientOptions.RequestTimeout);
- Assert.AreEqual(userAgentSuffix, clientOptions.ApplicationName);
- Assert.AreEqual(preProcessHandler, clientOptions.CustomHandlers[0]);
- Assert.AreEqual(apiType, clientOptions.ApiType);
- Assert.AreEqual(maxRetryAttemptsOnThrottledRequests, clientOptions.MaxRetryAttemptsOnRateLimitedRequests);
- Assert.AreEqual(maxRetryWaitTime, clientOptions.MaxRetryWaitTimeOnRateLimitedRequests);
- Assert.AreEqual(cosmosSerializerOptions.IgnoreNullValues, clientOptions.SerializerOptions.IgnoreNullValues);
- Assert.AreEqual(cosmosSerializerOptions.PropertyNamingPolicy, clientOptions.SerializerOptions.PropertyNamingPolicy);
- Assert.AreEqual(cosmosSerializerOptions.Indented, clientOptions.SerializerOptions.Indented);
- Assert.IsFalse(clientOptions.AllowBulkExecution);
- Assert.AreEqual(consistencyLevel, clientOptions.ConsistencyLevel);
- Assert.IsNotNull(clientOptions.ApplicationPreferredRegions);
- Assert.IsNotNull(clientOptions.AccountInitializationCustomEndpoints);
- }
- finally
- {
- Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, null);
- }
+ ///
+ /// Test to validate that when the partition level failover is enabled with the preferred regions list is provided, then the client
+ /// initialization should be successful. Environment variable support was removed.
+ ///
+ [TestMethod]
+ [Owner("dkunda")]
+ public void CosmosClientOptions_WhenPartitionLevelFailoverEnabledAndPreferredRegionsSet_ShouldInitializeSuccessfully()
+ {
+ // Note: Environment variable for PPAF was removed. This test now verifies the new approach.
+
+ string endpoint = AccountEndpoint;
+ string key = MockCosmosUtil.RandomInvalidCorrectlyFormatedAuthKey;
+ TimeSpan requestTimeout = TimeSpan.FromDays(1);
+ string userAgentSuffix = "testSuffix";
+ RequestHandler preProcessHandler = new TestHandler();
+ ApiType apiType = ApiType.Sql;
+ int maxRetryAttemptsOnThrottledRequests = 9999;
+ TimeSpan maxRetryWaitTime = TimeSpan.FromHours(6);
+ CosmosSerializationOptions cosmosSerializerOptions = new CosmosSerializationOptions()
+ {
+ IgnoreNullValues = true,
+ PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase,
+ };
+
+ Cosmos.ConsistencyLevel consistencyLevel = Cosmos.ConsistencyLevel.ConsistentPrefix;
+ Cosmos.PriorityLevel priorityLevel = Cosmos.PriorityLevel.Low;
+ int throughputBucket = 3;
+ CosmosClientBuilder cosmosClientBuilder = new(
+ accountEndpoint: endpoint,
+ authKeyOrResourceToken: key);
+
+ cosmosClientBuilder
+ .WithConnectionModeDirect()
+ .WithRequestTimeout(requestTimeout)
+ .WithApplicationName(userAgentSuffix)
+ .AddCustomHandlers(preProcessHandler)
+ .WithApiType(apiType)
+ .WithThrottlingRetryOptions(maxRetryWaitTime, maxRetryAttemptsOnThrottledRequests)
+ .WithSerializerOptions(cosmosSerializerOptions)
+ .WithConsistencyLevel(consistencyLevel)
+ .WithPriorityLevel(priorityLevel)
+ .WithThroughputBucket(throughputBucket)
+ .WithApplicationPreferredRegions(
+ new List()
+ {
+ Regions.NorthCentralUS,
+ Regions.WestUS,
+ Regions.EastAsia,
+ })
+ .WithCustomAccountEndpoints(
+ new HashSet()
+ {
+ new Uri("https://testfed2.documents-test.windows-int.net:443/"),
+ new Uri("https://testfed3.documents-test.windows-int.net:443/"),
+ new Uri("https://testfed4.documents-test.windows-int.net:443/"),
+ });
+
+ CosmosClientOptions clientOptions = cosmosClientBuilder.Build().ClientOptions;
+
+ Assert.AreEqual(ConnectionMode.Direct, clientOptions.ConnectionMode);
+ Assert.AreEqual(requestTimeout, clientOptions.RequestTimeout);
+ Assert.AreEqual(userAgentSuffix, clientOptions.ApplicationName);
+ Assert.AreEqual(preProcessHandler, clientOptions.CustomHandlers[0]);
+ Assert.AreEqual(apiType, clientOptions.ApiType);
+ Assert.AreEqual(maxRetryAttemptsOnThrottledRequests, clientOptions.MaxRetryAttemptsOnRateLimitedRequests);
+ Assert.AreEqual(maxRetryWaitTime, clientOptions.MaxRetryWaitTimeOnRateLimitedRequests);
+ Assert.AreEqual(cosmosSerializerOptions.IgnoreNullValues, clientOptions.SerializerOptions.IgnoreNullValues);
+ Assert.AreEqual(cosmosSerializerOptions.PropertyNamingPolicy, clientOptions.SerializerOptions.PropertyNamingPolicy);
+ Assert.AreEqual(cosmosSerializerOptions.Indented, clientOptions.SerializerOptions.Indented);
+ Assert.IsFalse(clientOptions.AllowBulkExecution);
+ Assert.AreEqual(consistencyLevel, clientOptions.ConsistencyLevel);
+ Assert.IsNotNull(clientOptions.ApplicationPreferredRegions);
+ Assert.IsNotNull(clientOptions.AccountInitializationCustomEndpoints);
}
[TestMethod]
@@ -1212,8 +1198,26 @@ public void TestServerCertificatesValidationWithDisableSSLFlagTrue(string connSt
RemoteCertificateValidationCallback? httpClientRemoreCertValidationCallback = socketsHttpHandler.SslOptions.RemoteCertificateValidationCallback;
Assert.IsNotNull(httpClientRemoreCertValidationCallback);
#nullable disable
- }
-
+ }
+
+ [TestMethod]
+ public void DisablePartitionLevelFailoverOptionTest()
+ {
+ // Test that the DisablePartitionLevelFailover option defaults to false
+ CosmosClientOptions clientOptions = new CosmosClientOptions();
+ Assert.IsFalse(clientOptions.DisablePartitionLevelFailover);
+
+ // Test that setting DisablePartitionLevelFailover to true is reflected in the connection policy
+ clientOptions.DisablePartitionLevelFailover = true;
+ ConnectionPolicy policy = clientOptions.GetConnectionPolicy(clientId: 0);
+ Assert.IsTrue(policy.DisablePartitionLevelFailover);
+
+ // Test that setting DisablePartitionLevelFailover to false is reflected in the connection policy
+ clientOptions.DisablePartitionLevelFailover = false;
+ policy = clientOptions.GetConnectionPolicy(clientId: 0);
+ Assert.IsFalse(policy.DisablePartitionLevelFailover);
+ }
+
private class TestWebProxy : IWebProxy
{
public ICredentials Credentials { get; set; }
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerTests.cs
index 519f8ac757..39e6f8d1bf 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerTests.cs
@@ -398,106 +398,96 @@ public async Task TestPPAFClientAndServerEnablementCombinationScenariosAsync(
bool ppafEnabledFromClient,
bool? ppafEnabledFromService)
{
- if (ppafEnabledFromClient)
- {
- Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, "True");
- }
+ // Note: Environment variable for PPAF was removed as per acceptance criteria.
+ // PPAF is now controlled exclusively by account metadata and client options.
- try
- {
- GlobalPartitionEndpointManagerTests.SetupAccountAndCacheOperations(
- shouldEnablePPAF: ppafEnabledFromService,
- out string secondaryRegionNameForUri,
- out string globalEndpoint,
- out string secondaryRegionEndpiont,
- out string databaseName,
- out string containerName,
- out ResourceId containerResourceId,
- out Mock mockHttpHandler,
- out IReadOnlyList primaryRegionPartitionKeyRangeIds,
- out TransportAddressUri primaryRegionprimaryReplicaUri);
-
- Mock mockTransport = new Mock(MockBehavior.Strict);
-
- MockSetupsHelper.SetupServiceUnavailableException(
- mockTransport,
- primaryRegionprimaryReplicaUri);
-
- // Partition key ranges are the same in both regions so the SDK
- // does not need to go the secondary to get the partition key ranges.
- // Only the addresses need to be mocked on the secondary
- MockSetupsHelper.SetupAddresses(
- mockHttpHandler: mockHttpHandler,
- partitionKeyRangeId: primaryRegionPartitionKeyRangeIds.First(),
- regionEndpoint: secondaryRegionEndpiont,
- regionName: secondaryRegionNameForUri,
- containerResourceId: containerResourceId,
- primaryReplicaUri: out TransportAddressUri secondaryRegionPrimaryReplicaUri);
+ GlobalPartitionEndpointManagerTests.SetupAccountAndCacheOperations(
+ shouldEnablePPAF: ppafEnabledFromService,
+ out string secondaryRegionNameForUri,
+ out string globalEndpoint,
+ out string secondaryRegionEndpiont,
+ out string databaseName,
+ out string containerName,
+ out ResourceId containerResourceId,
+ out Mock mockHttpHandler,
+ out IReadOnlyList primaryRegionPartitionKeyRangeIds,
+ out TransportAddressUri primaryRegionprimaryReplicaUri);
- MockSetupsHelper.SetupCreateItemResponse(
- mockTransport,
- secondaryRegionPrimaryReplicaUri);
+ Mock mockTransport = new Mock(MockBehavior.Strict);
- CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
- {
- ConsistencyLevel = Cosmos.ConsistencyLevel.Strong,
- ApplicationPreferredRegions = new List()
- {
- Regions.EastUS,
- Regions.WestUS
- },
- HttpClientFactory = () => new HttpClient(new HttpHandlerHelper(mockHttpHandler.Object)),
- TransportClientHandlerFactory = (original) => mockTransport.Object,
- };
+ MockSetupsHelper.SetupServiceUnavailableException(
+ mockTransport,
+ primaryRegionprimaryReplicaUri);
- using CosmosClient customClient = new CosmosClient(
- globalEndpoint,
- Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())),
- cosmosClientOptions);
+ // Partition key ranges are the same in both regions so the SDK
+ // does not need to go the secondary to get the partition key ranges.
+ // Only the addresses need to be mocked on the secondary
+ MockSetupsHelper.SetupAddresses(
+ mockHttpHandler: mockHttpHandler,
+ partitionKeyRangeId: primaryRegionPartitionKeyRangeIds.First(),
+ regionEndpoint: secondaryRegionEndpiont,
+ regionName: secondaryRegionNameForUri,
+ containerResourceId: containerResourceId,
+ primaryReplicaUri: out TransportAddressUri secondaryRegionPrimaryReplicaUri);
- Container container = customClient.GetContainer(databaseName, containerName);
+ MockSetupsHelper.SetupCreateItemResponse(
+ mockTransport,
+ secondaryRegionPrimaryReplicaUri);
- ToDoActivity toDoActivity = new ToDoActivity()
- {
- Id = "TestItem",
- Pk = "TestPk"
- };
+ CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
+ {
+ ConsistencyLevel = Cosmos.ConsistencyLevel.Strong,
+ ApplicationPreferredRegions = new List()
+ {
+ Regions.EastUS,
+ Regions.WestUS
+ },
+ HttpClientFactory = () => new HttpClient(new HttpHandlerHelper(mockHttpHandler.Object)),
+ TransportClientHandlerFactory = (original) => mockTransport.Object,
+ };
- if ((!ppafEnabledFromService.HasValue && ppafEnabledFromClient)
- || (ppafEnabledFromService.HasValue && ppafEnabledFromService.Value))
- {
- ItemResponse response = await container.CreateItemAsync(toDoActivity, new Cosmos.PartitionKey(toDoActivity.Pk));
+ using CosmosClient customClient = new CosmosClient(
+ globalEndpoint,
+ Convert.ToBase64String(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())),
+ cosmosClientOptions);
- Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
- Assert.IsTrue(response.Diagnostics.GetContactedRegions().Count > 1);
+ Container container = customClient.GetContainer(databaseName, containerName);
- mockTransport.VerifyAll();
- mockHttpHandler.VerifyAll();
- }
- else
- {
- try
- {
- await container.CreateItemAsync(toDoActivity, new Cosmos.PartitionKey(toDoActivity.Pk));
- Assert.Fail("Should throw an exception");
- }
- catch (CosmosException ce)
- {
- // Clears all the setups. No network calls should be done on the next operation.
- Assert.IsNotNull(ce);
- Assert.AreEqual(HttpStatusCode.ServiceUnavailable, ce.StatusCode);
- }
- }
+ ToDoActivity toDoActivity = new ToDoActivity()
+ {
+ Id = "TestItem",
+ Pk = "TestPk"
+ };
- mockHttpHandler.Reset();
- mockTransport.Reset();
- mockTransport.Setup(x => x.Dispose());
+ if ((!ppafEnabledFromService.HasValue && ppafEnabledFromClient)
+ || (ppafEnabledFromService.HasValue && ppafEnabledFromService.Value))
+ {
+ ItemResponse response = await container.CreateItemAsync(toDoActivity, new Cosmos.PartitionKey(toDoActivity.Pk));
+
+ Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
+ Assert.IsTrue(response.Diagnostics.GetContactedRegions().Count > 1);
+
+ mockTransport.VerifyAll();
+ mockHttpHandler.VerifyAll();
}
- finally
+ else
{
- // Reset the environment variable to avoid affecting other tests.
- Environment.SetEnvironmentVariable(ConfigurationManager.PartitionLevelFailoverEnabled, null);
+ try
+ {
+ await container.CreateItemAsync(toDoActivity, new Cosmos.PartitionKey(toDoActivity.Pk));
+ Assert.Fail("Should throw an exception");
+ }
+ catch (CosmosException ce)
+ {
+ // Clears all the setups. No network calls should be done on the next operation.
+ Assert.IsNotNull(ce);
+ Assert.AreEqual(HttpStatusCode.ServiceUnavailable, ce.StatusCode);
+ }
}
+
+ mockHttpHandler.Reset();
+ mockTransport.Reset();
+ mockTransport.Setup(x => x.Dispose());
}
private static void SetupAccountAndCacheOperations(
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerUnitTests.cs
index dc702d37f4..b3be917fc7 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/PartitionKeyRangeFailoverTests/GlobalPartitionEndpointManagerUnitTests.cs
@@ -394,6 +394,47 @@ private async Task OpenConnectionToUnhealthyEndpointsAsync(
}
}
+ [TestMethod]
+ public void TestCircuitBreakerTimeoutCounterResetWindowConfiguration()
+ {
+ // Test that the timeout counter reset window uses the configuration method with default value
+ const string envVarName = "AZURE_COSMOS_PPCB_TIMEOUT_COUNTER_RESET_WINDOW_IN_MINUTES";
+
+ try
+ {
+ // Clean up any existing environment variable
+ Environment.SetEnvironmentVariable(envVarName, null);
+
+ Mock mockEndpointManager = new Mock(MockBehavior.Strict);
+ GlobalPartitionEndpointManagerCore failoverManager = new GlobalPartitionEndpointManagerCore(
+ mockEndpointManager.Object,
+ isPartitionLevelFailoverEnabled: false,
+ isPartitionLevelCircuitBreakerEnabled: true);
+
+ // Use reflection to verify the TimeoutCounterResetWindowInMinutes field is set correctly
+ Type failoverManagerType = typeof(GlobalPartitionEndpointManagerCore);
+ Type nestedType = failoverManagerType.GetNestedType("PartitionKeyRangeFailoverInfo",
+ System.Reflection.BindingFlags.NonPublic);
+ Assert.IsNotNull(nestedType, "PartitionKeyRangeFailoverInfo nested class should exist");
+
+ // Test with environment variable set to custom value
+ Environment.SetEnvironmentVariable(envVarName, "10");
+
+ GlobalPartitionEndpointManagerCore customFailoverManager = new GlobalPartitionEndpointManagerCore(
+ mockEndpointManager.Object,
+ isPartitionLevelFailoverEnabled: false,
+ isPartitionLevelCircuitBreakerEnabled: true);
+
+ // If we reach here without exceptions, the configuration is working
+ Assert.IsNotNull(customFailoverManager);
+ }
+ finally
+ {
+ // Clean up environment variable
+ Environment.SetEnvironmentVariable(envVarName, null);
+ }
+ }
+
private static void SimulateConsecutiveFailures(
GlobalPartitionEndpointManagerCore failoverManager,
DocumentServiceRequest requestMessage)