Skip to content

Commit

Permalink
Adding retry context properties to InvocationRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
alrod committed Feb 22, 2021
1 parent a7ee1f9 commit 80d8e45
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 27 deletions.
31 changes: 20 additions & 11 deletions sample/NodeRetry/HttpTrigger-RetryFunctionJson/index.js
@@ -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
@@ -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
};
}
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
};
}
}
}
}
}
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
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
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
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
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

0 comments on commit 80d8e45

Please sign in to comment.