How to run functionality only sometimes in an Azure function - c#

I have an Azure function with a trigger to make it run once every 15 minutes:
TimerTrigger("0 */15 * * * *", RunOnStartup =false)
Within this function, there is some functionality that I only want to run once per hour. I am currently checking the minute of the current time to see whether it should run or not. But as I understand it, Azure function triggers are not always precise (this is running on a consumption-based app service), so instead I am checking for a range of minutes.
int currentMinute = DateTime.Now.Minute;
bool extraFunctionality = (currentMinute >= 58 && currentMinute <= 2);
This seems like it will work; only running this functionality during the ":00" runs, once per hour. However, it looks like bad code to me, for a couple reasons:
The up to 2 minutes early and 2 minutes late number was chosen pretty much arbitrarily; I don't have a good idea of what sort of time span to use there.
It relies on using DateTime.Now which will return different results depending on server settings, even though I don't care about anything other than the current minute.
The code simply doesn't read like the intent is to get it to run once per hour.
Is there a better / more proper way to do this within Azure functions? Can I either get information from the TimerInfo parameter or the ExecutionContext parameter about which 15-minute trigger caused the function to run? Or can I have a separate TimerTrigger which runs once per hour, and then have different functionality based on which of the 2 timers caused the function to trigger? Or is there some way to have the TimerTrigger itself pass in a parameter telling me which 15-minute window it is in?
Or, is my code fine as-is; perhaps with some adjustment to the number of minutes I allow it to be off?

You could create two Azure Functions, one which runs at 15, 30, 45 minutes past the hour and another which runs on the hour. Then in each of these functions set a variable for if it's runnin on the hour.
[FunctionName("HourlyFunction")]
public static void RunHourly([TimerTrigger("0 0 15,30,45 * * *")]TimerInfo myTimer, ILogger log)
{
_myService.Run(true);
}
[FunctionName("FifteenMinuteFunction")]
public static void RunEveryFifteen([TimerTrigger("0 0 * * * *")]TimerInfo myTimer, ILogger log)
{
_myService.Run(false);
}
Then have a separate service which can be called by those functions:
public class MyService : IMyService
{
public async Task Run(bool isHourlyRun)
{
// Do work here
}
}
* I'm not sure those cron expressions are correct

Related

Azure TimerTrigger is not working in .NET 7 Isolated Worker Process

I'm trying to trigger an orchestrated function TimerTrigger in .NET 7. Below is the function I created
[Function(nameof(FunctionSampleTimer))]
public async Task Run([TimerTrigger("*/5 * * * * *", RunOnStartup = false)] MyInfo myTimer, [DurableClient] DurableTaskClient client, FunctionContext context)
{
var instanceid = Guid.NewGuid().ToString();
var logger = context.GetLogger("TimerFunction");
logger.LogInformation($"Function Ran. Next timer schedule = {myTimer.ScheduleStatus.Next}");
}
The above function is getting triggered once every 5 seconds only for the first time when the project is created, and is not getting triggered again and getting stuck on that "Host lock lease acquired..." line.
Console output shows this:
[2023-02-20T01:26:52.843Z] Azure Functions .NET Worker (PID: 49120) initialized in debug mode. Waiting for debugger to attach...
[2023-02-20T01:26:53.305Z] The next 5 occurrences of the 'FunctionSampleTimer' schedule (Cron: '0,5,10,15,20,25,30,35,40,45,50,55 * * * * *') will be:
[2023-02-20T01:26:53.307Z] 02/19/2023 17:26:55-08:00 (02/20/2023 01:26:55Z)
[2023-02-20T01:26:53.308Z] 02/19/2023 17:27:00-08:00 (02/20/2023 01:27:00Z)
[2023-02-20T01:26:53.309Z] 02/19/2023 17:27:05-08:00 (02/20/2023 01:27:05Z)
[2023-02-20T01:26:53.310Z] 02/19/2023 17:27:10-08:00 (02/20/2023 01:27:10Z)
[2023-02-20T01:26:53.311Z] 02/19/2023 17:27:15-08:00 (02/20/2023 01:27:15Z)
[2023-02-20T01:26:53.312Z]
[2023-02-20T01:26:53.323Z] Host started (775ms)
[2023-02-20T01:26:53.324Z] Job host started
[2023-02-20T01:26:54.472Z] {
[2023-02-20T01:26:54.473Z] "ProcessId": 49120,
[2023-02-20T01:26:54.474Z] "RuntimeIdentifier": "win10-x64",
[2023-02-20T01:26:54.475Z] "WorkerVersion": "1.8.0.0",
[2023-02-20T01:26:54.476Z] "ProductVersion": "1.8.0-local202209270007\u002B04ccbd8e45bb9017dc30ff5e1343e893a216e173",
[2023-02-20T01:26:54.477Z] "FrameworkDescription": ".NET 7.0.0",
[2023-02-20T01:26:54.478Z] "OSDescription": "Microsoft Windows 10.0.22621",
[2023-02-20T01:26:54.479Z] "OSArchitecture": "X64",
[2023-02-20T01:26:54.480Z] "CommandLine": "C:\\Users\\swkandhi\\source\\repos\\FunctionTimerTrigger\\FunctionTimerTrigger\\bin\\Debug\\net7.0\\FunctionTimerTrigger.dll --host 127.0.0.1 --port 58647 --workerId e70ddb42-819a-45b2-a17c-dca35392699d --requestId 326e359b-9d29-4105-b2f1-9ef01892ae95 --grpcMaxMessageLength 2147483647"
[2023-02-20T01:26:54.481Z] }
[2023-02-20T01:26:54.493Z] Worker process started and initialized.
[2023-02-20T01:26:55.053Z] Executing 'Functions.FunctionSampleTimer' (Reason='Timer fired at 2023-02-19T17:26:55.0194750-08:00', Id=872452e5-4d71-42ea-a16d-d4790ccf38a2)
[2023-02-20T01:26:57.821Z] Host lock lease acquired by instance ID '00000000000000000000000074E57A56'.
With what you have there for timing (5 * * * * *) I think it means it should run once at the 5 second mark. If you want it to run every 5 seconds you should do this (*/5 * * * * *)
I have observed for every minute and couple of minutes in .NET 7 Isolated Azure Functions with generating the GUID Id, it is working successfully:
Below are my test cases:
For every
1st Run
Next Runs
2 minutes
Yes
Yes
1 minute
Yes
Yes
60 seconds
Yes
Yes
30 Seconds
Yes
No
5 Seconds
Yes
No
For every minute:
For every 2 minutes:
For every 60 Seconds:
For every 5 Seconds:
AFAIK, the shortest interval requires at least 60 seconds minimum in the CRON Expression - the same is mentioned in the articles of Hostinger - Cron Job Tutorial.

How does Serilog calculate Elapsed in Web API?

For Web API in .NET 6, I'm configuring Serilog request logging.
app.UseSerilogRequestLogging(options =>
{
options.MessageTemplate = "Elapsed: {Elapsed:0.0000} ms";
});
How is Elapsed calculated exactly?
Serilog has custom RequestLoggingMiddleware which is injected into pipeline via UseSerilogRequestLogging call (that's why it is important to call this method as early as possible cause otherwise it will not take into account time consumed by components that appear before it in the pipeline, as mentioned in the docs). The implementation is pretty straightforward - sample time at the start of logging middleware, invoke the rest of pipeline, sample time at the end, calculate the difference. Currently calculation looks somewhat like this:
var start = Stopwatch.GetTimestamp();
var Elapsed = GetElapsedMilliseconds(start, Stopwatch.GetTimestamp());
static double GetElapsedMilliseconds(long start, long stop)
{
return (stop - start) * 1000 / (double)Stopwatch.Frequency;
}

How to trigger multiple c# functions in a particular time in specific order using azure function?

I have created two functions which triggered on everyday at specific time (2:00 AM) but both functions triggered independently on specific time in azure function.
I have tried following working example which function1 and function2 triggered everyday at specific time (2:00 AM)
[FunctionName("Function1")]
public static void Method1([TimerTrigger("0 0 2 * * *")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"Function 1: {DateTime.Now}");
}
[FunctionName("Function2")]
public static void Method2([TimerTrigger("0 0 2 * * *")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"Function 2: { DateTime.Now}");
}
but both function triggered independently,
For example First time I executed code, following function order was displayed which is expected result -
Function1: 5/11/2021 02:00:00 AM
Function2: 5/11/2021 02:00:00 AM
but Second time I again execute code than following function order was displayed -
Function2: 5/11/2021 02:00:00 AM
Function1: 5/11/2021 02:00:00 AM
EXPECTED RESULT:-
Both functions should be triggered at 2:00 AM such that function1 triggers first and after that function2 should be trigger as following order -
Function1: 5/11/2021 02:00:00 AM
Function2: 5/11/2021 02:00:00 AM
How to trigger multiple functions in a particular time in specific order using azure function?
Although you set the Function1 at first and set Function2 at second, but they will be triggered independently as you test.
If you want them be triggered in order, why not put two function execution code into one function(execute the first part of code then execute the second part of code) ?
Or another option is modify the cron expression of second function to: 0 1 2 * * *, then it will be trigger at 2:01 AM.

FixedDelayRetry attribute on Azure Function doesn't work properly

Since the release of the latest Microsoft.Azure.WebJobs assembly, it is now possible to setup the retry policy of an Azure Function through an attribute such as [FixedDelayRetry]. I was very interested in this feature since I have a Blob Trigger Function, which retries five times by default when it fails, and I was not interested in the retry policy feature. I wanted the function to not retry when it fails, or only once.
So I have setup this attribute in my function as you can see below, and have indicated an amount of maximum one retry if the function fails :
public class Function1
{
private readonly IGremlinService _gremlinService;
private readonly TelemetryClient _telemetryClient;
public Function1(IGremlinService gremlinService, TelemetryConfiguration telemetryConfiguration)
{
this._gremlinService = gremlinService;
this._telemetryClient = new TelemetryClient(telemetryConfiguration);
}
[FunctionName(nameof(Function1))]
[FixedDelayRetry(1, "00:00:10")]
public async Task Run([BlobTrigger("files/{directory}/{name}.pdf", Connection = "AzureWebJobsStorage")] Stream myBlob, string name, ILogger logger)
{
try
{
//my lengthy code not related to the issue
}
}
}
When I try this code on my testing environment, with the Storage Emulator and my CosmosDb Emulator, it works perfectly fine, the function retries only once when it encounters an exception.
However, when I run my function on the Azure platform, instead of retrying only once when it fails, it retries... nine times. I setup the value of the maximum of retries to 5, and this time, the function retried 25 times. I have the feeling that, instead of retrying the amount of times specified in the attribute, the function multiplies that number to the default retry policy of 5. Below you can see my logs, where the function clearly retried 10 times :
What am I doing wrong ?
The function app retry policy is independent of any retries or resiliency that the trigger provides. The function retry policy will only layer on top of a trigger resilient retry. You can read about retry behaviour at here.
Since Azure Blob has maxDequeueCount set to 5 (read
here) that’s why you see that your message is being retried multiple times.
To achieve the desired result you can define the maxDequeueCount property in host.json to 1.

How to create recurring Hangfire job every 10 minutes for only 24 hours

When I have a new customer signup that does not complete the process, I send them an email with next steps. I need to create a job that runs every 10 minutes for the first 24 hours after signup. After that time, there is another process that takes over. I schedule the job like this:
RecurringJob.AddOrUpdate(customerId, () => new NewCustomerProcess().checkNewCustomerStatus(customerId)), "*/10 * * * *");
If I add a job start time to the job class:
private DateTime _jobstart = DateTime.UtcNow;
Can I inspect that within the job to figure out when 24 hours has passed then remove the job?
RecurringJob.RemoveIfExists(customerId);
Does Hangfire re-instantiate the job class every time it runs?
if i understand your question correctly.
Hangfire does create a new instance of the job class every time.
So if I needed to solve this problem, I would pass in DateTime as a parameter every time the job is created:
RecurringJob.AddOrUpdate(customerId, () => new
NewCustomerProcess().checkNewCustomerStatus(customerId, DateTime.Now.AddDays(2))), "*/10 * * * *");
And then in the checkNewCustomerStatus compare with DateTime.Now
if (DateTime.Now > dateEnqueued)
{
//Job is complete
return;
}

Categories

Resources