Skip to content

Commit 428215c

Browse files
authored
Add retry policy support for worker-indexing scenarios (#9265)
1 parent a9e69d2 commit 428215c

File tree

5 files changed

+111
-3
lines changed

5 files changed

+111
-3
lines changed

src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,25 @@ internal void ProcessFunctionMetadataResponses(FunctionMetadataResponse function
863863
Language = metadata.Language
864864
};
865865

866+
if (metadata.RetryOptions is not null)
867+
{
868+
functionMetadata.Retry = new RetryOptions
869+
{
870+
MaxRetryCount = metadata.RetryOptions.MaxRetryCount,
871+
Strategy = metadata.RetryOptions.RetryStrategy.ToRetryStrategy()
872+
};
873+
874+
if (functionMetadata.Retry.Strategy is RetryStrategy.FixedDelay)
875+
{
876+
functionMetadata.Retry.DelayInterval = metadata.RetryOptions.DelayInterval?.ToTimeSpan();
877+
}
878+
else
879+
{
880+
functionMetadata.Retry.MinimumInterval = metadata.RetryOptions.MinimumInterval?.ToTimeSpan();
881+
functionMetadata.Retry.MaximumInterval = metadata.RetryOptions.MaximumInterval?.ToTimeSpan();
882+
}
883+
}
884+
866885
functionMetadata.SetFunctionId(metadata.FunctionId);
867886

868887
foreach (var property in metadata.Properties)

src/WebJobs.Script.Grpc/MessageExtensions/GrpcMessageConversionExtensions.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,14 @@ internal static TypedData ToRpcLongArray(this long[] arrLong)
357357
return typedData;
358358
}
359359

360+
internal static RetryStrategy ToRetryStrategy(this RpcRetryOptions.Types.RetryStrategy retryStrategy) =>
361+
retryStrategy switch
362+
{
363+
RpcRetryOptions.Types.RetryStrategy.FixedDelay => RetryStrategy.FixedDelay,
364+
RpcRetryOptions.Types.RetryStrategy.ExponentialBackoff => RetryStrategy.ExponentialBackoff,
365+
_ => throw new InvalidOperationException($"Unknown RetryStrategy RpcDataType: {retryStrategy}.")
366+
};
367+
360368
private static bool ShouldIncludeEmptyEntriesInMessagePayload(GrpcCapabilities capabilities)
361369
{
362370
return !string.IsNullOrWhiteSpace(capabilities.GetCapabilityState(RpcWorkerConstants.IncludeEmptyEntriesInMessagePayload));

src/WebJobs.Script/Host/WorkerFunctionMetadataProvider.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,15 @@ internal IEnumerable<FunctionMetadata> ValidateMetadata(IEnumerable<RawFunctionM
193193
}
194194
}
195195

196-
// retry option validation
196+
// populate retry options if json string representation is provided
197197
if (!string.IsNullOrEmpty(rawFunction.RetryOptions))
198198
{
199199
function.Retry = JObject.Parse(rawFunction.RetryOptions).ToObject<RetryOptions>();
200+
}
201+
202+
// retry option validation
203+
if (function.Retry is not null)
204+
{
200205
Utility.ValidateRetryOptions(function.Retry);
201206
}
202207

src/WebJobs.Script/Utility.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -912,11 +912,11 @@ public static void ValidateRetryOptions(RetryOptions
912912
case RetryStrategy.ExponentialBackoff:
913913
if (!retryOptions.MinimumInterval.HasValue)
914914
{
915-
throw new ArgumentNullException(nameof(retryOptions.DelayInterval));
915+
throw new ArgumentNullException(nameof(retryOptions.MinimumInterval));
916916
}
917917
if (!retryOptions.MaximumInterval.HasValue)
918918
{
919-
throw new ArgumentNullException(nameof(retryOptions.DelayInterval));
919+
throw new ArgumentNullException(nameof(retryOptions.MaximumInterval));
920920
}
921921
// ensure values specified to create ExponentialBackoffRetryAttribute are valid
922922
_ = new ExponentialBackoffRetryAttribute(retryOptions.MaxRetryCount.Value, retryOptions.MinimumInterval.ToString(), retryOptions.MaximumInterval.ToString());

test/WebJobs.Script.Tests/UtilityTests.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,82 @@ public void TryReadAsBool_ReturnsExpectedBoolValue(object value, bool expectedVa
10221022
Assert.Equal(resultValue, expectedValueResult);
10231023
}
10241024

1025+
[Fact]
1026+
public void ValidateRetryOptions_ThrowsWhenMaxRetryIsNull()
1027+
{
1028+
var retryOptions = new RetryOptions
1029+
{
1030+
Strategy = RetryStrategy.FixedDelay
1031+
};
1032+
1033+
var ex = Assert.Throws<ArgumentNullException>(() => Utility.ValidateRetryOptions(retryOptions));
1034+
Assert.Equal("Value cannot be null. (Parameter 'MaxRetryCount')", ex.Message);
1035+
}
1036+
1037+
[Fact]
1038+
public void ValidateRetryOptions_ThrowsWhenFixedDelayArgsNull()
1039+
{
1040+
var retryOptions = new RetryOptions
1041+
{
1042+
Strategy = RetryStrategy.FixedDelay,
1043+
MaxRetryCount = 5
1044+
};
1045+
1046+
var ex = Assert.Throws<ArgumentNullException>(() => Utility.ValidateRetryOptions(retryOptions));
1047+
Assert.Equal("Value cannot be null. (Parameter 'DelayInterval')", ex.Message);
1048+
}
1049+
1050+
[Fact]
1051+
public void ValidateRetryOptions_ThrowsWhenExponentialBackoffArgsNull()
1052+
{
1053+
var retryOptions1 = new RetryOptions
1054+
{
1055+
Strategy = RetryStrategy.ExponentialBackoff,
1056+
MaxRetryCount = 5,
1057+
MinimumInterval = TimeSpan.FromSeconds(5)
1058+
};
1059+
1060+
var retryOptions2 = new RetryOptions
1061+
{
1062+
Strategy = RetryStrategy.ExponentialBackoff,
1063+
MaxRetryCount = 5,
1064+
MaximumInterval = TimeSpan.MaxValue
1065+
};
1066+
1067+
var ex1 = Assert.Throws<ArgumentNullException>(() => Utility.ValidateRetryOptions(retryOptions1));
1068+
Assert.Equal("Value cannot be null. (Parameter 'MaximumInterval')", ex1.Message);
1069+
1070+
var ex2 = Assert.Throws<ArgumentNullException>(() => Utility.ValidateRetryOptions(retryOptions2));
1071+
Assert.Equal("Value cannot be null. (Parameter 'MinimumInterval')", ex2.Message);
1072+
}
1073+
1074+
[Fact]
1075+
public void ValidateRetryOptions_FixedDelaySuccess()
1076+
{
1077+
var retryOptions = new RetryOptions
1078+
{
1079+
Strategy = RetryStrategy.FixedDelay,
1080+
MaxRetryCount = 5,
1081+
DelayInterval = TimeSpan.FromSeconds(600)
1082+
};
1083+
1084+
Utility.ValidateRetryOptions(retryOptions);
1085+
}
1086+
1087+
[Fact]
1088+
public void ValidateRetryOptions_ExponentialBackoffSuccess()
1089+
{
1090+
var retryOptions = new RetryOptions
1091+
{
1092+
Strategy = RetryStrategy.ExponentialBackoff,
1093+
MaxRetryCount = 5,
1094+
MinimumInterval = TimeSpan.FromSeconds(10),
1095+
MaximumInterval = TimeSpan.MaxValue
1096+
};
1097+
1098+
Utility.ValidateRetryOptions(retryOptions);
1099+
}
1100+
10251101
private static void VerifyLogLevel(IList<LogMessage> allLogs, string msg, LogLevel expectedLevel)
10261102
{
10271103
var message = allLogs.FirstOrDefault(l => l.FormattedMessage.Contains(msg));

0 commit comments

Comments
 (0)