Search code examples
azure-functionsazure-webjobs

Azure WebJobs: how to force a function to run on schedule


I have a continuous webjob that contains a function with a TimerTrigger configured to run each minute:

public static async Task TestFunction([TimerTrigger("0 * * * * *")] TimerInfo info)
{
    Console.WriteLine($"{DateTime.UtcNow:s}: beginning to process the function...");

    // Imitate a long processing
    await Task.Delay(75 * 1000);

    Console.WriteLine($"{DateTime.UtcNow:s}: finished processing the function");
}

Let's say the first invocation is at 00:00:00. Then, because the function runs longer than a minute, the next invocation is past due and is triggered at 00:01:15. The next is at 00:02:30 etc.

I'd like the function to be invoked exactly on schedule, even if it'll result in two functions running in parallel.
Multiple instances section in WebJobs SDK docs states that:

"The timer trigger automatically ensures that only one instance of the timer runs, so you don't get more than one function instance running at a given scheduled time"

Is there any way to change this behaviour?
Or should I use something else to achieve the required behaviour? (Scheduled WebJob, Azure Function or some other service?)


Solution

  • I've solved the problem by introducing a queue and splitting the function into two: a producer and a consumer.

    The first function is triggered on schedule and just puts messages on to the queue:

    public static async Task TestProducer([TimerTrigger("0 * * * * *")] TimerInfo info)
    {
        var startTime = DateTime.UtcNow;
        var message = new BrokeredMessage(startTime);
        var queueClient = QueueClient.CreateFromConnectionString("ServiceBusConnString", "queue-name");
        await queueClient.SendAsync(message);
    }
    

    The second function does the actual work:

    public static async Task TestConsumer([ServiceBusTrigger("queue-name")] DateTime startTime)
    {
        Console.WriteLine($"{DateTime.UtcNow:s}: beginning to process the function for startTime={startTime}");
    
        // Imitate a long processing that depends on a startTime
        await Task.Delay(75 * 1000);
    
        Console.WriteLine($"{DateTime.UtcNow:s}: finished processing the function for startTime={startTime}");
    }
    

    When the first function enqueues a new message while the second function is still working, a new instance of the second function is invoked.