Running an azure function on a specific time for only once? - c#

So I'm working on sending mobile app push notifications and I've already set up Azure Notification hub, but I wanted to do scheduling in it, I understand there's an option in build in azure to do that, since it costs 200$ i decided to create Azure Functions to handle it,
I've gone through the NCRON Expressions, now I wanted to know how to schedule a job to run once on a specific date, all I could find is repetition based ones and also is it possible to run a job dynamically as in the date would vary
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("0 0 15 2 Jan")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
I was trying to do something like this, running something on 2nd of Jan at 15:00Hrs, it doesn't seem to work, am I missing something here and how do I make TimerTrigger dynamic?

To describe clearly, I reedit the whole answer. Hope this time I can explain clearly.
OK. Fist of all, you need to know, azure Function has a declaration section and a configuration section.
On local, the declaration section is ([TimerTrigger("* * * * * *")]TimerInfo myTimer, ILogger log), and the configuration section is local.settings.json file.
When you deploy to Azure. It changes. Declaration section turns to function.json, and the Application Settings becomes the configuration section.
To your requirement, you can add a key in the configuration section and get it in your function.
For example,
On local:
function.cs:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace TimeTrigger
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("%Schedule%")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
}
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"Schedule": "* * * * * *"
}
}
You can change how the timetrigger is triggered by changing the value of the key in the json.(For example, using powershell to modify the value. Or you can use code to modify.)
On portal:
And similar as on local, you can do this on portal:
Declaration section:
Configuration Section:

You can generate the detailed CRON expression using below link:
http://corntab.com/?c=0_15_2_1_*
0 0 15 2 Jan * is the resulting CRON expression which will run it every 2nd day of January at 1500 hours for any years.
As rightly suggested by #Bowman, please check and validate the expression from above link and see if it helps.

Firstly the main problem you have now suppose is your expression, the right expression format you could refer to the doc:NCRONTAB expressions.
{second} {minute} {hour} {day} {month} {day-of-week}
Then about your requirement about run a job dynamically. You can put the schedule expression in an app setting and set this property to the app setting name wrapped in % signs, as in this example: %ScheduleAppSetting%. You could check it in the configuration.
Even with this expression it will show an error, however it will still works.

Why using a timer function if you need to run it only once?
I would say a better solution is to send a message to a queue so it runs at a scheduled time using the attribute ScheduledEnqueueTimeUtc.
https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-sequencing#scheduled-messages

Related

azure Webjob TimerTrigger, force scheduling

I have an azure webjob TimerTrigger.
We may be led to stop the webjob, in a way or another (for instance changing an appSetting, aborts the webjob).
The webjob is defined as TimerTrigger in Azure. There are storage accounts for AzureWebJobsDashboard and AzureWebJobsStorage in the appSettings of the webjob. The webjob is executed at a certain time of the day. The execution of the webjob may last 5 or 6 hours. there are massive writings in a storage account (around 13000) and in storage table.
The question is :
When restarting the webjob, there is, often, an UnscheduledInvocationReason: IsPastDue, OriginalSchedule. I would like to avoid that, that the next execution of the webjob will be done accordingly to the cron expression of the timerTrigger webjob.
Is it possible, and if so how to do that ?
Any idea ?
Regards.
If I understand the problem correctly, you could use RunOnStartup = false (default) and UseMonitor = false
Timer trigger for Azure Functions
Parameter
Description
RunOnStartup
If true, the function is invoked when the runtime starts. For example, the runtime starts when the function app wakes up after going idle due to inactivity. when the function app restarts due to function changes, and when the function app scales out. So runOnStartup should rarely if ever be set to true, especially in production.
UseMonitor
Set to true or false to indicate whether the schedule should be monitored. Schedule monitoring persists schedule occurrences to aid in ensuring the schedule is maintained correctly even when function app instances restart. If not set explicitly, the default is true for schedules that have a recurrence interval greater than or equal to 1 minute. For schedules that trigger more than once per minute, the default is false.
There are a few ways to set these values, however it can be set from the attribute it self
[FunctionName("MyLovelyHorseFunction")]
public static void Run(
[TimerTrigger(
"0 */5 * * * *",
RunOnStartup = false,
UseMonitor = false)]
TimerInfo myTimer,
ILogger log)
{
...
}
Also note, you can explicitly check for past due (which may or may not help you out depending on your needs)
if (myTimer.IsPastDue)
{
...
}

ASPBoilerplate BackgroundJobs database pooling

I am using ABP framework to develop a web application. By default, the default background job manager pools the database every five seconds to find possible existing defined jobs. According to ABP documents and its implementation I find that it's set to five seconds by default. However, I am not able to update the timer to update this. Even if I tried to disable it using the code below, it was unsuccessful and it is still running.
if (DebugHelper.IsDebug)
{
Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
}
To continue, is there a way to replace this background job to quartz only. I think in the case of adding Quartz integration, they both run concurrently.
Any help will be appreciated.
According to its implementation the default value has been set to five seconds and this can be configured and overridden at the PreInitialize method of the CoreModule. All it needs is below:
public override void PreInitialize()
{
...
BackgroundJobManager.JobPollPeriod = 20000; //20 seconds
...
}

Azure timertriggered webjob hangs randomly

I want to schedule a timertriggered method to call other methods but somehow the CronJob method won't run if I use it to call one of my own methods, I simply get this console output:
"
Found the following functions:
...ProcessQueueMessage
...Functions.CronJob
Job host started
"
and nothing else happens for a couple of minutes and then it might suddenly start working. But if I only use the CronJob() method for running it's own Console.WriteLine("Timer job fired") statement everything works.
I have been trying to find a solution to this problem for hours now but no one seems to have the same problem. Any ideas on what I'm doing wrong?
public static void CronJob([TimerTrigger("*/3 * * * * *", RunOnStartup = true)] TimerInfo timerInfo)
{
Console.WriteLine("Timer job fired! ");
DoTask();
}
private static void DoTask()
{
Console.WriteLine("Doing task...");
}
Main method:
static void Main()
{
var config = new JobHostConfiguration();
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
var host = new JobHost(config);
config.UseTimers();
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
Any ideas on what I'm doing wrong?
According to your description, it is not related with whether you call code directly. The root reason is that a blob lease (the Singleton Lock) is taken for a default time of 30 seconds.
As Rob Reagan mention that you could set JobHostConfiguration.Tracing.ConsoleLeve
to Verbose. When the webjob hangs you could get the information "Unable to aquire Singleton lock".
For more detail info you could refer to this issue.
When the listener starts for a particular TimerTrigger function, a blob lease (the Singleton Lock) is taken for a default time of 30 seconds. This is the lock that ensures that only a single instance of your scheduled function is running at any time. If you kill your console app, that lease will still be held until it expires naturally

Configurable Timer Triggers - Azure Web Jobs

I'm building a job to trigger at some regular interval (say 1 minute). I've successfully used triggered web jobs with time-span hard coded in the functions.
public void foo([TimerTrigger("00:01:00")] TimerInfo timer)
Now if I ever want to change the trigger time from 1-min to 2-min I've to redeploy the code. Instead is there a way to make the TimeTrigger Configurable, from a config file.
Note that replacing the string with a dynamically read value isn't possible as the TimerTrigger Attribute is a const string expression or a Type.
After much digging, I've realized that this can be done with an SDK extension class TimerSchedule.
For it, you would need a base class that you'll be able to use for multiple triggers.
class CustomTimerTriggerBase: TimerSchedule
{
TimeSpan timer;
public CustomTimerTriggerBase(string triggerConfigKey)
{
timer=TimeSpan.Parse(ConfigurationManager.AppSettings[triggerConfigKey]);
}
public override DateTime GetNextOccurrence(DateTime now)
{
return now.Add(timer);
}
}
Use this Base to generate your timers...
public sealed class FooTimer : CustomTimerTriggerBase
{
public FooTimer() : base("FooTimerKey") {}
}
In your, App.config have a key for "FooTimer"
<add key="FooTimerKey" value="00:02:00" />
Use this FooTimer class in your webjob functions.
public void foo([TimerTrigger(typeof(FooTimer)] TimerInfo timer)
Now you can simply change the value in app config instead of redeploying the code.
NOTE: Since you are using Timespan to parse, the string can be of any format you need as defined in TimeSpan formats.
UPDATE
As pointed by l--''''''---------'''''''''''' and Andy Dobedoe
now (as of 2019) it is much simpler to achieve this.
public static async Task RunAsync([TimerTrigger("%MYCRON%")]TimerInfo myTimer
Finds the setting called MYCRON and uses the cron expression from there
You can do this like so:
public static void Run([TimerTrigger("%MYSCHEDULE%")] TimerInfo myTimer, ILogger log)
where MYSCHEDULE is an environment variable which you can store in your local.settings.json file as well as in your application settings in the portal.
An example value for MYSCHEDULE would be:
"MYSCHEDULE": "0 */2 * * * *"
It turns out, this is pretty easy nowadays. Just put the app setting in as your cron schedule expression and it will look it up for you.
e.g.
public static async Task RunAsync([TimerTrigger("%MYCRON%")]TimerInfo myTimer
Finds the setting called MYCRON and uses the cron expression from there
AFAIK, you need to specific the scheduleExpression parameter for TimerTrigger in your code or implement your WeeklySchedule or DailySchedule described in this sample TimerSamples.cs. For changing the schedule without re-deploy your code, I assume that you could leverage Azure Scheduler to trigger your webjob on some schedule and you could change the schedule settings as you expected without re-deploy your webjob. For more details, you could refer to the section about adding a scheduler job in this tutorial.

Debug Azure WebJob locally

I've been creating an Azure WebJob, it works aparently fine but I need to create a new function and I need test locally before upload to production site, I run on Debug the console program and this recognize all functions but I can't trigger any function.
Documentation say next trigger is every minute.... (https://github.com/Azure/azure-webjobs-sdk-extensions#timertrigger)
My code:
public static async void ProcessAugustEndowments([TimerTrigger("0 */1 * * * *", RunOnStartup = true)] TimerInfo timerInfo)
{
Console.WriteLine("Endowments process tried");
await endowmentNotification();
}
Output:
I run on Debug the console program and this recognize all functions but I can't trigger any function.
Based on your code, I tested it on my side and found that when my firstly debug the application, then I could get the following output:
But, when I restarted the application, I found that it would take some time for the function to be triggered.
Please make sure that you have installed the latest version packages of "Microsoft.Azure.WebJobs" and "Microsoft.Azure.WebJobs.Extensions". For more details, you could follow this tutorial.
And please try to reduce the time interval in your TimerTrigger and wait for a period when the job host has started, then try to find out whether the function could be triggered on your side. Here is my code sample, you could refer to it.
Program.cs
static void Main()
{
JobHostConfiguration config = new JobHostConfiguration();
// Add Triggers and Binders for Timer Trigger.
config.UseTimers();
JobHost host = new JobHost(config);
//host.RunAndBlock();
host.Start();
Console.WriteLine("[{0}] Job Host started!!!", DateTime.Now);
Console.ReadLine();
}
Function.cs
//Function triggered by a timespan schedule every 5 sec.
public static async void ProcessAugustEndowments([TimerTrigger("*/5 * * * * *", RunOnStartup = true)] TimerInfo timerInfo)
{
Console.WriteLine("Endowments process tried");
await endowmentNotification();
}
private static async Task endowmentNotification()
{
//sleep for 2 sec to simulate processing business logic
await Task.Delay(TimeSpan.FromSeconds(2));
}
Additionally, if the TimerTrigger could not meet your requirement, you could refer to this official tutorial to create a schedule WebJob, also you could refer to this blog.
If your functions are not triggered, I presume it is because you didn't configure your JobHost you use TimerTrigger:
var config = new JobHostConfiguration();
config.UseTimers();
How the JobHost works with Triggers :
When the JobHost starts, it discovers and indexes the functions with some *TriggerAttribute.
By Specifying the config.UseTimers(); you tell to the JobHost to index functions with TimerTriggerAttribute.
This is also valid for others types of triggers like ServiceBusTrigger that need config.UseServiceBus() to work

Categories