Azure-functions-durable-extension: SignalEntity Function: ScheduledTimeUtc Not Being Taken Into Account

Created on 20 Mar 2020  路  5Comments  路  Source: Azure/azure-functions-durable-extension

Description

I want an entity to call itself on a schedule (kind of like it is operating as its own orchestration) but the parameter scheduledTimeUtc appears to take no effect.

Expected behavior

The operation signaled fires off on the UTC time specified.

Actual behavior

The operation signaled fires off immediately.

Relevant source code snippets

        public async Task DoSomething()
        {

            var later = DateTime.UtcNow.AddSeconds(15);

            Statuses.Add($"Triggered at {DateTime.UtcNow}, next should run at {later}");

            // the DoSomething() method fires off instantly, despite the code suggesting
            // it should wait 15 seconds before running again
            Entity.Current.SignalEntity<IMyEntity>(
                Entity.Current.EntityId,
                later,
                entity => entity.DoSomething()
            );
        }

Known workarounds

None known at this stage.

App Details

  • Durable Functions extension version (e.g. v1.8.3): 2.1.1
  • Azure Functions runtime version (1.0 or 2.0): Microsoft.NET.Sdk.Functions v3.05
  • Programming language used: C#
bug

Most helpful comment

Thanks for looking into it and submitting such a detailed repro, @johndowns!

I was able to track this bug down and submit a PR #1345 with the fix.

All 5 comments

@sebastianburckhardt is this something you could take a quick look at?

Hi @speedy-ms, thanks for the report and repro snippet!

I tried to repro with the code you provided (see complete below) but it seems to be working as expected. With the code as pasted below I can send a "DoSomething" signal:

$ curl http://localhost:7071/api/DoSomething -d ""

and then if I check a bit later I see that it fired periodically as expected:

$ curl http://localhost:7071/api/Get
{"entityExists":true,"entityState":{"statuses":["Triggered at 3/20/2020 2:31:32 PM, next should run at 3/20/2020 2:31:47 PM","Triggered at 3/20/2020 2:31:58 PM, next should run at 3/20/2020 2:32:13 PM","Triggered at 3/20/2020 2:32:23 PM, next should run at 3/20/2020 2:32:38 PM"]}}

Is there something that is different in your situation? I think I used the same runtime and library versions. If you use the code exactly as below, do you still see the issue?

Code for repro:

 public interface IMyEntity
    {
        void DoSomething();
    }

    [JsonObject(MemberSerialization.OptIn)]
    public class MyEntity : IMyEntity
    {
        [JsonProperty()]
        public List<string> Statuses { get; set; } = new List<string>();

        public void DoSomething()
        {
            if (Statuses.Count > 100) return;

            var later = DateTime.UtcNow.AddSeconds(15);

            Statuses.Add($"Triggered at {DateTime.UtcNow}, next should run at {later}");

            Entity.Current.SignalEntity<IMyEntity>(
                Entity.Current.EntityId,
                later,
                entity => entity.DoSomething()
            );
        }

        [FunctionName(nameof(MyEntity))]
        public static Task Run([EntityTrigger] IDurableEntityContext ctx) => ctx.DispatchAsync<MyEntity>();
    }

    public static class HttpBindingForTesting
    { 
        [FunctionName("DoSomething")]
        public static async Task<IActionResult> DoSomething(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
            [DurableClient] IDurableClient client)
        {
            await client.SignalEntityAsync<IMyEntity>("x", p => p.DoSomething());
            return new OkResult();
        }

        [FunctionName("Get")]
        public static async Task<IActionResult> Get(
           [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req,
           [DurableClient] IDurableClient client)
        {
            var response = await client.ReadEntityStateAsync<MyEntity>(new EntityId("MyEntity", "x"));
            return new OkObjectResult(response);
        }
    }

Thanks for promptly looking at it - it does indeed work as expected!

I can't explain the difference that I was seeing a few days back, I have since moved on to using a more standard orchestration / activity function model. I might spend some time looking at my previous code and seeing if there was a glaring hole in my logic, but until then I will close off this issue.

@sebastianburckhardt I have just run into the same issue and have been able to consistently reproduce it. It seems to only occur under the following conditions:

  • The method within the entity must return Task.
  • The overload of SignalEntity must be one that takes a type parameter.

In these conditions, the scheduledTimeUtc seems to be ignored.

For example, here is an entity method that exhibits the behaviour:

public Task SignalTestAsynchronousDoesNotWork()
{
    var later = DateTime.UtcNow.AddSeconds(15);
    Entity.Current.SignalEntity<IMyEntity>(
        Entity.Current.EntityId,
        later,
        async entity => await entity.SignalTestAsynchronousDoesNotWork() // Also tried using `entity => entity.SignalTestAsynchronousDoesNotWork()` - same result
    );
    return Task.CompletedTask;
}

And here is a complete Gist that shows the issue as well as provides two working methods for contrast.

Thanks for looking into it and submitting such a detailed repro, @johndowns!

I was able to track this bug down and submit a PR #1345 with the fix.

Was this page helpful?
0 / 5 - 0 ratings