I want to have a very lightweight approach where I expose an HTTP endpoint (trigger) that somewhat exposes a Durable Entity (actor) and has the actor perform work on a frequent basis.
The logic in my HTTP trigger is like this:
- Check if entity with uid already exists
- If not, signal entity and fire
InitializeMonitoringAsync
method on the actor - If yes, call entity state on the actor and return some values
- Once initialized, the actor (durable entity) should frequently call the
RefreshActionAsync
on its own.
The issue I have is that the Refresh method seems to keep executing constantly, without the configured delays to be taken into account. What am I missing?
Update (workaround):
Apparently, when I change the interface methods (that now return a Task
) to methods that return void
(and I change the signal call to : e => e.RefreshActionAsync()
), then it seems to work. But I don't understand why, and this doesn't seem to be a documented limitation either.
The code below:
Entity interface
public interface IReproEntity
{
Task RefreshActionAsync();
Task InitializeMonitoringAsync();
}
Entity function
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ReproEntity : IReproEntity
{
[JsonProperty] public bool Activated { get; set; }
[JsonProperty] public int LoopCount { get; set; }
public Task InitializeMonitoringAsync()
{
ScheduleNext(DateTime.UtcNow.AddSeconds(10));
Activated = true;
return Task.CompletedTask;
}
public Task RefreshActionAsync()
{
LoopCount += 1;
ScheduleNext();
return Task.CompletedTask;
}
private void ScheduleNext(DateTime nextSchedule = default)
{
if (nextSchedule == default) nextSchedule = DateTime.UtcNow.AddSeconds(30);
Entity.Current.SignalEntity<IReproEntity>(Entity.Current.EntityId, nextSchedule,
async (e) => await e.RefreshActionAsync());
}
[FunctionName(nameof(ReproEntity))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
{
return ctx.DispatchAsync<ReproEntity>();
}
}
HTTP Trigger function
[FunctionName("Repro_HttpStart")]
public async Task<IActionResult> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "repro/{uid}")]
HttpRequest req, string uid,
[DurableClient] IDurableEntityClient entityClient,
ILogger log)
{
var entityId = new EntityId(nameof(ReproEntity), uid);
var entityState = await entityClient.ReadEntityStateAsync<ReproEntity>(entityId);
if (!(entityState.EntityExists && entityState.EntityState.Activated))
{
await entityClient.SignalEntityAsync(entityId, nameof(IReproEntity.InitializeMonitoringAsync));
return new NotFoundObjectResult(new {error = $"The actor {uid} does not exist"});
}
return new OkObjectResult(new
{
entityState.EntityState.LoopCount, entityState.EntityState.Activated
});
}