Skip to content
Merged
11 changes: 11 additions & 0 deletions Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,17 @@ public Func<HttpClient> HttpClientFactory
/// </remarks>
public AvailabilityStrategy AvailabilityStrategy { get; set; }

/// <summary>
Comment thread
dibahlfi marked this conversation as resolved.
Outdated
/// Provides access to the maximum number of in-region retries for session retry policy.
/// If a value of less than or equal to 0 is provided we will not retry and yield after the original attempt, otherwise default is going to be 1
/// use value of less than or equal to for this parameter with care as this will have negative consequences for multi-master and some other scenerios.
/// </summary>
internal int MaxInRegionRetryCountForSessionRetry
{
get => this.SessionRetryOptions.MaxInRegionRetryCount;
set => this.SessionRetryOptions.MaxInRegionRetryCount = value;
}

/// <summary>
/// Provides SessionTokenMismatchRetryPolicy optimization through customer supplied region switch hints,
/// which guide SDK-internal retry policies on how early to fallback to the next applicable region.
Expand Down
12 changes: 12 additions & 0 deletions Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,18 @@ public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosm
return this;
}

/// <summary>
Comment thread
dibahlfi marked this conversation as resolved.
Outdated
/// Sets the maximum number of in-region retries for session retry policy.
/// </summary>
/// <param name="maxInRegionRetryCount">The maximum number of retries within each region for read and write operations.
/// use value of less than or equal to 0 for this parameter with care as this will have negative consequences for multi-master and some other scenerios.</param>
/// <returns>The <see cref="CosmosClientBuilder"/> object</returns>
internal CosmosClientBuilder WithMaxInRegionRetryCountForSessionRetry(int maxInRegionRetryCount)
{
this.clientOptions.MaxInRegionRetryCountForSessionRetry = maxInRegionRetryCount;
return this;
}

/// <summary>
/// Provides SessionTokenMismatchRetryPolicy optimization through customer supplied region switch hints,
/// which guide SDK-internal retry policies on how early to fallback to the next applicable region.
Expand Down
6 changes: 3 additions & 3 deletions Microsoft.Azure.Cosmos/src/SessionRetryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public SessionRetryOptions()
/// replication latency between the regions you chose
/// </summary>
public TimeSpan MinInRegionRetryTime { get; private set; }

/// <summary>
/// Sets the maximum number of retries within each region for read and write operations. The minimum value is 1 - the backoff time for the last in-region retry will ensure that the total retry time within the
/// Sets the maximum number of retries within each region for read and write operations - the backoff time for the last in-region retry will ensure that the total retry time within the
/// region is at least the min. in-region retry time.
/// </summary>
public int MaxInRegionRetryCount { get; private set; }
public int MaxInRegionRetryCount { get; internal set; }
Comment thread
dibahlfi marked this conversation as resolved.
Outdated

/// <summary>
/// hints which guide SDK-internal retry policies on how early to switch retries to a different region. If true, will retry all replicas once and add a minimum delay before switching to the next region.If false, it will
Expand Down
13 changes: 5 additions & 8 deletions Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,10 @@ internal static class ConfigurationManager
internal static readonly int MinMinInRegionRetryTimeForWritesInMs = 100;

/// <summary>
/// intent is If a client specify a value, we will force it to be atleast 1, otherwise default is going to be 1(right now both the values are 1 but we have the provision to change them in future).
/// If a client specify a value less than or equal to 0 we will not retry and yield after the original attempt, otherwise default is going to be 1
Comment thread
dibahlfi marked this conversation as resolved.
Outdated
/// </summary>
internal static readonly string MaxRetriesInLocalRegionWhenRemoteRegionPreferred = "AZURE_COSMOS_MAX_RETRIES_IN_LOCAL_REGION_WHEN_REMOTE_REGION_PREFERRED";
internal static readonly int DefaultMaxRetriesInLocalRegionWhenRemoteRegionPreferred = 1;
internal static readonly int MinMaxRetriesInLocalRegionWhenRemoteRegionPreferred = 1;

/// <summary>
/// A read-only string containing the environment variable name for enabling binary encoding. This will eventually
Expand Down Expand Up @@ -112,12 +111,10 @@ public static T GetEnvironmentVariable<T>(string variable, T defaultValue)

public static int GetMaxRetriesInLocalRegionWhenRemoteRegionPreferred()
{
Comment thread
dibahlfi marked this conversation as resolved.
return Math.Max(
ConfigurationManager
.GetEnvironmentVariable(
variable: MaxRetriesInLocalRegionWhenRemoteRegionPreferred,
defaultValue: DefaultMaxRetriesInLocalRegionWhenRemoteRegionPreferred),
MinMaxRetriesInLocalRegionWhenRemoteRegionPreferred);
return ConfigurationManager
.GetEnvironmentVariable(
variable: MaxRetriesInLocalRegionWhenRemoteRegionPreferred,
defaultValue: DefaultMaxRetriesInLocalRegionWhenRemoteRegionPreferred);
}

public static TimeSpan GetMinRetryTimeInLocalRegionWhenRemoteRegionPreferred()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ public async Task TestInitAsync()
[DataRow(FaultInjectionOperationType.QueryItem, 1, true, DisplayName = "Validate Query Item operation with remote region preferred.")]
[DataRow(FaultInjectionOperationType.ReadItem, 2, false, DisplayName = "Validate Read Item operation with local region preferred.")]
[DataRow(FaultInjectionOperationType.QueryItem, 2, false, DisplayName = "Validate Query Item operation with local region preferred.")]
[DataRow(FaultInjectionOperationType.ReadItem, 0, true, DisplayName = "Validate Read Item operation with 0 sessionTokenMismatchRetryAttempts.")]
[DataRow(FaultInjectionOperationType.QueryItem, -1, true, DisplayName = "Validate Query Item operation with negative sessionTokenMismatchRetryAttempts.")]
Comment thread
dibahlfi marked this conversation as resolved.
Outdated
[TestCategory("MultiMaster")]
public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOperationType faultInjectionOperationType,
int sessionTokenMismatchRetryAttempts, Boolean remoteRegionPreferred)
{
string[] preferredRegions = this.writeRegionMap.Keys.ToArray();
Environment.SetEnvironmentVariable(ConfigurationManager.MinInRegionRetryTimeForWritesInMs, "100");
Environment.SetEnvironmentVariable(ConfigurationManager.MaxRetriesInLocalRegionWhenRemoteRegionPreferred, Convert.ToString(sessionTokenMismatchRetryAttempts));

try
{
// if I go to first region for reading an item, I should get a 404/2002 response for 10 minutes
Expand All @@ -71,6 +73,7 @@ public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOper
ConsistencyLevel = ConsistencyLevel.Session,
ApplicationPreferredRegions = preferredRegions,
ConnectionMode = ConnectionMode.Direct,
MaxInRegionRetryCountForSessionRetry = sessionTokenMismatchRetryAttempts
};

using (CosmosClient faultInjectionClient = new CosmosClient(
Expand Down Expand Up @@ -106,14 +109,14 @@ public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOper

if (remoteRegionPreferred)
{
Assert.IsTrue(hitCount >= sessionTokenMismatchRetryAttempts && hitCount <= (1 + sessionTokenMismatchRetryAttempts) * 4);
int effectiveRetryAttempts = Math.Max(sessionTokenMismatchRetryAttempts, 0);
Assert.IsTrue(hitCount >= effectiveRetryAttempts && hitCount <= (1 + effectiveRetryAttempts) * 4);
}
}
}
finally
{
Environment.SetEnvironmentVariable(ConfigurationManager.MinInRegionRetryTimeForWritesInMs, null);
Environment.SetEnvironmentVariable(ConfigurationManager.MaxRetriesInLocalRegionWhenRemoteRegionPreferred, null);
}
}

Expand All @@ -128,14 +131,15 @@ public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOper
[DataRow(FaultInjectionOperationType.DeleteItem, 2, false, DisplayName = "Validate Delete Item operation with local region preferred.")]
[DataRow(FaultInjectionOperationType.UpsertItem, 1, false, DisplayName = "Validate Upsert Item operation with local region preferred.")]
[DataRow(FaultInjectionOperationType.PatchItem, 1, false, DisplayName = "Validate Patch Item operation with remote region preferred.")]
[DataRow(FaultInjectionOperationType.CreateItem, 0, true, DisplayName = "Validate Write Item operation with 0 sessionTokenMismatchRetryAttempts.")]
[DataRow(FaultInjectionOperationType.ReplaceItem, -1, true, DisplayName = "Validate Replace Item operation with negative sessionTokenMismatchRetryAttempts.")]
[TestCategory("MultiMaster")]
public async Task WriteOperationWithReadSessionUnavailableTest(FaultInjectionOperationType faultInjectionOperationType,
int sessionTokenMismatchRetryAttempts, Boolean remoteRegionPreferred)
{

string[] preferredRegions = this.writeRegionMap.Keys.ToArray();
Environment.SetEnvironmentVariable(ConfigurationManager.MinInRegionRetryTimeForWritesInMs, "100");
Environment.SetEnvironmentVariable(ConfigurationManager.MaxRetriesInLocalRegionWhenRemoteRegionPreferred, Convert.ToString(sessionTokenMismatchRetryAttempts));

try
{
Expand All @@ -161,6 +165,7 @@ public async Task WriteOperationWithReadSessionUnavailableTest(FaultInjectionOpe
ConsistencyLevel = ConsistencyLevel.Session,
ApplicationPreferredRegions = preferredRegions,
ConnectionMode = ConnectionMode.Direct,
MaxInRegionRetryCountForSessionRetry = sessionTokenMismatchRetryAttempts
};

using (CosmosClient faultInjectionClient = new CosmosClient(
Expand Down Expand Up @@ -193,7 +198,9 @@ public async Task WriteOperationWithReadSessionUnavailableTest(FaultInjectionOpe
if (remoteRegionPreferred)
{
// higher hit count is possible while in MinRetryWaitTimeWithinRegion
Assert.IsTrue(hitCount >= sessionTokenMismatchRetryAttempts);
int effectiveRetryAttempts = Math.Max(sessionTokenMismatchRetryAttempts, 0);
// higher hit count is possible while in MinRetryWaitTimeWithinRegion
Assert.IsTrue(hitCount >= effectiveRetryAttempts);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ public void SessionRetryOptionsValidValuesTest()
}

}
[TestMethod]
public void CosmosClientOptions_SetMaxInRegionRetryCountForSessionRetry_AllowsNonPositiveValues()
{
CosmosClientOptions options = new CosmosClientOptions
{
// Set to zero
MaxInRegionRetryCountForSessionRetry = 0
};
Assert.AreEqual(0, options.SessionRetryOptions.MaxInRegionRetryCount, "Should allow setting MaxInRegionRetryCount to 0 via CosmosClientOptions");

// Set to negative
options.MaxInRegionRetryCountForSessionRetry = -10;
Assert.AreEqual(-10, options.SessionRetryOptions.MaxInRegionRetryCount, "Should allow setting MaxInRegionRetryCount to negative value via CosmosClientOptions");
}

[TestMethod]
public void SessionRetryOptionsDefaultValuesTest()
Expand Down
Loading