Azure / azure-functions-dotnet-worker

Azure Functions out-of-process .NET language worker
MIT License
429 stars 183 forks source link

TimerTrigger does not fire #2427

Open oleks9o6 opened 6 months ago

oleks9o6 commented 6 months ago

Description

I'm using Durable Functions hosted on-premise with MSSQL database as a storage in isolated mode. I have set up a timer trigger but it never fires locally. The only way I could make it work is by using Azurite emulator locally. I assume the timer trigger requires Azure storage to work but I could not find it somewhere specified in the documentation.

If the assumption above is correct, is it possible to use the timer trigger without Azure storage? What about the warmup trigger?

Steps to reproduce

Microsoft.Azure.Functions.Worker 1.21.0 Microsoft.Azure.Functions.Worker.Extensions.DurableTask 1.2.0-rc.1 Microsoft.Azure.Functions.Worker.Extensions.DurableTask.SqlServer 1.2.2 Microsoft.Azure.Functions.Worker.Extensions.Timer 4.3.0

The code is taken from here https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer:

    public class DataRetention
    {
        [Function(nameof(DataRetention))]
        [FixedDelayRetry(5, "00:00:10")]
        public static void Run([TimerTrigger("0 */1 * * * *")] TimerInfo timerInfo, FunctionContext context)
        {
            var logger = context.GetLogger(nameof(DataRetention));
        }
    }
liliankasem commented 6 months ago

The timer trigger does require storage (it uses the host storage), it's briefly mentioned in documentation here:

The timer trigger uses a storage lock to ensure that there is only one timer instance when a function app scales out to multiple instances

https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=python-v2%2Cisolated-process%2Cnodejs-v4&pivots=programming-language-csharp#function-apps-sharing-storage

The developer guide doc talks about the host storage and dependencies a bit here:

To ensure one execution per event, locks are taken with blobs using the AzureWebJobsStorage connection.

https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob&pivots=programming-language-csharp#connecting-to-host-storage-with-an-identity

It is a bit all over so we can flag a documentation enhancement here to make this clearer in the Timer specific docs.

From our side, there is no way to use the Timer without storage. However, I don't know how durable works in this regard with allowing other storage providers (like MSSQL). So this might be a question for the durable team to support timers also using the configured storage?

@davidmrdavid - can help with this?

davidmrdavid commented 6 months ago

Hi @oleks9o6, @liliankasem :

The TimerTrigger is not a "Durable Functions"-specific trigger per se (those are only the activityTrigger, orchestrationTrigger, and entityTrigger), so the choice of MSSQL as the Durable Functions storage provider does not circumvent the need for an Azure Storage resource to power the TimerTrigger. Hope that makes sense!

oleks9o6 commented 6 months ago

Hi @liliankasem,

Thanks for the update. Enhancing the Timer-specific docs would be a great idea. Sometimes it is just unclear to what area/team the potential issue may belong.

@davidmrdavid, we currently cannot use an external Azure Storage so, it looks like we also cannot go with a TimerTrigger. Of course, there is another approach - eternal orchestration - that we will utilize. But it is not as elegant as a simple timer.

liliankasem commented 6 months ago

I'll mark this as a documentation item

davidmrdavid commented 6 months ago

Hi @oleks9o6 - an eternal orchestration should do the trick if I am imagining the way you are: an orchestration calling the createTimer API and then calling continueAsNew to restart itself. Definitely not as elegant, but if you're trying to circumvent Azure Storage, it may give you what you need.

oleks9o6 commented 6 months ago

Hi @oleks9o6 - an eternal orchestration should do the trick if I am imagining the way you are: an orchestration calling the createTimer API and then calling continueAsNew to restart itself. Definitely not as elegant, but if you're trying to circumvent Azure Storage, it may give you what you need.

Exactly. One more downside of eternal orchestration (to mimic the timer trigger functionality) is the need to perform an initial call to start it.

davidmrdavid commented 6 months ago

Yes, that's correct. Unfortunately, the DurableTimer is not meant to be a replacement for the TimerTrigger (the latter is a trigger, the former is a way to pause an orchestration, which is a trigger in itself). Let me know if this presents a blocker, happy to discuss workarounds, but we may need to move to another thread / repo.

@liliankasem: something else that came to mind, does the TimerTrigger not throw some kind of warnings if there's no AzureStorage connection to be found? Perhaps that would have helped debug this more interactively than documentation. Just my 2 cents!

oleks9o6 commented 6 months ago

Thanks, @davidmrdavid! I don't think it is a blocker for me - stating it manually once is something I can manage. But it would be much nicer to have the true timer trigger with an on-premise setup (despite it might be a niche scenario) as well.