I have a timer-triggered function setup like this
public class PBARCronTrigger
{
private readonly eReserveFunctions _settings;
public PBARCronTrigger(IOptions<eReserveFunctions> settings)
{
_settings = settings.Value;
}
[FunctionName("PBARCronTrigger")]
public async Task Run([TimerTrigger("%PBARCron%")] TimerInfo myTimer, ILogger log)
{
log.LogInformation($"PBARCronTrigger function executing at: {DateTime.Now}");
using (var client = new HttpClient())
and I have the app setting for PBARCron set to every 5 minutes:
but the trigger is not triggering. I connect to live logs and nothing happens. It keeps on saying "No new trace in the past x min(s)"
Your cron expression doesn't look right to me. Checking it in an evaluator even states that it's non-standard and may not work in every environment.
I think what you want is 0-55/5 * * * *, or more simply, */5 * * * *.
Related
I would like to pass the azure sql input binding where my sql command is a select statement with a parameter. This does not work. It keeps telling me the 'complete' is not a parameter. What am I doing wrong or is what I'm trying to do impossible with sql input binding? When I have a simple statement like select top(10) id, status from Dispatch - it works. Can I not pass a string to the parameters?
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer,
[Sql("select top(10)id, status from Dispatch where status = #critstatus; ",
CommandType = System.Data.CommandType.Text,
Parameters = "#critstatus= {'complete'}",
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable dispatch, ILogger log)
or
[FunctionName("Function1")]
public async Task Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer,
[Sql("select top(10)id, status from Dispatch where status = \''complete\'' ",
CommandType = System.Data.CommandType.Text,
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable dispatch, ILogger log)
According to this MSFT Documentation, Your SQL Query is not bindable as input to the Timer trigger as we have tested in our local environment.
Supported Input Bindings for the Azure Function Timer Trigger:
To fetch the data by giving input parameters for every 2 min (Timer Trigger) in Azure Function, we can call the SQL Query String inside the Run Method Code.
Function 1.cs:
public void Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
string connString = "Server=<yourservername>,1433;Initial Catalog=samplesqldb0808;Persist Security Info=False;User ID=<username>;Password=<password>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
//Query to fetch data
string queryString = "SELECT TOP 2 Id, Status FROM [dbo].[Dispatch] WHERE Status = 'Complete';";
//Connection
using (SqlConnection connection = new SqlConnection(connString))
{
SqlCommand command = new SqlCommand(queryString, connection);
command.Parameters.AddWithValue("#Status", "Complete");
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
log.LogInformation(String.Format("{0}, {1}", reader["Id"], reader["Status"]));
Console.WriteLine(String.Format("{0}, {1}",
reader["Id"], reader["Status"]));// etc
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
}
Created Sample data in the database with below SQL Query:
CREATE TABLE dbo.Dispatch (
[Id] int primary key,
[Status] nvarchar(200) not null
)
INSERT INTO Dispatch (Id, Status)
VALUES
(571, 'Pending'), (572, 'Complete'),
(573, 'InProgress'), (598, 'Complete'),
(593, 'Complete'),(581, 'Complete'),
(597, 'InProgress'), (596, 'Pending');
Result:
I could see the results in the console for both of the methods Logging and Console WriteLine:
I was not able to get the parameter to work so for now I was able to get the timer to work with the azure sql input binding.
public async Task Run([TimerTrigger("0 */2 * * * *")] TimerInfo myTimer,
[Sql("select top(10)id, status, arrivaldate from Dispatch where
status = \'in progress\';" ,
CommandType = System.Data.CommandType.Text,
ConnectionStringSetting = "SqlConnectionString")]
IAsyncEnumerable<Dispatch> dispatch, ILogger log)
{
IAsyncEnumerator<Dispatch> enumerator =
dispatch.GetAsyncEnumerator();
var dispatchList = new List<FindDispatch>();
while (await enumerator.MoveNextAsync())
{
dispatchList.Add(enumerator.Current);
}
await enumerator.DisposeAsync();
I've improved my code a bit, moving the timer to app settings and using %% variable in the timer definition of the function.
I have a function that I want to run every night at 1am. But for debugging I really want it to fire as fast as possible after startup. I solved this by setting the pattern to 5 * * * * * timer in app setting locally, and 0 0 1 * * * in the app config in Azure.
But I don't want to have it actually run every 5 sec, I just want it to fire as fast as possible while debugging. So I've added a counter variable. Enabling only the first run to move forward:
private static int count = 0;
[Disable]
[FunctionName("FetchProjects")]
async public static void Run([TimerTrigger("%FetchProjectsTimer%")]TimerInfo myTimer, TraceWriter log)
{
#if DEBUG
if (count != 0) return;
count++;
#endif
How can I improve this if possible? The count isn't thread safe for one, and I want to remove the #if DEBUG check if possible
It's not clear what you're trying to achieve. You just want to test the logic of your time trigger? What about unit test it (just the logic).
I usually leave my Azure functions as thin as possible and unit test the business logic. Here's how you can do it:
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
FooService.DoFoo();
}
[FunctionName("Function2")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
FooService.DoFoo();
return new OkObjectResult($"Success");
}
}
//extracted logic which I will be tested by Nunit
public static class FooService
{
public static void DoFoo()
{
//some logic in here
}
}
We have to write multiple triggers. I was hoping to create separate functions, based on the trigger types. So if I need 5 timer triggers, that will run at different times, I would create one Timer trigger function class and name the functions like [TimerTrigger1], [TimerTrigger2], [TimerTrigger3] ... and so forth. After I added the code I am not sure if I can do that anymore.
Can someone suggest how I can go about adding multiple triggers? I can't have two Run functions under one class.
public static class TimerTrigger
{
[FunctionName("InsertTimerTrigger1")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
// Do task 1
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("InsertTimerTrigger2")]
public static void Run([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log)
{
//Do Task 2
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
You can create multiple functions in Single Class. You can change Run Method name.
public static class Function1
{
[FunctionName("Function1")]
public static void Method1([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("Function2")]
public static void Method2([TimerTrigger("0 */3 * * * *")]TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
But I will recommend, Create multiple functions will help you (5 in your case).
If you are using common business logic, you can put in a common class and inject in all function.
You can independently Enable/Disable/Delete function from FunctionApp Instance.
You can monitor each function independently (from Function Monitor section)
You can choose any name for the methods. (Naming it as "Run" is not a requirement.)
public static class TimerTrigger
{
[FunctionName("InsertTimerTrigger1")]
public static void InsertTimerTrigger1([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
// Do task 1
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
[FunctionName("InsertTimerTrigger2")]
public static void InsertTimerTrigger2([TimerTrigger("0 */15 * * * *")]TimerInfo myTimer, ILogger log)
{
//Do Task 2
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
I am looking at this example to run a durable function Activity after a set timeout.
https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-eternal-orchestrations
This will allow my function activity to perform processing of data, then wait exactly 1 hour before it attempts to load again. This will continue to run forever. Perfect.
However, when publishing the Function to Azure, I don't want to have to manually invoke/start the function via the associated HTTP Trigger. I just want the durable function to kickoff automatically and start processing.
Is this possible? If not, what is a suggested work around?
Thanks!
As discussed in the comments, one way of doing this would be to add a new Task in your Release pipeline.
Here is what I understood of your setup from your question:
[FunctionName("ClientFunction")]
public static async Task<HttpResponseMessage> OnHttpTriggerAsync([HttpTrigger(AuthorizationLevel.Anonymous, "post")]
HttpRequestMessage request, [OrchestrationClient] DurableOrchestrationClient starter, ILogger logger)
{
// Triggers the orchestrator.
string instanceId = await starter.StartNewAsync("OrchestratorFunction", null);
return new HttpResponseMessage(HttpStatusCode.OK);
}
[FunctionName("OrchestratorFunction")]
public static async Task DoOrchestrationThingsAsync([OrchestrationTrigger] DurableOrchestrationContext context, ILogger logger)
{
DateTime deadline = context.CurrentUtcDateTime.Add(TimeSpan.FromHours(1));
await context.CreateTimer(deadline, CancellationToken.None);
// Triggers some yout activity.
await context.CallActivityAsync("ActivityFunction", null);
}
[FunctionName("ActivityFunction")]
public static Task DoAnAwesomeActivity([ActivityTrigger] DurableActivityContext context)
{
}
Now, every time you deploy a new version of the Function App, you need the orchestrator to be run. However, I do not think it can be started by itself.
What I propose is to have a simple bash script (using curl or something else) that would call the ClientFunction at the appropriate URL.
On top of that, one of the nice things of this solution is that you could make the deployment fail if the Azure Function does not respond.
This seems to be working too.
[FunctionName("AutoStart")]
public static async Task Run([TimerTrigger("*/5 * * * * *", RunOnStartup = true, UseMonitor = false)]TimerInfo myStartTimer,
[DurableClient] IDurableClient orchestrationClient, ILogger log)
{
string instanceId = await orchestrationClient.StartNewAsync("Start_Orchestrator", null);
}
I don't know if there are hidden problems with this, but I'm experimenting now with having a TimerTrigger that runs on startup and also once a day at midnight (or whatever schedule you want). That TimerTrigger will search the list of instances for any running instances of this orchestration, terminate them, then start a new one.
private const string MyOrchestrationName = "MyOrchestration";
[FunctionName("MyOrchestration_Trigger")]
public async Task MyOrchestrationr_Trigger(
[TimerTrigger("0 0 0 * * *", RunOnStartup = true)] TimerInfo timer,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log,
CancellationToken cancellationToken)
{
// Get all the instances currently running that have a status of Pending, Running, ContinuedAsNew
var instances = await starter.ListInstancesAsync(new OrchestrationStatusQueryCondition()
{
ShowInput = false,
RuntimeStatus = new List<OrchestrationRuntimeStatus>() { OrchestrationRuntimeStatus.Suspended, OrchestrationRuntimeStatus.Pending, OrchestrationRuntimeStatus.Running, OrchestrationRuntimeStatus.ContinuedAsNew }
}, cancellationToken);
// Find any instances of the current orchestration that are running.
var myInstances = instances.DurableOrchestrationState.Where(inst => inst.Name == MyOrchestrationName);
List<Task> terminateTasks = new List<Task>();
foreach (var instance in myInstances )
{
// Delete any instances that are currently running.
terminateTasks.Add(starter.TerminateAsync(instance.InstanceId, $"Restarting eternal orchestration"));
}
await Task.WhenAll(terminateTasks);
// Start the new task now that other instances have been terminated.
string instanceId = await starter.StartNewAsync(MyOrchestrationName, null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
}
I think at least for my purposes this will be safe. Any activities that are running when you terminate will still run to completion (which is what I want in my case), so you would just kill it and restart it on a schedule.
I am using Azure Functions with Attributes to define functionality.
public static class PostPublishTimerTrigger
{
[FunctionName("PostPublishTimerTrigger")]
public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer,
TraceWriter log,
[Queue("post-published")] ICollector<string> postPublishedQueue)
{
// Additional code here
}
}
Is there a way to pull the Schedule 0 */5 * * * * for a configuration setting, be it with Configuration Manager or Environment Variables?
Thanks!
Yes, you can do
[TimerTrigger("%schedule%")]
and then add a setting called schedule with value 0 */5 * * * *