My process is as follows:
This all works well with sequential processing but I'm concerned that a long-running task could block the queue for all other users (and this would be an issue for the web app).
I considered the BlockingCollection in .NET to hold the items in memory and then process them but I cannot guarantee that a row from the UserQueue table won't get put into that collection more than once (due to the non-unique nature of BlockingCollection) unless I use a database flag (BeingProcessed = true for example). I'm not keen on a database flag because if my service was stopped for any reason it could leave unprocessed items in the table with the BeingProcessed = true.
Is there a more standard approach to this that I am missing or should I consider Quartz.net or similar?
The basic trick is to use a test-and-set with a date, rather than just a simple boolean. Here's how you do that.
Let's say that your UserQueue table is super simple. Something like this, at the moment:
create table UserQueue (id integer primary key, description varchar not null)
So far, so good. But we want to safely grab a task and do something with it.
To start, let's alter the schema slightly:
create table UserQueue (id integer primary key, description varchar not null,
dtLocked datetime null)
Now, we simply follow a straight-forward procedure:
select * from UserQueue limit 1NOW() where it is currently null via e.g. update UserQueue set dtLocked = NOW() where id = @id and dtLocked is nullBecause we're now using a datetime for the lock, we can clean out dead tasks on a regular basis via simple update statement that deletes locks older than some amount of time—say, five minutes.
As a bonus, this design lets you safely process multiple tasks at once, so you can eliminate any chance of a user task blocking by simply firing up more threads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With