-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Use recommended retries for token and IAM sign blob endpoints. #2879
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reviewed the first 6 commits - the comment on the 7th doesn't indicate that I've reviewed much of it yet! I think it's fine to add a single commit to fix typos etc - no need to move the change into the relevant original commit. Will try to get to the rest of the PR this time tomorrow.
@@ -28,48 +28,45 @@ public class ExponentialBackOff : IBackOff | |||
/// <summary>The maximum allowed number of retries.</summary> | |||
private const int MaxAllowedNumRetries = 20; | |||
|
|||
private readonly TimeSpan deltaBackOff; | |||
/// <summary>The random instance which generates a random number to add the to next back-off.</summary> | |||
private static readonly Random Random = new Random(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random isn't thread-safe - at least in some implementations. (I think it may be in .NET 8+but that's only a vague memory.)
We should probably lock on every use of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I've just noticed that this isn't actually introducing a new Random, just moving it - but the comment still stands... we should probably fix that in a separate PR.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, I added a commit at the end.
Starting on .NET 6 there's Random.Shared, but we can't use that at the moment.
/// <summary> | ||
/// Percent of the current unjittered back-off used to bound the jitter. | ||
/// If the value is set to 10, and the current back-off is 1000ms, then the jitter will be generated | ||
/// from [-100, 100]ms and the jittered back-off will be [900,1100]ms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd change this to "-100ms to 100ms" or similar, to avoid anything thinking it may be markdown. (Ditto the later part.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, done here and in all other places in this file.
/// Constructs a new exponential back-off with the given delta percent and maximum retries. | ||
/// If unspecified, <paramref name="maximumNumOfRetries"/> is set to 10. | ||
/// </summary> | ||
public ExponentialBackOff(short deltaBackOffPercent, int maximumNumOfRetries = 10) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be tempted to make this a factory method with a private constructor - something taking "two integers" could end up getting confusing if we add more overloads later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good idea.
(int)(DeltaBackOff.TotalMilliseconds * 1)); | ||
int backOffMilli = (int)(Math.Pow(2.0, (double)currentRetry - 1) * 1000 + randomMilli); | ||
return TimeSpan.FromMilliseconds(backOffMilli); | ||
double unjitterdBackOffMs = Math.Pow(2.0, currentRetry - 1) * 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: unjitterd => unjittered (or possibly "raw"?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raw seems better, done.
private int GetPercentBoundedJitter(double unjitterdBackOffMs) => | ||
DeltaBackOffPercent == 0 ? | ||
0 : | ||
GetRandomBoundedValue((int)(DeltaBackOffPercent * unjitterdBackOffMs)/100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: space before and after the /
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -50,6 +57,8 @@ public class ExponentialBackOffInitializer : IConfigurableHttpClientInitializer | |||
|
|||
/// <summary> | |||
/// Constructs a new back-off initializer with the given policy and back-off handler create function. | |||
/// If <paramref name="policy"/> has the <see cref="ExponentialBackOffPolicy.RecommendedOrDefault"/> flag set, | |||
/// the <see cref="BackOffHandler"/> will be set for handling both excetions and HTTP Status codes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
excetions => exceptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
/// <summary> | ||
/// Retry strategy recommended for the OAuth2 token endpoints. | ||
/// </summary> | ||
internal static readonly ExponentialBackOffInitializer OAuth2TokenEndpointRecommendedRetry = new ExponentialBackOffInitializer( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest making this a property instead of an internal field. (Just by adding { get; }
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes
HandleExceptionFunc = ex => false, | ||
HandleUnsuccessfulResponseFunc = response => | ||
response.StatusCode == HttpStatusCode.RequestTimeout || // 408 | ||
(int) response.StatusCode == 429 || // 429 Enum value not availbale in .NET Standard 2.0 or .NET Framework 4.6.2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
availbale => available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And add a comment saying what the enum value would be - I don't know what 429 means offhand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both done
/// <see cref="ExponentialBackOffPolicy.UnsuccessfulResponse503"/> flags. Otherwise, the returned value will be <paramref name="originalPolicy"/>. | ||
/// This method facilitate honoring any custom policies that may have been set. Calling code is resposible for honoring the recommended policies. | ||
/// </summary> | ||
internal static ExponentialBackOffPolicy StripOAuth2TokenEndpointRecommendedPolicy(ExponentialBackOffPolicy originalPolicy) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm very glad this is internal, as it feels a bit odd to me. I'm not quite sure what the purpose is yet, but I suspect I'll understand later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just don't have enums for configuring retries, they are also Flags.
The recommended OAuth Token endpoint retry includes retrying on 503, but not on exceptions. So, if the original enum value has RecommendedOrDefault set we don't need to add a specific policy for 503. We only keep here what's not recommended, in that way, calling code know whether it needs to add a policy different than recommended.
Calling code also needs to check if it needs to add the recommended policy or not.
Happy to chat about this though. I had several different approaches for this, but ended up preferring this one.
@@ -40,6 +39,97 @@ public class ServiceAccountCredentialTests | |||
private static readonly TimeSpan JwtLifetime = TimeSpan.FromMinutes(60); | |||
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |||
|
|||
[Fact] | |||
public void Deafault_RecommendedRetryPolicy() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deafault => Default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I think I understand all of this now. I'm sure we'd have represented backoffs in a different (non-enum-based) way if we got to redesign it from scratch, but this seems fine as a way of fitting in with that design :)
@@ -81,4 +81,59 @@ public void StripOAuth2TokenEndpointRecommendedPolicy_Strips(ExponentialBackOffP | |||
var stripped = GoogleAuthConsts.StripOAuth2TokenEndpointRecommendedPolicy(original); | |||
Assert.Equal(expected, stripped); | |||
} | |||
|
|||
[Fact] | |||
public void IamSingBlobTokenEndpointRecommendedRetry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sing => Sign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Fix an exception message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All comments addressed in sorted review commits (it was not hard this time to do that).
Plus, and extra commit at the end to lock on random.
Thanks!
@@ -28,48 +28,45 @@ public class ExponentialBackOff : IBackOff | |||
/// <summary>The maximum allowed number of retries.</summary> | |||
private const int MaxAllowedNumRetries = 20; | |||
|
|||
private readonly TimeSpan deltaBackOff; | |||
/// <summary>The random instance which generates a random number to add the to next back-off.</summary> | |||
private static readonly Random Random = new Random(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, I added a commit at the end.
Starting on .NET 6 there's Random.Shared, but we can't use that at the moment.
/// <summary> | ||
/// Percent of the current unjittered back-off used to bound the jitter. | ||
/// If the value is set to 10, and the current back-off is 1000ms, then the jitter will be generated | ||
/// from [-100, 100]ms and the jittered back-off will be [900,1100]ms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, done here and in all other places in this file.
/// Constructs a new exponential back-off with the given delta percent and maximum retries. | ||
/// If unspecified, <paramref name="maximumNumOfRetries"/> is set to 10. | ||
/// </summary> | ||
public ExponentialBackOff(short deltaBackOffPercent, int maximumNumOfRetries = 10) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good idea.
(int)(DeltaBackOff.TotalMilliseconds * 1)); | ||
int backOffMilli = (int)(Math.Pow(2.0, (double)currentRetry - 1) * 1000 + randomMilli); | ||
return TimeSpan.FromMilliseconds(backOffMilli); | ||
double unjitterdBackOffMs = Math.Pow(2.0, currentRetry - 1) * 1000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Raw seems better, done.
private int GetPercentBoundedJitter(double unjitterdBackOffMs) => | ||
DeltaBackOffPercent == 0 ? | ||
0 : | ||
GetRandomBoundedValue((int)(DeltaBackOffPercent * unjitterdBackOffMs)/100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
/// <summary> | ||
/// Retry strategy recommended for the OAuth2 token endpoints. | ||
/// </summary> | ||
internal static readonly ExponentialBackOffInitializer OAuth2TokenEndpointRecommendedRetry = new ExponentialBackOffInitializer( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes
HandleExceptionFunc = ex => false, | ||
HandleUnsuccessfulResponseFunc = response => | ||
response.StatusCode == HttpStatusCode.RequestTimeout || // 408 | ||
(int) response.StatusCode == 429 || // 429 Enum value not availbale in .NET Standard 2.0 or .NET Framework 4.6.2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both done
/// <see cref="ExponentialBackOffPolicy.UnsuccessfulResponse503"/> flags. Otherwise, the returned value will be <paramref name="originalPolicy"/>. | ||
/// This method facilitate honoring any custom policies that may have been set. Calling code is resposible for honoring the recommended policies. | ||
/// </summary> | ||
internal static ExponentialBackOffPolicy StripOAuth2TokenEndpointRecommendedPolicy(ExponentialBackOffPolicy originalPolicy) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We just don't have enums for configuring retries, they are also Flags.
The recommended OAuth Token endpoint retry includes retrying on 503, but not on exceptions. So, if the original enum value has RecommendedOrDefault set we don't need to add a specific policy for 503. We only keep here what's not recommended, in that way, calling code know whether it needs to add a policy different than recommended.
Calling code also needs to check if it needs to add the recommended policy or not.
Happy to chat about this though. I had several different approaches for this, but ended up preferring this one.
@@ -40,6 +39,97 @@ public class ServiceAccountCredentialTests | |||
private static readonly TimeSpan JwtLifetime = TimeSpan.FromMinutes(60); | |||
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | |||
|
|||
[Fact] | |||
public void Deafault_RecommendedRetryPolicy() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@@ -81,4 +81,59 @@ public void StripOAuth2TokenEndpointRecommendedPolicy_Strips(ExponentialBackOffP | |||
var stripped = GoogleAuthConsts.StripOAuth2TokenEndpointRecommendedPolicy(original); | |||
Assert.Equal(expected, stripped); | |||
} | |||
|
|||
[Fact] | |||
public void IamSingBlobTokenEndpointRecommendedRetry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
ad82ce2
to
abc3758
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good!
Thanks, rebasing the review commits and merging after. |
Towards b/332955964 and b/368412308
Actual recommended policies and their usage will be added in upcoming commits. Towards b/332955964 and b/368412308
Towards b/332955964
Towards b/332955964
…oints. Towards b/332955964
Towards b/368412308
…point. Towards b/368412308
…b endpoint. Towards b/368412308
abc3758
to
a5d793a
Compare
Closes b/332955964 and b/368412308
@jskeet , as always, one commit at a time.