Durable Task Framework extension for Azure Functions
MIT License
711
stars
263
forks
source link
ActivityTrigger with byte[] as input results in null reference exception after upgrade to Microsoft.Azure.Functions.Worker.Extensions.DurableTask 1.1.1 #2746
After upgrading from Microsoft.Azure.Functions.Worker.Extensions.DurableTask1.0.4 to 1.1.1 my ActivityTrigger functions throw an exception on a byte[] binding parameter.
I have an Azure Durable Function and my orchestrator invokes an ActivityTrigger Function which returns a byte[][]. I then use a fan-out/fan-in pattern to process each byte[] in this array in parallel. With package version 1.0.4 this works, but after upgrading to 1.1.1 calling the ActivityTrigger throws an exception.
The Function with ActivityTrigger should accept byte[] as valid input.
Actual behavior
The following exception is thrown in the autogenerated DirectFunctionExecutor
System.NullReferenceException
HResult=0x80004003
Message=Object reference not set to an instance of an object.
Source=FunctionApp
StackTrace:
at FunctionApp.DirectFunctionExecutor.<ExecuteAsync>d__3.MoveNext() in C:\Users\[redacted]\source\repos\Sandbox\FunctionApp\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GeneratedFunctionExecutor.g.cs:line 50
This exception was originally thrown at this call stack:
FunctionApp.DirectFunctionExecutor.ExecuteAsync(Microsoft.Azure.Functions.Worker.FunctionContext) in GeneratedFunctionExecutor.g.cs
Relevant source code snippets
Create a new empty Function App. Change the Program.cs to the following:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
})
.Build();
host.Run();
And create class FanOutByteArrayOrchestrator.cs with the below content:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using System.Text;
using System.Text.Json;
namespace FunctionApp
{
public class FanOutByteArrayOrchestrator
{
[Function("FanOutByteArrayOrchestratorApi")]
public async Task<HttpResponseData> RunApi(
[HttpTrigger] HttpRequestData request,
[DurableClient] DurableTaskClient durableTaskClient)
{
var orchestrationInstanceId = await durableTaskClient
.ScheduleNewOrchestrationInstanceAsync("FanOutByteArrayOrchestrator")
.ConfigureAwait(true);
return durableTaskClient.CreateCheckStatusResponse(request, orchestrationInstanceId);
}
[Function("FanOutByteArrayOrchestrator")]
public async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext taskOrchestrationContext)
{
var events = await taskOrchestrationContext
.CallActivityAsync<byte[][]>("CreateEvents")
.ConfigureAwait(true);
// Fan-out/fan-in
var eventTasks = events
.Select(x => taskOrchestrationContext.CallActivityAsync("ProcessEvent", input: x))
.ToList();
await Task.WhenAll(eventTasks).ConfigureAwait(true);
}
[Function("CreateEvents")]
public Task<byte[][]> CreateEvents([ActivityTrigger] CancellationToken cancellationToken)
{
var cars = new List<Car>
{
new() { HorsePower = 500, Name = "Tesla Model 3"},
new() { HorsePower = 1000, Name = "Red Bull Formula 1 Car"},
};
var byteArray = cars.Select(x => Encoding.UTF8.GetBytes(JsonSerializer.Serialize(x)));
return Task.FromResult(byteArray.ToArray());
}
[Function("ProcessEvent")]
public Task ProcessEvent([ActivityTrigger] byte[] eventToProcess, CancellationToken cancellationToken)
{
var jsonString = Encoding.UTF8.GetString(eventToProcess);
var car = JsonSerializer.Deserialize<Car>(jsonString);
Console.WriteLine($"Name: {car?.Name} has {car?.HorsePower} horse power.");
return Task.CompletedTask;
}
}
internal class Car
{
public int HorsePower { get; set; }
public string? Name { get; set; }
}
}
Description
After upgrading from
Microsoft.Azure.Functions.Worker.Extensions.DurableTask
1.0.4 to 1.1.1 my ActivityTrigger functions throw an exception on abyte[]
binding parameter.I have an Azure Durable Function and my orchestrator invokes an
ActivityTrigger
Function which returns abyte[][]
. I then use a fan-out/fan-in pattern to process eachbyte[]
in this array in parallel. With package version 1.0.4 this works, but after upgrading to 1.1.1 calling the ActivityTrigger throws an exception.I reported this before in https://github.com/Azure/azure-functions-durable-extension/issues/2678 which was closed as duplicate and I hoped it would be resolved with version 1.1.1, but unfortunately the issue still occurs in the latest version.
Expected behavior
The Function with ActivityTrigger should accept
byte[]
as valid input.Actual behavior
The following exception is thrown in the autogenerated
DirectFunctionExecutor
Relevant source code snippets
Create a new empty Function App. Change the Program.cs to the following:
And create class
FanOutByteArrayOrchestrator.cs
with the below content:Content of the .csproj file:
Screenshot
Known workarounds
Downgrade to Microsoft.Azure.Functions.Worker.Extensions.DurableTask 1.0.4.
If deployed to Azure
Same behavior is seen when deployed to Azure - in my case an isolated azure function app.