Skip to content

Commit f8f93e5

Browse files
committed
Add more tests for hub region retry
1 parent 84d2dd8 commit f8f93e5

2 files changed

Lines changed: 86 additions & 4 deletions

File tree

Microsoft.Azure.Cosmos/src/ClientRetryPolicy.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,16 +457,21 @@ private ShouldRetryResult ShouldRetryOnSessionNotAvailable(DocumentServiceReques
457457
{
458458
this.addHubRegionProcessingOnlyHeader = true;
459459
}
460-
#endif
460+
461461
if (this.sessionTokenRetryCount > MaxSessionTokenRetryCount)
462+
{
463+
// Hub region header was set at count == MaxSessionTokenRetryCount and the
464+
// request was retried with it. If the hub still returns 404/1002, stop.
465+
return ShouldRetryResult.NoRetry();
466+
}
467+
#else
468+
if (this.sessionTokenRetryCount > 1)
462469
{
463470
// When cannot use multiple write locations, then don't retry the request if
464471
// we have already tried this request on the write location.
465-
// The count threshold is 2 (not 1) because the hub region processing header
466-
// is set after the first retry fails (count=2) and the request must be retried
467-
// once more with that header before giving up.
468472
return ShouldRetryResult.NoRetry();
469473
}
474+
#endif
470475
else
471476
{
472477
this.retryContext = new RetryContext

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,83 @@ public async Task ClientRetryPolicy_HubRegionDiscovery_EndToEnd_DirectMode()
596596
Assert.AreEqual(bool.TrueString, headerValues[0], "Hub region header value should remain 'True'.");
597597
}
598598

599+
/// <summary>
600+
/// Verifies that once the hub region header is set (after two consecutive 404/1002),
601+
/// it persists through subsequent retries triggered by other retriable errors
602+
/// (503 ServiceUnavailable, 408 RequestTimeout) and that the normal preferred-region
603+
/// cycling continues with the header attached.
604+
/// </summary>
605+
[TestMethod]
606+
public async Task ClientRetryPolicy_HubRegionHeader_PersistsThroughRetriableErrors()
607+
{
608+
const bool enableEndpointDiscovery = true;
609+
610+
using GlobalEndpointManager endpointManager = this.Initialize(
611+
useMultipleWriteLocations: false,
612+
enableEndpointDiscovery: enableEndpointDiscovery,
613+
isPreferredLocationsListEmpty: false,
614+
enforceSingleMasterSingleWriteLocation: true);
615+
616+
ClientRetryPolicy retryPolicy = new ClientRetryPolicy(
617+
endpointManager,
618+
this.partitionKeyRangeLocationCache,
619+
new RetryOptions(),
620+
enableEndpointDiscovery,
621+
isThinClientEnabled: false);
622+
623+
DocumentServiceRequest request = this.CreateRequest(isReadRequest: true, isMasterResourceType: false);
624+
625+
// ---- 1st 404/1002 ----
626+
retryPolicy.OnBeforeSendRequest(request);
627+
ShouldRetryResult shouldRetry = await retryPolicy.ShouldRetryAsync(
628+
new DocumentClientException(
629+
message: "1st 404/1002",
630+
innerException: null,
631+
statusCode: HttpStatusCode.NotFound,
632+
substatusCode: SubStatusCodes.ReadSessionNotAvailable,
633+
requestUri: request.RequestContext.LocationEndpointToRoute,
634+
responseHeaders: new DictionaryNameValueCollection()),
635+
CancellationToken.None);
636+
Assert.IsTrue(shouldRetry.ShouldRetry);
637+
638+
// ---- 2nd 404/1002 → hub header flag gets set ----
639+
retryPolicy.OnBeforeSendRequest(request);
640+
shouldRetry = await retryPolicy.ShouldRetryAsync(
641+
new DocumentClientException(
642+
message: "2nd 404/1002",
643+
innerException: null,
644+
statusCode: HttpStatusCode.NotFound,
645+
substatusCode: SubStatusCodes.ReadSessionNotAvailable,
646+
requestUri: request.RequestContext.LocationEndpointToRoute,
647+
responseHeaders: new DictionaryNameValueCollection()),
648+
CancellationToken.None);
649+
Assert.IsTrue(shouldRetry.ShouldRetry, "Should retry so the hub header can be sent.");
650+
651+
// ---- 3rd request: hub header should be present ----
652+
retryPolicy.OnBeforeSendRequest(request);
653+
string[] headerValues = request.Headers.GetValues(HubRegionHeader);
654+
Assert.IsNotNull(headerValues, "Hub region header must be present after two 404/1002 failures.");
655+
Assert.AreEqual(bool.TrueString, headerValues[0]);
656+
657+
// ---- Now simulate a retriable 503 ServiceUnavailable error ----
658+
shouldRetry = await retryPolicy.ShouldRetryAsync(
659+
new DocumentClientException(
660+
message: "503 ServiceUnavailable after hub header set",
661+
innerException: null,
662+
statusCode: HttpStatusCode.ServiceUnavailable,
663+
substatusCode: SubStatusCodes.Unknown,
664+
requestUri: request.RequestContext.LocationEndpointToRoute,
665+
responseHeaders: new DictionaryNameValueCollection()),
666+
CancellationToken.None);
667+
Assert.IsTrue(shouldRetry.ShouldRetry, "Should retry on 503 ServiceUnavailable.");
668+
669+
// ---- 4th request: hub header must STILL be present after 503 retry ----
670+
retryPolicy.OnBeforeSendRequest(request);
671+
headerValues = request.Headers.GetValues(HubRegionHeader);
672+
Assert.IsNotNull(headerValues, "Hub region header must persist through 503 retry.");
673+
Assert.AreEqual(bool.TrueString, headerValues[0], "Hub region header value should remain 'True'.");
674+
}
675+
599676
private async Task ValidateConnectTimeoutTriggersClientRetryPolicyAsync(
600677
bool isReadRequest,
601678
bool useMultipleWriteLocations,

0 commit comments

Comments
 (0)