Sean Feldman
Sean Feldman

Reputation: 26002

Durable Functions - infinite execution with state doesn't work

I'm setting up a durable Function that should run infinitely with by invoking itself every hour and keeping a state. The function compiles, bus fails at run with the following error:

2017-07-12T07:04:05.614 Exception while executing function: Functions.DurableTimer. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'context'. Microsoft.Azure.WebJobs.Extensions.DurableTask: Cannot convert to DurableOrchestrationContext.

Function code

#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"

using System;
using System.Threading;

public static async Task Run(DurableOrchestrationContext context, TraceWriter log)
{
    log.Info($"Durable function executed at: {context.CurrentUtcDateTime}");

    // sleep for one hour between calls
    DateTime nextExecution = context.CurrentUtcDateTime.AddHours(1);
    await context.CreateTimer(nextExecution, CancellationToken.None);

    int counterState = context.GetInput<int>();
    log.Info($"Counter state: {counterState}");

    counterState++;

    context.ContinueAsNew(counterState); 
}

function.json

{
  "bindings": [
    {
      "name": "context",
      "type": "orchestrationTrigger",
      "direction": "in"
    }
  ],
  "disabled": false
}

Upvotes: 3

Views: 790

Answers (1)

Chris Gillum
Chris Gillum

Reputation: 15042

My guess is that you are trying to manually trigger the orchestration function in the portal UI, which unfortunately is not supported at this time. I verified by trying to reproduce your exact scenario and clicking the Run button in the portal. I've opened a bug to track this issue: https://github.com/Azure/azure-functions-durable-extension/issues/10

Assuming that's the case, you can workaround it by creating a separate function which knows how to trigger your orchestrator function. Here is the example I posted in the GitHub issue:

Code

#r "Microsoft.Azure.WebJobs.Extensions.DurableTask"

using System;

public static async Task Run(string functionName, DurableOrchestrationClient starter, TraceWriter log)
{
    log.Info($"Starting orchestration named: {functionName}");
    string instanceId = await starter.StartNewAsync(functionName, null);
    log.Info($"Started orchestration with ID = '{instanceId}'.");   
}

function.json

{
  "bindings": [
    {
      "type": "manualTrigger",
      "direction": "in",
      "name": "functionName"
    },
    {
      "name": "starter",
      "type": "orchestrationClient",
      "direction": "in"
    }
  ],
  "disabled": false
}

This is a generic function that can start any orchestrator function by name. In your case, you can put DurableTimer as the input.

Apologies that this is non-obvious and thanks for your patience as we smooth out the experience. :)

Upvotes: 4

Related Questions