3

I have a [DurableOrchestrationClient] that starts a [OrchestrationTrigger] that starts a long running [ActivityTrigger] function.
I know how to use TerminateAsync() to Terminate the [OrchestrationTrigger] from running.

The problem is that [ActivityTrigger] is not aborted when I terminate the [OrchestrationTrigger] function. [ActivityTrigger] keeps running until its done.
I need a way to abort the long running [ActivityTrigger] when I call TerminateAsync().

My guess is that I can pass a CancellationToken to [ActivityTrigger] function and then check cancellationToken.IsCancellationRequested to abort.
But how to do this?

Here is a test code


        [FunctionName("A_ProcessPayment")]
        public static async Task<processTracker> A_ProcessPayment(
            [ActivityTrigger] DurableActivityContext context, 
            TraceWriter log,
            CancellationToken cancellationToken)
        {
            processTracker p = context.GetInput<processTracker>();

            try
            {
                for (int i = 0; i < 5; i++){
                    if (cancellationToken.IsCancellationRequested) // This is always false! 
                    {
                        break;  
                    }

                    Trace.WriteLine("Long task loop: " + i);
                    await Task.Delay(10000);
                }
            }
            catch (OperationCanceledException) {
                log.Warning("C# HTTP trigger function canceled.");
                return p;
            }

            Trace.WriteLine("Long task DONE");
            return p;
        }
Henrik
  • 243
  • 1
  • 3
  • 12

1 Answers1

1

Update:

You can choose to pass the token as an object to the Activity Trigger, like this:

using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

namespace OrchesterTrigger
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<List<string>> RunOrchestrator(
            [OrchestrationTrigger] DurableOrchestrationContext context)
        {
            var outputs = new List<string>();
            string mytoken = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
            outputs.Add(await context.CallActivityAsync<string>("Function1", mytoken));
            return outputs;
        }

        [FunctionName("Function1")]
        public static string SayHello([ActivityTrigger] string mytoken, ILogger log)
        {
            log.LogInformation($"Mytoken is {mytoken}.=======================================");
            return $"Mytoken is {mytoken}!";
        }

        [FunctionName("Function1_HttpStart")]
        public static async Task<HttpResponseMessage> HttpStart(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
            [OrchestrationClient]DurableOrchestrationClient starter,
            ILogger log)
        {
            // Function input comes from the request content.
            string instanceId = await starter.StartNewAsync("Function1", null);

            log.LogInformation($"================================Started orchestration with ID = '{instanceId}'.");

            return starter.CreateCheckStatusResponse(req, instanceId);
        }
    }
}

What I showed is to pass in a value of type string, you can replace it with the type you want, it is type compatible.

Original Answer:

There is no way to stop a activity trigger once they have been start. you can choose to break the steps into more steps.

Have a look of the offcial doc. Activity functions and sub-orchestrations will run to completion, regardless of whether you've terminated the orchestration instance that called them.

Cindy Pau
  • 13,085
  • 1
  • 15
  • 27
  • Thanks. I wonder if there is a way to pass a cancellationToken so I can manually break my long running activity function? In my example code I have a cancellationToken that break the long running loop. If not, then I need to handle this logic myself. Maybe use Redis to set a state that break the loop. – Henrik Dec 25 '19 at 17:22
  • @HenrikBergström Yes, you can pass an object to the activity trigger. I have update the answer. Does this help you? If you have any questions, welcome to discuss. – Cindy Pau Dec 26 '19 at 07:10
  • Thanks again. The problem I need to solve is not to pass token as string. The problem is to have a cancellationToken that is Automaticly canceled when I call TerminateAsync(). Then I use this cancellationToken in all [ActivityTrigger] function and have this break the long running loops. Sorry if my question was unclear. – Henrik Dec 26 '19 at 11:26
  • @HenrikBergström I know, but you can pass anything you what. Not must string. – Cindy Pau Dec 26 '19 at 11:29
  • @HenrikBergström Actually, the type is Object. – Cindy Pau Dec 26 '19 at 11:30
  • 1
    When I pass the CancellationToken to [ActivityTrigger] and call TerminateAsync() the CancellationToken is not affected. I wonder if there is a way to have CancellationToken to be set to canceled. – Henrik Dec 26 '19 at 11:31
  • @Henrik Note that the different part of the durable function may run on different `node`. We can only use some kind of RPC to signal. We can send RPC registration information as parameter (string) to provide signaling. – minus one Feb 11 '22 at 22:15
  • @Henrik The cancellation token will get serialized to JSON and back when the activity starts so there is no link to the instance of the original token. You'd be better to use a durable entity that wraps around a cancellation token [see here](https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-entities?tabs=csharp) – Will Blair Jul 25 '22 at 10:45