I have a Blazor Interactive Server app running on Azure App Services. It scales out/in with a minimum of 2 instances and it got up to 5 once.
In the app I have a BackgroundService
that I use to send emails, synchronize with an external service, mark events closed when they have hit their end date, etc. It runs once every 5 minutes and when I signal it (changed something I want to get synchronized ASAP).
If there was one instance then this is a piece of cake. I read all the pending items from the DB and act on them and/or update the database record.
But with 2+ instances there's the issue of sending 2 emails. I can set things up so I get a DbUpdateConcurrencyException
when I've sent it twice but by the time I get that it's too late - I've sent 2 emails.
And with Azure spinning up new instances and shutting down old ones to run Windows update, etc. I can't find any way to identify one instance to be the only one running. Plus I need to signal the worker in every instance.
So, is there some way I can create a semaphore or something in the database. Where it then is the only background thread allowed to run for 2 minutes (time limit in case Azure then shuts down that server)? Or some other approach?
Because all the work this worker does is based off the database, if any one instance is processing the background items, it will get all items wanted by all instances. So there's no need to signal the other instances when one instance is done processing.
Update: Based on the comments below, let me add a bit.
That's why this is so tricky. For some of the key items they need to be processed in order, I want to have multiple instances available to run, and I need the ability for all app server instances to be able to kick off the processing.
On the good news side, if an instance is signaled and another instance is running, that works. The second instance can do nothing.
There is a beautiful library that does exactly this - DistributedLock. Works great.