Scenario
My project has a need to periodically scan the database and notify administrators for particular issues. For example:
- Any errors in the logs since last update?
- Do values match expected value from external source?
- New clients?
My nodes (plain old windows services) are currently using Azure Service Bus QueueClient for other distributed tasks, so my original thought was to do something with this same component. However, this proved pretty challenging (see below).
From brainstorming I've come up with the following possible options:
Azure ServiceBus QueueClient
Description: Upon receiving a task, the message handler will enqueue a new message set for X in the future.
For example:
var client = QueueClient.Create("DetectErrors");
var options = /* ... */
client.OnMessage((message) => {
client.Send(new BrokeredMessage() {
ScheduledEnqueueTimeUtc = DateTime.Today.AddDays(1)
});
if (DetectStuffInDb())
{
SendEmail();
}
}, options)
Issue: How to fire that 1st message? This needs to fire when there is no pending message and only fire a single message (not one per node as might be the case if all nodes start the same time).
Azure QueueClient + WebJob
Description: Use an Azure Webjob to hit an endpoint on my WebApi, which will enqueue a new message. (a slight derivation of this would be to use Azure Automation + Runbook to enqueue a message)
Issue: Seems odd to make so many 'hops' to handle each task. [WebJob] -> [WebApi] -> [Queue] -> [Node]
. Additionally, this would make development\testing a little difficult since the WebJob may not have access to a localhost WebApi.
Task.Delay + DB
Description: Use Task.Delay to space out recurring tasks on each distributed node & verify with the DB if another node is currently handling the task. Something like
var id = Cmd("SELECT [Id] FROM [T]");
while (true)
{
var newId = Guid.NewGuid();
Cmd("UPDATE [T] SET [Id] = '<newId>' WHERE [Id] = '<id>'");
id = Cmd("SELECT [Id] FROM [T]");
if (newId == id)
{
if (DetectStuffInDb())
{
SendEmail();
}
}
await Task.Delay(x);
}
Issues: Requires a lot of querying of the database by all the nodes.
Question
- Any suggestions to how to best handle this scenario?
- Is there a better way to handle this scenario that doesn't require timed\recurring tasks?