Skip to content

Commit c7c61be

Browse files
authored
HttpTimeoutPolicy Improvements Phase 3: Fixes the TimeoutPolicies to Have Segregated Timeouts for Point-Reads and Non-Point-Reads with Uniform Timeouts (#5497)
# Pull Request Template ## Description At a high-level, this PR aims to unify the timeouts across PPAF and thin client http retry policies for read only requests. Furthermore, HttpTimeoutPolicies for PPAF and Thinclient will have segregated timeouts for Point-Reads and Non-Point-Reads. With this change, we will have the ability to fine-tune/optimize the timeouts for the respective operations. In this context, requests are allowed to retry 3 times with 6s, 6s and 10s timeouts respectively for each retry. We chose these timeouts based on the default backend response timeouts and it will help requests being timeout with aggressive timeouts. As next step, we have planned for DR drill to identify the appropriate timeouts. Depending on the findings from the drill, we may go towards the direction of configurable timeout for first retry. ## Type of change Please delete options that are not relevant. - [x] Bug fix (non-breaking change which fixes an issue) ## Closing issues To automatically close an issue: closes #5496
1 parent c6bcbe3 commit c7c61be

File tree

4 files changed

+85
-19
lines changed

4 files changed

+85
-19
lines changed

Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicy.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,16 @@ public static HttpTimeoutPolicy GetTimeoutPolicy(
4848
{
4949
if (isThinClientEnabled)
5050
{
51-
return documentServiceRequest.IsReadOnlyRequest
52-
? HttpTimeoutPolicyForThinClient.InstanceShouldRetryAndThrow503OnTimeout
53-
: HttpTimeoutPolicyForThinClient.InstanceShouldNotRetryAndThrow503OnTimeout;
51+
if (documentServiceRequest.IsReadOnlyRequest)
52+
{
53+
return documentServiceRequest.OperationType == OperationType.Read
54+
? HttpTimeoutPolicyForThinClient.InstanceShouldRetryAndThrow503OnTimeoutForPointReads
55+
: HttpTimeoutPolicyForThinClient.InstanceShouldRetryAndThrow503OnTimeoutForNonPointReads;
56+
}
57+
else
58+
{
59+
return HttpTimeoutPolicyForThinClient.InstanceShouldNotRetryAndThrow503OnTimeoutForWrites;
60+
}
5461
}
5562
// Data Plane Reads.
5663
else if (documentServiceRequest.IsReadOnlyRequest)

Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForPartitionFailover.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ private HttpTimeoutPolicyForPartitionFailover(bool isPointRead)
2020
}
2121

2222
// Timeouts and delays are based on the following rationale:
23-
// For point reads: 3 attempts with timeouts of 1s, 6s, and 6s respectively.
23+
// For point reads: 3 attempts with timeouts of 6s, 6s, and 10s respectively.
2424
// For non-point reads: 3 attempts with timeouts of 6s, 6s, and 10s respectively.
2525
private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>()
2626
{
27-
(TimeSpan.FromSeconds(1), TimeSpan.Zero),
2827
(TimeSpan.FromSeconds(6), TimeSpan.Zero),
2928
(TimeSpan.FromSeconds(6), TimeSpan.Zero),
29+
(TimeSpan.FromSeconds(10), TimeSpan.Zero),
3030
};
3131

3232
private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForNonPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>()

Microsoft.Azure.Cosmos/src/HttpClient/HttpTimeoutPolicyForThinClient.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,43 @@ internal sealed class HttpTimeoutPolicyForThinClient : HttpTimeoutPolicy
1111
{
1212
public bool shouldRetry;
1313
public bool shouldThrow503OnTimeout;
14+
public bool isPointRead;
1415
private static readonly string Name = nameof(HttpTimeoutPolicyForThinClient);
15-
public static readonly HttpTimeoutPolicy InstanceShouldRetryAndThrow503OnTimeout = new HttpTimeoutPolicyForThinClient(true, true);
16-
public static readonly HttpTimeoutPolicy InstanceShouldNotRetryAndThrow503OnTimeout = new HttpTimeoutPolicyForThinClient(true, false);
16+
public static readonly HttpTimeoutPolicy InstanceShouldRetryAndThrow503OnTimeoutForPointReads = new HttpTimeoutPolicyForThinClient(shouldThrow503OnTimeout: true, shouldRetry: true, isPointRead: true);
17+
public static readonly HttpTimeoutPolicy InstanceShouldRetryAndThrow503OnTimeoutForNonPointReads = new HttpTimeoutPolicyForThinClient(shouldThrow503OnTimeout: true, shouldRetry: true, isPointRead: false);
18+
public static readonly HttpTimeoutPolicy InstanceShouldNotRetryAndThrow503OnTimeoutForWrites = new HttpTimeoutPolicyForThinClient(shouldThrow503OnTimeout: true, shouldRetry: false, isPointRead: false);
1719

1820
private HttpTimeoutPolicyForThinClient(
1921
bool shouldThrow503OnTimeout,
20-
bool shouldRetry)
22+
bool shouldRetry,
23+
bool isPointRead)
2124
{
2225
this.shouldThrow503OnTimeout = shouldThrow503OnTimeout;
2326
this.shouldRetry = shouldRetry;
27+
this.isPointRead = isPointRead;
2428
}
2529

26-
private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelays = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>()
30+
private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>()
2731
{
28-
(TimeSpan.FromSeconds(.5), TimeSpan.Zero),
29-
(TimeSpan.FromSeconds(1), TimeSpan.Zero),
30-
(TimeSpan.FromSeconds(5), TimeSpan.Zero),
32+
(TimeSpan.FromSeconds(6), TimeSpan.Zero),
33+
(TimeSpan.FromSeconds(6), TimeSpan.Zero),
34+
(TimeSpan.FromSeconds(10), TimeSpan.Zero),
35+
};
36+
37+
private readonly IReadOnlyList<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> TimeoutsAndDelaysForNonPointReads = new List<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)>()
38+
{
39+
(TimeSpan.FromSeconds(6), TimeSpan.Zero),
40+
(TimeSpan.FromSeconds(6), TimeSpan.Zero),
41+
(TimeSpan.FromSeconds(10), TimeSpan.Zero),
3142
};
3243

3344
public override string TimeoutPolicyName => HttpTimeoutPolicyForThinClient.Name;
3445

35-
public override int TotalRetryCount => this.TimeoutsAndDelays.Count;
46+
public override int TotalRetryCount => this.isPointRead ? this.TimeoutsAndDelaysForPointReads.Count : this.TimeoutsAndDelaysForNonPointReads.Count;
3647

3748
public override IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> GetTimeoutEnumerator()
3849
{
39-
return this.TimeoutsAndDelays.GetEnumerator();
50+
return this.isPointRead ? this.TimeoutsAndDelaysForPointReads.GetEnumerator() : this.TimeoutsAndDelaysForNonPointReads.GetEnumerator();
4051
}
4152

4253
public override bool ShouldRetryBasedOnResponse(HttpMethod requestHttpMethod, HttpResponseMessage responseMessage)

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

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -619,23 +619,71 @@ public void HttpTimeoutPolicyForParitionFailoverForQueries()
619619
[TestMethod]
620620
public void HttpTimeoutPolicyForParitionFailoverForReads()
621621
{
622-
HttpTimeoutPolicy httpTimeoutPolicyForQuery = HttpTimeoutPolicy.GetTimeoutPolicy(
622+
HttpTimeoutPolicy httpTimeoutPolicyForPointReads = HttpTimeoutPolicy.GetTimeoutPolicy(
623623
documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Read),
624624
isPartitionLevelFailoverEnabled: true,
625625
isThinClientEnabled: false);
626-
IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForQuery.GetTimeoutEnumerator();
626+
IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForPointReads.GetTimeoutEnumerator();
627627

628628
int count = 0;
629629
while (availableRetries.MoveNext())
630630
{
631-
if (count == 0)
631+
if (count <= 1)
632+
{
633+
Assert.AreEqual(new TimeSpan(0, 0, 6), availableRetries.Current.requestTimeout);
634+
}
635+
else if (count == 2)
636+
{
637+
Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout);
638+
}
639+
count++;
640+
}
641+
}
642+
643+
[TestMethod]
644+
public void HttpTimeoutPolicyWhenThinClientEnabledForPointReads()
645+
{
646+
HttpTimeoutPolicy httpTimeoutPolicyForPointReads = HttpTimeoutPolicy.GetTimeoutPolicy(
647+
documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Read),
648+
isPartitionLevelFailoverEnabled: false,
649+
isThinClientEnabled: true);
650+
IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForPointReads.GetTimeoutEnumerator();
651+
652+
int count = 0;
653+
while (availableRetries.MoveNext())
654+
{
655+
if (count <= 1)
632656
{
633-
Assert.AreEqual(new TimeSpan(0, 0, 1), availableRetries.Current.requestTimeout);
657+
Assert.AreEqual(new TimeSpan(0, 0, 6), availableRetries.Current.requestTimeout);
658+
}
659+
else if (count == 2)
660+
{
661+
Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout);
634662
}
635-
else if (count == 1 || count ==2 )
663+
count++;
664+
}
665+
}
666+
667+
[TestMethod]
668+
public void HttpTimeoutPolicyWhenThinClientEnabledForNonPointReads()
669+
{
670+
HttpTimeoutPolicy httpTimeoutPolicyForQuery = HttpTimeoutPolicy.GetTimeoutPolicy(
671+
documentServiceRequest: CosmosHttpClientCoreTests.CreateDocumentServiceRequestByOperation(ResourceType.Document, OperationType.Query),
672+
isPartitionLevelFailoverEnabled: false,
673+
isThinClientEnabled: true);
674+
IEnumerator<(TimeSpan requestTimeout, TimeSpan delayForNextRequest)> availableRetries = httpTimeoutPolicyForQuery.GetTimeoutEnumerator();
675+
676+
int count = 0;
677+
while (availableRetries.MoveNext())
678+
{
679+
if (count <= 1)
636680
{
637681
Assert.AreEqual(new TimeSpan(0, 0, 6), availableRetries.Current.requestTimeout);
638682
}
683+
else if (count == 2)
684+
{
685+
Assert.AreEqual(new TimeSpan(0, 0, 10), availableRetries.Current.requestTimeout);
686+
}
639687
count++;
640688
}
641689
}

0 commit comments

Comments
 (0)