Timer trigger in Durable Azure Functions - c#

I have such initial azure function:
[FunctionName("TriggerNoResponseFunction")]
public static async Task Run(
[TimerTrigger("0 */1 * * * *")] TimerInfo info,
[OrchestrationClient] DurableOrchestrationClient starter)
My Orchestrator function:
[FunctionName("NoResponseOrchestrator")]
public static async Task Run(
[OrchestrationTrigger] DurableOrchestrationContext context,
ILogger log)
And an activity trigger:
[FunctionName("NoResponseHandlerFunction")]
public static void Run(
[ActivityTrigger] DbReadOptions readOptions)
But in this case my Activity trigger function gets called a lot of times intead of CRON expression which as i understand should restrict count of callings. What i'm doing wrong. How to achieve durable function to get called only by timer schedule?
Thanks for help in advance

Related

Is it possible that 2 azure functions can get triggered by one eventhub?

Is it possible that 2 azure functions can get triggered by one eventhub? One azure function will write its data to database1 and the other azure function writes its data to database2
[FunctionName("EventToDB1")]
public async System.Threading.Tasks.Task Run([EventHubTrigger("eventhub", Connection = "Debezium")]
EventData[] events, ILogger log)
{
{
[FunctionName("EventToDB2")]
public async System.Threading.Tasks.Task Run([EventHubTrigger("eventhub", Connection = "Debezium")]
EventData[] events, ILogger log)
{
{
answer on the possibility of having 2 azure functions get triggered by one eventhub
Yes that is possible by using different consumer groups. Because you specified the same connection to the Event Hub, being "Debezium", I Assume you want both funtions to process the same message. You have to create a new consumer group and specify the name using the ConsumerGroup property of the EventHubTrigger attribute (The default consumergroup is $Default):
public class EventToDB1
{
[FunctionName("EventToDB1")]
public async System.Threading.Tasks.Task Run(
[EventHubTrigger("eventhub",
Connection = "Debezium",
ConsumerGroup = "CG1")]
EventData[] events, ILogger log)
{
}
}
public class EventToDB2
{
[FunctionName("EventToDB2")]
public async System.Threading.Tasks.Task Run(
[EventHubTrigger("eventhub",
Connection = "Debezium",
ConsumerGroup = "CG2")]
EventData[] events, ILogger log)
{
}
}
Each consumer group receives the same messages from the Event Hub.
I do agree with #peter bons, you need to create two consumer groups for that and you can create two consumers by below process:
You can also use logic apps to work with event hubs.

Azure Function QueueTrigger - Put new items back on queue

I have an Azure Function based on a QueueTrigger. This gets triggered when something appears on the queue, but after I processed this item, I then want to put new items back on the queue.
Is there a way to do this directly from within the Azure Function?
[Function("Batch")]
public async Task Run([QueueTrigger("batch", Connection = "DataQueue")] string data,
FunctionContext context)
{
var model = JsonConvert.DeserializeObject<MyObject>(data);
// 1. process model
// 2. Put items back on queue?
}
You can use output bindings as the following:
[StorageAccount("MyStorageConnectionAppSetting")]
public static class QueueFunctions
{
[FunctionName("QueueOutput")]
[return: Queue("myqueue-items")]
public static string QueueOutput([HttpTrigger] dynamic input, ILogger log)
{
log.LogInformation($"C# function processed: {input.Text}");
return input.Text;
}
}

Unit Tests for timer triggered Azure Function: provide test data

To Unit Testing HTTP triggered Azure Functions with test data is well and often described. For example here:
How to write unit test for azure function v1
Mock data are given in the http request.
But I have a timer triggerd Azure function which reads data from a FTP server. I would like to Unit Test the function with test data.
My function:
[FunctionName("MyTimerTriggeredFunction")]
public static void Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
ServiceClass serviceClass = new ServiceClass(log, context);
List<Order> orderList = serviceClass.getOrdersFromFtp();
...
a running Unit Test function to test the logger, just to show how I started:
public void TestLogger()
{
// https://learn.microsoft.com/de-de/azure/azure-functions/functions-test-a-function
var logger = (ListLogger)TestFactory.CreateLogger(LoggerTypes.List);
MyTimerTriggeredFunction.Run(null, logger, null);
var msg = logger.Logs[0];
bool testString = msg.Contains("C# Timer trigger function executed at");
Assert.IsTrue(testString);
}
serviceClass.getOrdersFromFtp() return a list of objects. I could create this list with mock data in the unit test but how to give it to the timer triggered azure function?
You should move your business logic outside of the azure function and test that, rather than coming up with ways to mock data for a timer-triggered function.
Your code would look something like this:
[FunctionName("MyTimerTriggeredFunction")]
public static void Run([TimerTrigger("0 */2 * * * *")]TimerInfo myTimer, ILogger log, ExecutionContext context)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
ServiceClass serviceClass = new ServiceClass(log, context);
serviceClass.DoWork();
...
class ServiceClass
{
public void DoWork()
{
List<Order> orderList = serviceClass.getOrdersFromFtp();
...

Azure Function V2 Service Bus Message Deferral

I am attempting to convert my v1 function to a v2 function, but I cannot find a replacement for deferring a message.
In V1 of Azure Functions it was a method on the BrokeredMesage called .DeferAsync(). In V2 there is no longer a BrokeredMessage but just a Microsoft.Azure.ServiceBus.Message and this does not contain the method of .DeferAsync().
According to the docs:
The API is BrokeredMessage.Defer or BrokeredMessage.DeferAsync in the .NET Framework client, MessageReceiver.DeferAsync in the .NET Standard client, and mesageReceiver.defer or messageReceiver.deferSync in the Java client.
But how can I get access to the MessageReciever?
Here is an example of my function:
[FunctionName("MyFunction")]
public static void Run([ServiceBusTrigger("topic", "subscription", Connection = "AzureServiceBusPrimary")]Message message, ILogger log)
{
//Code
}
So does anyone know how to defer a V2 Message that is triggered from the Azure Service Bus?
As you mention, the new message receiver offers an async defer method and you can add this to your function by using the following code:
[FunctionName("MyFunction")]
public static async Task Run([ServiceBusTrigger("topic", "subscription", Connection = "AzureServiceBusPrimary")]Message message, string lockToken, MessageReceiver messageReceiver, ILogger log)
{
//Your function logic
await messageReceiver.DeferAsync(lockToken);
}

Can durable functions have multiple triggers?

I have a durable function that is triggered once a day by a Timer Trigger:
[FunctionName("MyDurableFunction")]
public static async Task Run(
[TimerTrigger("0 0 23 * * *", RunOnStartup = false)] TimerInfo myTimer,
[OrchestrationClient] DurableOrchestrationClient starter,
ILogger log)
{
await starter.StartNewAsync("OrchestrationFunction", null);
}
[FunctionName("OrchestrationFunction")]
public static async Task OrchestrationFunction(
[OrchestrationTrigger]DurableOrchestrationContext context,
ILogger log)
{
// do stuff
}
This works fine. For testing purposes I would also like to be able to trigger the durable function via a Http Trigger, so I added this:
[FunctionName("MyDurableFunctionHttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "demo")]HttpRequest req,
[OrchestrationClient] DurableOrchestrationClient starter,
ILogger log)
{
await starter.StartNewAsync("OrchestrationFunction", null);
return new OkObjectResult("blah");
}
Running these locally, including either the http trigger or the timer trigger will trigger the function, but including both in the class means that neither trigger events will occur. Is it possible to have multiple trigger types start an orchestration trigger?
I believe you can only have one trigger type per function but can suggest you write all your logic in to a separate project/assembly and then just reference the assembly and call the entry point via parameters, keeping your function implementation clean and simple and centralising the execution logic in another project (or classes within the same project).
On your code, you should have Orchestrator and Activity functions, so you could write one Activity function to do the work and call it from two orchestrators. The guidance on Durable Functions is to keep the orchestrator clean and simple managing just that - the orchestration, offloading the work to the Activities.
I recommend you look at the durable monitor pattern for your timer based requirement and look at the HTTP APIs for HTTP Triggers.
What you could do is create multiple normal functions, one for each type of trigger. A scheduled trigger, http trigger, blob trigger, or any other supported trigger.
Within that function you can start a new orchestration function. That orchestration function does not require a trigger in itself. You only need the DurableOrchestrationContext.
public static async Task<object> RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context,
ILogger log)
{
// orchestration logic here
}
[FunctionName("Info_HttpStart1")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "starter1")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("Info", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
[FunctionName("Info_HttpStart2")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "starter2")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("Info", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}

Categories