Search code examples
azure-functionsazure-durable-functionsazure-functions-isolated

TaskActivityContext is null within activity function in Durable Functions (Isolated)


I recently upgraded to the isolated NET7 Azure Functions worker. Durable Function's API has changed quiete a bit in with that, but it is GA now.

I need to get the orchestration's instance id from within the activity function. Previously (with the in-process model), I was able to inject the IDurableActivityContext interface in the function and reference the instance id from there.

Afaik, the equivalent now is the TaskActivityContext class. However, if I inject this into the function it is always null.

How to get it? I could just pass it as an input from the orchestrator. But I would not prefer this.

namespace MyNamespace;

public class TestActionable
{
    private readonly ILogger _log;
    public TestActionable(ILogger<TestActionable> log) => _log = log;

    [Function("TestActionable_Trigger")]
    public async Task TriggerWorkflow([DurableClient] DurableTaskClient client,
    [QueueTrigger(QueueNames.ACTIONABLE_TEST, Connection = BindingConnectionNames.MY_BINDINGNAME)]string q)
    {
        var now = DateTimeOffset.UtcNow.ToIsoString();
        _log.LogWarning("Triggering workflow now");
        await client.ScheduleNewOrchestrationInstanceAsync("TestActionable_Workflow", input: now);
        _log.LogWarning("Triggered");
    }


    [Function("TestActionable_Workflow")]
    public static async Task RunWorkflow([OrchestrationTrigger] TaskOrchestrationContext ctx)
    {
        var log = ctx.CreateReplaySafeLogger("TestActionable_Workflow");
        log.LogWarning("Workflow started with input= {input}", ctx.GetInput<string>());

        var input = new { a=1337 };
        var output = await ctx.CallActivityAsync<bool>("TestActionable_Activity", input);
        log.LogWarning("Activity completed successfully= {output}", output);
        log.LogWarning("Workflow complete");
    }
    

    [Function("TestActionable_Activity")]
    public async Task<bool> RunActivity([ActivityTrigger] object input, TaskActivityContext ctx)
    {
        _log.LogWarning("Started activity, input= {i}", input);

        if (ctx is null)
        {
            _log.LogError("TaskActivityContext is null");
            return false;
        }

        _log.LogWarning("TaskActivityContext= {ctx}", ctx.Serialize());
        _log.LogWarning("The orchestration's instance id is= {ctx}", ctx.InstanceId);
        _log.LogWarning("Waited 1s, activity complete");
        await Task.Delay(1000);
        return true;
    }
}

Here's the log output

[2023-03-03T08:03:01.668Z] Executing 'Functions.TestActionable_Trigger' (Reason='New queue message detected on 'actionable-test'.', Id=1cc9891b-85a4-4dd4-90e2-19417cc7519a)
[2023-03-03T08:03:01.674Z] Trigger Details: MessageId: ae725935-4b5c-4299-ac48-44f1af934363, DequeueCount: 1, InsertedOn: 2023-03-03T08:03:00.000+00:00
[2023-03-03T08:03:01.863Z] Host lock lease acquired by instance ID '00000000000000000000000041F3D3B5'.
[2023-03-03T08:03:02.192Z] Scheduling new TestActionable_Workflow orchestration with instance ID '388c8c6c4f554f49ac1d9f50ac747e9b' and 28 bytes of input data.
[2023-03-03T08:03:02.192Z] Triggering workflow now
[2023-03-03T08:03:02.648Z] Triggered
[2023-03-03T08:03:02.757Z] Executed 'Functions.TestActionable_Trigger' (Succeeded, Id=1cc9891b-85a4-4dd4-90e2-19417cc7519a, Duration=1152ms)
[2023-03-03T08:03:06.020Z] Executing 'Functions.TestActionable_Workflow' (Reason='(null)', Id=0e4c510b-78c9-4e3a-8e9a-8f3cef8b4eb5)
[2023-03-03T08:03:06.213Z] Workflow started with input= 2023-03-03T08:03:02.12594Z
[2023-03-03T08:03:06.333Z] Executed 'Functions.TestActionable_Workflow' (Succeeded, Id=0e4c510b-78c9-4e3a-8e9a-8f3cef8b4eb5, Duration=358ms)
[2023-03-03T08:03:06.498Z] Executing 'Functions.TestActionable_Activity' (Reason='(null)', Id=07db96ca-df51-41e2-9906-e7012d7f2eda)
[2023-03-03T08:03:06.533Z] Started activity, input= {"a":1337}
[2023-03-03T08:03:06.533Z] TaskActivityContext is null
[2023-03-03T08:03:06.548Z] Executed 'Functions.TestActionable_Activity' (Succeeded, Id=07db96ca-df51-41e2-9906-e7012d7f2eda, Duration=56ms)
[2023-03-03T08:03:06.692Z] Executing 'Functions.TestActionable_Workflow' (Reason='(null)', Id=edde658a-c820-41bb-8d09-cc13ca14040d)
[2023-03-03T08:03:06.721Z] Activity completed successfully= False
[2023-03-03T08:03:06.721Z] Workflow complete
[2023-03-03T08:03:06.739Z] Executed 'Functions.TestActionable_Workflow' (Succeeded, Id=edde658a-c820-41bb-8d09-cc13ca14040d, Duration=52ms)

Solution

  • Apparently, this is by design. As noted here there is no equivalent in the isolated worker for DurableActivityContext. The TaskActivityContext I mentioned in my question, and which I figured to be the replacement for what I need (which it is not), appears to be only usable with the new approach of writing durable functions code, see class-based.