Skip to content

Adding retry context properties to InvocationRequest #7134

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

Merged
merged 1 commit into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions sample/NodeRetry/HttpTrigger-RetryFunctionJson/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
var invocationCount = 0;
var errorString = 'An error occurred';

module.exports = async function (context, req) {
const reset = req.query.reset;
invocationCount = reset ? 0 : invocationCount
if (context.executionContext.retryContext && (context.executionContext.retryContext.retryCount !== invocationCount
|| !(context.executionContext.retryContext.maxRetryCount === 4 || context.executionContext.retryContext.maxRetryCount === 0)
|| !(context.executionContext.retryContext.exception.message.includes(errorString)))) {
context.res = {
status: 500
};
} else {
const reset = req.query.reset;
invocationCount = reset ? 0 : invocationCount

context.log('JavaScript HTTP trigger function processed a request.invocationCount: ' + invocationCount);
context.log('JavaScript HTTP trigger function processed a request.invocationCount: ' + invocationCount);

invocationCount = invocationCount + 1;
const responseMessage = "invocationCount: " + invocationCount;
if (invocationCount < 4) {
throw new Error('An error occurred');
invocationCount = invocationCount + 1;
const responseMessage = "invocationCount: " + invocationCount;
if (invocationCount < 4) {
throw new Error(errorString);
}
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage
};
}
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage
};
}
34 changes: 22 additions & 12 deletions sample/NodeRetry/HttpTrigger-RetryHostJson/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
var invocationCount = 0;
var errorString = 'An error occurred';

module.exports = async function (context, req) {
const reset = req.query.reset;
invocationCount = reset ? 0 : invocationCount
if (context.executionContext.retryContext && (context.executionContext.retryContext.retryCount !== invocationCount
|| !(context.executionContext.retryContext.maxRetryCount === 2 || context.executionContext.retryContext.maxRetryCount === 0)
|| !(context.executionContext.retryContext.exception.message.includes(errorString)))) {
debugger;
context.res = {
status: 500
};
} else {
const reset = req.query.reset;
invocationCount = reset ? 0 : invocationCount

context.log('JavaScript HTTP trigger function processed a request.invocationCount: ' + invocationCount);

invocationCount = invocationCount + 1;
const responseMessage = "invocationCount: " + invocationCount;
if (invocationCount < 2) {
throw new Error('An error occurred');
context.log('JavaScript HTTP trigger function processed a request.invocationCount: ' + invocationCount);

invocationCount = invocationCount + 1;
const responseMessage = "invocationCount: " + invocationCount;
if (invocationCount < 2) {
throw new Error('An error occurred');
}
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage
};
}
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Host.Diagnostics;
using Microsoft.Azure.WebJobs.Script.Description;
using Microsoft.Azure.WebJobs.Script.Grpc.Extensions;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
using Microsoft.Azure.WebJobs.Script.Workers.SharedMemoryDataTransfer;
using Microsoft.Extensions.Logging;
using RpcException = Microsoft.Azure.WebJobs.Script.Grpc.Messages.RpcException;

namespace Microsoft.Azure.WebJobs.Script.Grpc
{
Expand All @@ -30,6 +32,8 @@ public static async Task<InvocationRequest> ToRpcInvocationRequest(this ScriptIn
TraceContext = GetRpcTraceContext(context.Traceparent, context.Tracestate, context.Attributes, logger),
};

SetRetryContext(context, invocationRequest);

var rpcValueCache = new Dictionary<object, TypedData>();
Dictionary<object, RpcSharedMemory> sharedMemValueCache = null;
StringBuilder logBuilder = null;
Expand Down Expand Up @@ -171,5 +175,27 @@ internal static RpcTraceContext GetRpcTraceContext(string activityId, string tra

return traceContext;
}

internal static void SetRetryContext(ScriptInvocationContext context, InvocationRequest invocationRequest)
{
if (context.ExecutionContext.RetryContext != null)
{
invocationRequest.RetryContext = new RetryContext()
{
RetryCount = context.ExecutionContext.RetryContext.RetryCount,
MaxRetryCount = context.ExecutionContext.RetryContext.MaxRetryCount
};
// RetryContext.Exception should not be null, check just in case
if (context.ExecutionContext.RetryContext.Exception != null)
{
invocationRequest.RetryContext.Exception = new RpcException()
{
Message = ExceptionFormatter.GetFormattedException(context.ExecutionContext.RetryContext.Exception), // merge message from InnerException
StackTrace = context.ExecutionContext.RetryContext.Exception.StackTrace ?? string.Empty,
Source = context.ExecutionContext.RetryContext.Exception.Source ?? string.Empty
};
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ mkdir %MSGDIR%

set OUTDIR=%MSGDIR%\DotNet
mkdir %OUTDIR%
%GRPC_TOOLS_PATH%\protoc.exe %PROTO% --csharp_out %OUTDIR% --grpc_out=%OUTDIR% --plugin=protoc-gen-grpc=%GRPC_TOOLS_PATH%\grpc_csharp_plugin.exe --proto_path=%PROTO_PATH% --proto_path=%PROTOBUF_TOOLS%
%GRPC_TOOLS_PATH%\protoc.exe %PROTO% --csharp_out %OUTDIR% --grpc_out=%OUTDIR% --plugin=protoc-gen-grpc=%GRPC_TOOLS_PATH%\grpc_csharp_plugin.exe --proto_path=%PROTO_PATH% --proto_path=%PROTOBUF_TOOLS%
```
## JavaScript
In package.json, add to the build script the following commands to build .js files and to build .ts files. Use and install npm package `protobufjs`.
Expand All @@ -81,7 +81,10 @@ In pom.xml add following under configuration for this plugin
<protoSourceRoot>${basedir}/<path to this repo>/azure-functions-language-worker-protobuf/src/proto</protoSourceRoot>

## Python
--TODO
```
python -m pip install -e .[dev] -U
python setup.py build
```

## Contributing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ message InvocationRequest {

// Populates activityId, tracestate and tags from host
RpcTraceContext trace_context = 5;

// Current retry context
RetryContext retry_context = 6;
}

// Host sends ActivityId, traceStateString and Tags from host
Expand All @@ -292,6 +295,18 @@ message RpcTraceContext {
map<string, string> attributes = 3;
}

// Host sends retry context for a function invocation
message RetryContext {
// Current retry count
int32 retry_count = 1;

// Max retry count
int32 max_retry_count = 2;

// Exception that caused the retry
RpcException exception = 3;
}

// Host requests worker to cancel invocation
message InvocationCancel {
// Unique id for invocation
Expand Down
2 changes: 1 addition & 1 deletion src/WebJobs.Script/WebJobs.Script.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="3.1.0" />
<PackageReference Include="Microsoft.Azure.AppService.Proxy.Client" Version="2.0.11020001-fabe022e" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.8.2-SNAPSHOT" />
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="2.1.1" />
<PackageReference Include="Microsoft.Azure.Functions.PowerShellWorker.PS6" Version="3.0.630" />
<PackageReference Include="Microsoft.Azure.Functions.PowerShellWorker.PS7" Version="3.0.629" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.27" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.0" />
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.10.0" />
<PackageReference Include="Microsoft.Azure.EventHubs" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.NodeJsWorker" Version="2.1.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.3" />
<PackageReference Include="Microsoft.Azure.Functions.JavaWorker" Version="1.8.2-SNAPSHOT" />
<PackageReference Include="Microsoft.Azure.Mobile.Client" Version="4.0.2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,43 @@ public async Task ToRpcInvocationRequest_MultipleInputBindings()
Assert.True(result.TriggerMetadata.ContainsKey("query"));
}

[Fact]
public void TestSetRetryContext_NoRetry()
{
ScriptInvocationContext context = new ScriptInvocationContext()
{
ExecutionContext = new ExecutionContext()
};
InvocationRequest request = new InvocationRequest();
Grpc.ScriptInvocationContextExtensions.SetRetryContext(context, request);

Assert.Null(request.RetryContext);
}

[Fact]
public void TestSetRetryContext_Retry()
{
ScriptInvocationContext context = new ScriptInvocationContext()
{
ExecutionContext = new ExecutionContext()
{
RetryContext = new Host.RetryContext()
{
RetryCount = 1,
MaxRetryCount = 2,
Exception = new Exception("test")
}
}
};
InvocationRequest request = new InvocationRequest();
Grpc.ScriptInvocationContextExtensions.SetRetryContext(context, request);

Assert.NotNull(request.RetryContext);
Assert.Equal(request.RetryContext.RetryCount, 1);
Assert.Equal(request.RetryContext.MaxRetryCount, 2);
Assert.NotNull(request.RetryContext.Exception);
}

/// <summary>
/// The inputs meet the requirement for being transferred over shared memory.
/// Ensure that the inputs are converted to <see cref="RpcSharedMemory"/>.
Expand Down