2

I am using Azure Durable Functions in a research project and I want to verify what the best way is to implement the concept of Timers/Reminders in Durable Entities.

In Service Fabric, it was possible for an Actor to schedule a 'durable timer' so that the framework would call a method on the Actor itself. I am looking for a similar concept.

Consider a Durable Entity that is created for every Device in my system. I want that every actor runs functionality on a scheduled basis, and schedules this on its own. (I want to avoid having an orchestration function that needs to schedule tasks for every device, and just have the entities running the tasks on their own).

Is this something that is allowed, possible or foreseen?

Hope this question is clear enough, but happy to provide more context, where needed.

Sam Vanhoutte
  • 3,247
  • 27
  • 48
  • 1
    From [the docs](https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-entities?tabs=csharp#entity-operations): To invoke an operation on an entity, specify the [....] Scheduled time, which is an optional parameter for specifying the delivery time of the operation. For example, an operation can be reliably scheduled to run several days in the future. So there is an option to schedule invocations of the entities – Peter Bons Sep 30 '20 at 20:30
  • Thanks Peter. But then you schedule it from the outside. I would love to set repeatable schedules from inside the entity function, once it's started. Does that work? Almost like fire and forget from the orchestration and then have the actor running on its own schedule – Sam Vanhoutte Sep 30 '20 at 20:32
  • So, I guess that I should start an orchestration for every entity (device) that takes care of the Scheduling + Timers. That would work, but it creates double the amount of "active functions". – Sam Vanhoutte Oct 01 '20 at 07:29
  • 1
    I think there is a way, see the answer – Peter Bons Oct 01 '20 at 07:58

1 Answers1

5

From the docs:

To invoke an operation on an entity, specify the [....] Scheduled time, which is an optional parameter for specifying the delivery time of the operation. For example, an operation can be reliably scheduled to run several days in the future. So there is an option to schedule invocations of the entities

But, you want to schedule from within the Durable Entity. This is also possible by using Entity.Current.SignalEntity (docs). This method as an overload that accepts a DateTime which specifies the time at which to start the operation.

The code below will start incrementing a counter by using an http triggered function. After the initial start by the azure function the entity will schedule an operation every 5 seconds for itself.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace FunctionApp3
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            [DurableClient] IDurableEntityClient client,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            // Tell the Durable Entity to increase a counter
            await client.SignalEntityAsync<ICounter>("aKey", e => e.IncreaseCount(1));

            return new OkResult();
        }
    }

    public interface ICounter
    {
        void IncreaseCount(int amount);
    }

    [JsonObject(MemberSerialization.OptIn)]
    public class Counter : ICounter
    {
        private readonly ILogger _log;

        public Counter(ILogger<Counter> log)
        {
            _log = log;
        }

        [JsonProperty("value")]
        public int CurrentValue { get; set; }

        public void IncreaseCount(int amount)
        {
            this.CurrentValue += amount;

            if(this.CurrentValue) > 10
                 return;

            // Schedule a call to this entity to IncreaseCount after 5 seconds. 
            // Once the method is called, schedule it again to create the effect of a timer
            Entity.Current.SignalEntity<ICounter>(Entity.Current.EntityId, DateTime.Now.AddSeconds(5), e => e.IncreaseCount(1));
            _log.LogInformation(this.CurrentValue.ToString());
        }

        [FunctionName(nameof(Counter))]
        public static Task Run([EntityTrigger] IDurableEntityContext ctx)
        {
            return ctx.DispatchAsync<Counter>();
        }
    }
}

This is just a rough sample but the takeaway is the use of Entity.Current.SignalEntity to schedule work by the entity itself.

Peter Bons
  • 26,826
  • 4
  • 50
  • 74
  • `IncreaseCount` calls itself? i'm really confused – Alex Gordon Oct 01 '20 at 09:46
  • Well, it could call any function. It shows the concept of an entity calling itself with a schedule, like you wanted. The demo shows an entity that updates a counter all by itself according to a schedule. When the schedule is elapsed it executes a function and then reschedules the work, simulating a timer in this case. But the point is, you can do what you want is to use SignalEntity – Peter Bons Oct 01 '20 at 09:51
  • where is that schedule indicated? where does it actually say "i want to call this every 5 minutes" ? – Alex Gordon Oct 01 '20 at 09:55
  • 1
    You cannot tell it to call something every 5 minutes like a timer. You can only schedule a method to be called once. But once that method is called, you can schedule again. That is what this code demonstrates: an entity that calls itself every 5 seconds. – Peter Bons Oct 01 '20 at 09:57
  • could we interrupt that operation? can we "stop" the "timer" ? – Alex Gordon Oct 01 '20 at 10:02
  • Yes, because you are in control and can decide whether to call `Entity.Current.SignalEntity(...)` or not. If it is not called no further executions will be scheduled. – Peter Bons Oct 01 '20 at 10:06
  • Maybe if you can add some (pseudo) code of what you want to accomplsih with your durable entity we can create a more focused example in case this one is confusing to you. – Peter Bons Oct 01 '20 at 10:07
  • 1
    very kind of you to offer so much help, thank you. im still trying to evaluate whether to use table storage, some other caching, or durable entities, i'm having a tough time understanding the advantages of durable entities over other approaches – Alex Gordon Oct 01 '20 at 10:09
  • 1
    Thank you, @PeterBons ! Your answer is spot on and really looks like what I needed. – Sam Vanhoutte Oct 01 '20 at 11:08
  • 2
    One benefit of durable entities is that methods are executed serially, one at a time, to prevent race conditions. That means that no multiple processes can update the same entity concurrent. If you would be using table storage and you mutate multiple rows within a method you would have to deal with possible concurrency issues. See https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-entities?tabs=csharp#comparison-with-virtual-actors and https://dev.to/azure/diving-into-durable-entities-with-azure-functions-173e – Peter Bons Oct 01 '20 at 11:35