Skip to content

Commit 0c4b228

Browse files
committed
fix: provide support to yield after original attempt
1 parent 7be150c commit 0c4b228

6 files changed

Lines changed: 57 additions & 16 deletions

File tree

Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,17 @@ public Func<HttpClient> HttpClientFactory
753753
/// </remarks>
754754
public AvailabilityStrategy AvailabilityStrategy { get; set; }
755755

756+
/// <summary>
757+
/// Provides access to the maximum number of in-region retries for session retry policy.
758+
/// 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
759+
/// 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.
760+
/// </summary>
761+
internal int MaxInRegionRetryCountForSessionRetry
762+
{
763+
get => this.SessionRetryOptions.MaxInRegionRetryCount;
764+
set => this.SessionRetryOptions.MaxInRegionRetryCount = value;
765+
}
766+
756767
/// <summary>
757768
/// Provides SessionTokenMismatchRetryPolicy optimization through customer supplied region switch hints,
758769
/// which guide SDK-internal retry policies on how early to fallback to the next applicable region.

Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,18 @@ public CosmosClientBuilder WithSerializerOptions(CosmosSerializationOptions cosm
583583
return this;
584584
}
585585

586+
/// <summary>
587+
/// Sets the maximum number of in-region retries for session retry policy.
588+
/// </summary>
589+
/// <param name="maxInRegionRetryCount">The maximum number of retries within each region for read and write operations.
590+
/// 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>
591+
/// <returns>The <see cref="CosmosClientBuilder"/> object</returns>
592+
internal CosmosClientBuilder WithMaxInRegionRetryCountForSessionRetry(int maxInRegionRetryCount)
593+
{
594+
this.clientOptions.MaxInRegionRetryCountForSessionRetry = maxInRegionRetryCount;
595+
return this;
596+
}
597+
586598
/// <summary>
587599
/// Provides SessionTokenMismatchRetryPolicy optimization through customer supplied region switch hints,
588600
/// which guide SDK-internal retry policies on how early to fallback to the next applicable region.

Microsoft.Azure.Cosmos/src/SessionRetryOptions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ public SessionRetryOptions()
2626
/// replication latency between the regions you chose
2727
/// </summary>
2828
public TimeSpan MinInRegionRetryTime { get; private set; }
29-
29+
3030
/// <summary>
31-
/// 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
31+
/// 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
3232
/// region is at least the min. in-region retry time.
3333
/// </summary>
34-
public int MaxInRegionRetryCount { get; private set; }
34+
public int MaxInRegionRetryCount { get; internal set; }
3535

3636
/// <summary>
3737
/// 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

Microsoft.Azure.Cosmos/src/Util/ConfigurationManager.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,10 @@ internal static class ConfigurationManager
8080
internal static readonly int MinMinInRegionRetryTimeForWritesInMs = 100;
8181

8282
/// <summary>
83-
/// 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).
83+
/// 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
8484
/// </summary>
8585
internal static readonly string MaxRetriesInLocalRegionWhenRemoteRegionPreferred = "AZURE_COSMOS_MAX_RETRIES_IN_LOCAL_REGION_WHEN_REMOTE_REGION_PREFERRED";
8686
internal static readonly int DefaultMaxRetriesInLocalRegionWhenRemoteRegionPreferred = 1;
87-
internal static readonly int MinMaxRetriesInLocalRegionWhenRemoteRegionPreferred = 1;
8887

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

113112
public static int GetMaxRetriesInLocalRegionWhenRemoteRegionPreferred()
114113
{
115-
return Math.Max(
116-
ConfigurationManager
117-
.GetEnvironmentVariable(
118-
variable: MaxRetriesInLocalRegionWhenRemoteRegionPreferred,
119-
defaultValue: DefaultMaxRetriesInLocalRegionWhenRemoteRegionPreferred),
120-
MinMaxRetriesInLocalRegionWhenRemoteRegionPreferred);
114+
return ConfigurationManager
115+
.GetEnvironmentVariable(
116+
variable: MaxRetriesInLocalRegionWhenRemoteRegionPreferred,
117+
defaultValue: DefaultMaxRetriesInLocalRegionWhenRemoteRegionPreferred);
121118
}
122119

123120
public static TimeSpan GetMinRetryTimeInLocalRegionWhenRemoteRegionPreferred()

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/SessionRetryOptionsTest.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ public async Task TestInitAsync()
3939
[DataRow(FaultInjectionOperationType.QueryItem, 1, true, DisplayName = "Validate Query Item operation with remote region preferred.")]
4040
[DataRow(FaultInjectionOperationType.ReadItem, 2, false, DisplayName = "Validate Read Item operation with local region preferred.")]
4141
[DataRow(FaultInjectionOperationType.QueryItem, 2, false, DisplayName = "Validate Query Item operation with local region preferred.")]
42+
[DataRow(FaultInjectionOperationType.ReadItem, 0, true, DisplayName = "Validate Read Item operation with 0 sessionTokenMismatchRetryAttempts.")]
43+
[DataRow(FaultInjectionOperationType.QueryItem, -1, true, DisplayName = "Validate Query Item operation with negative sessionTokenMismatchRetryAttempts.")]
4244
[TestCategory("MultiMaster")]
4345
public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOperationType faultInjectionOperationType,
4446
int sessionTokenMismatchRetryAttempts, Boolean remoteRegionPreferred)
4547
{
4648
string[] preferredRegions = this.writeRegionMap.Keys.ToArray();
4749
Environment.SetEnvironmentVariable(ConfigurationManager.MinInRegionRetryTimeForWritesInMs, "100");
48-
Environment.SetEnvironmentVariable(ConfigurationManager.MaxRetriesInLocalRegionWhenRemoteRegionPreferred, Convert.ToString(sessionTokenMismatchRetryAttempts));
50+
4951
try
5052
{
5153
// if I go to first region for reading an item, I should get a 404/2002 response for 10 minutes
@@ -71,6 +73,7 @@ public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOper
7173
ConsistencyLevel = ConsistencyLevel.Session,
7274
ApplicationPreferredRegions = preferredRegions,
7375
ConnectionMode = ConnectionMode.Direct,
76+
MaxInRegionRetryCountForSessionRetry = sessionTokenMismatchRetryAttempts
7477
};
7578

7679
using (CosmosClient faultInjectionClient = new CosmosClient(
@@ -106,14 +109,14 @@ public async Task ReadOperationWithReadSessionUnavailableTest(FaultInjectionOper
106109

107110
if (remoteRegionPreferred)
108111
{
109-
Assert.IsTrue(hitCount >= sessionTokenMismatchRetryAttempts && hitCount <= (1 + sessionTokenMismatchRetryAttempts) * 4);
112+
int effectiveRetryAttempts = Math.Max(sessionTokenMismatchRetryAttempts, 0);
113+
Assert.IsTrue(hitCount >= effectiveRetryAttempts && hitCount <= (1 + effectiveRetryAttempts) * 4);
110114
}
111115
}
112116
}
113117
finally
114118
{
115119
Environment.SetEnvironmentVariable(ConfigurationManager.MinInRegionRetryTimeForWritesInMs, null);
116-
Environment.SetEnvironmentVariable(ConfigurationManager.MaxRetriesInLocalRegionWhenRemoteRegionPreferred, null);
117120
}
118121
}
119122

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

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

140144
try
141145
{
@@ -161,6 +165,7 @@ public async Task WriteOperationWithReadSessionUnavailableTest(FaultInjectionOpe
161165
ConsistencyLevel = ConsistencyLevel.Session,
162166
ApplicationPreferredRegions = preferredRegions,
163167
ConnectionMode = ConnectionMode.Direct,
168+
MaxInRegionRetryCountForSessionRetry = sessionTokenMismatchRetryAttempts
164169
};
165170

166171
using (CosmosClient faultInjectionClient = new CosmosClient(
@@ -193,7 +198,9 @@ public async Task WriteOperationWithReadSessionUnavailableTest(FaultInjectionOpe
193198
if (remoteRegionPreferred)
194199
{
195200
// higher hit count is possible while in MinRetryWaitTimeWithinRegion
196-
Assert.IsTrue(hitCount >= sessionTokenMismatchRetryAttempts);
201+
int effectiveRetryAttempts = Math.Max(sessionTokenMismatchRetryAttempts, 0);
202+
// higher hit count is possible while in MinRetryWaitTimeWithinRegion
203+
Assert.IsTrue(hitCount >= effectiveRetryAttempts);
197204
}
198205
}
199206
}

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/SessionRetryOptionsUnitTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ public void SessionRetryOptionsValidValuesTest()
3535
}
3636

3737
}
38+
[TestMethod]
39+
public void CosmosClientOptions_SetMaxInRegionRetryCountForSessionRetry_AllowsNonPositiveValues()
40+
{
41+
CosmosClientOptions options = new CosmosClientOptions
42+
{
43+
// Set to zero
44+
MaxInRegionRetryCountForSessionRetry = 0
45+
};
46+
Assert.AreEqual(0, options.SessionRetryOptions.MaxInRegionRetryCount, "Should allow setting MaxInRegionRetryCount to 0 via CosmosClientOptions");
47+
48+
// Set to negative
49+
options.MaxInRegionRetryCountForSessionRetry = -10;
50+
Assert.AreEqual(-10, options.SessionRetryOptions.MaxInRegionRetryCount, "Should allow setting MaxInRegionRetryCount to negative value via CosmosClientOptions");
51+
}
3852

3953
[TestMethod]
4054
public void SessionRetryOptionsDefaultValuesTest()

0 commit comments

Comments
 (0)