Post

Prevent Duplicate Invocations of Durable Functions Using Azure Functions With C#

Prevent Duplicate Invocations of Durable Functions Using Azure Functions With C#

Durable Functions is an extension for Azure Functions to help write stateful services in a stateless environment. When starting a new process, you may want to verify it isn’t already running by checking the list of in-flight Durable Functions with some custom logic.

Durable Functions includes functionality to Query all instances which can help us.

Note: With many in-flight Durable Functions this approach could be problematic by needing to query many pages of results.

Code

Here’s a starting point to work from, copy and edit as needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace MyApplication
{
    public class Api
    {
        [FunctionName("Initiate")]
        public async Task<HttpResponseMessage> Initiate(
            [HttpTrigger(AuthorizationLevel.User, "post", Route = "my/route")] HttpRequestMessage req,
            [DurableClient] IDurableOrchestrationClient client)
        {
            string listContinuationToken = null;
            do
            {
                var currentInstances = await client.ListInstancesAsync(
                    new OrchestrationStatusQueryCondition
                    {
                        ContinuationToken = listContinuationToken,
                        RuntimeStatus = new[]
                        {
                            OrchestrationRuntimeStatus.ContinuedAsNew,
                            OrchestrationRuntimeStatus.Pending,
                            OrchestrationRuntimeStatus.Running
                        }
                    },
                    CancellationToken.None);

                var alreadyRunning = currentInstances.DurableOrchestrationState
                    .Any(x => /* Logic here */);

                if (alreadyRunning)
                {
                    return new HttpResponseMessage(HttpStatusCode.BadRequest)
                    {
                        Content = new StringContent("This process is currently running.")
                    };
                }

                listContinuationToken = currentInstances.ContinuationToken;
            }
            while (listContinuationToken != null);

            var instanceId = await client.StartNewAsync("BeginProcess");

            return client.CreateCheckStatusResponse(req, instanceId);
        }

        [FunctionName("BeginProcess")]
        public async Task<IEnumerable<string>> BeginProcess(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        {
            // Code here
        }
    }
}
This post is licensed under CC BY 4.0 by the author.