views:

291

answers:

6

I have a .NET Windows Service that has a timer with interval set to 10 seconds. Every 10 seconds, it queries the database for any work to be done, picks up top 3 work items and processes them. The time taken to process each work item would depend on the user. It might range from a few seconds to several minutes.

I feel that 10 seconds is too short. I picked a low interval as the user is waiting for the process to be completed (they see a progress bar in the front end) and the faster the service picks up the work, the better. Also, the # of work items (three) was picked in random.

I do have "lock(this)" in my code to prevent one thread from stepping over the other. Is a short duration of 10 seconds okay in production environments?

EDIT: Also, does it make sense that I am picking only 3 items every time to be processed.

A: 

I suppose that would depend on how complicated your query is. If it's a simple query that returns instantly and is only being run by a single service every 10 seconds, I doubt it'll be an issue, but I know nothing of your environment so don't hold me to that.

If you're looking for another solution altogether, you could look at a messaging architecture instead of polling.

Basically in this scenario, whatever system you have that's adding the work to the database in the first place can fire off a message to your service to let it know that there's work to do. Then your service doesn't need to poll anything, it just sits and waits for a notification that it needs to check the database. Alternatively, you could include the work to do in the message itself and let the service process it and store what needs to be stored in the database itself.

If you're interested in messaging, take a look into NServiceBus. It's basically a wrapper around MSMQ and will open your mind to a whole new world of ideas concerning inter-connected systems.

The options are endless really.

Chris Stavropoulos
A problem with sending the work as part of the message is that, if the service is down, the to-do task could potentially be lost. Also, if the processing service is not able to receive a message, it may never be aware that there is work to do, resulting in tasks that are never processed. Placing a message queue in a central persistent repository - like, say, a database =), prevents these issues.
David Lively
On the contrary. If you work with an actual messaging service like MSMQ, it's guaranteed delivery. If the service is down or not listening or whatever the case may be, it'll pick it up the next opportunity it can.
Chris Stavropoulos
A: 

I would use the interval of 10 seconds.
Do another check when a user process has completed (to prevent an unnecessary 10sec wait)

You could look into the SQLDependency class to catch database events. That would be even better, but a bit more difficult to implement.

For more information on the SQLDependecy look here:
http://www.codeproject.com/KB/database/chatter.aspx

Zyphrax
+1  A: 

I am currently handling something like this at work, with the main timer fetching from a stored procedure the work to be done each second (1000 ms).

Though, the fetching is not limited to X items (like 3 in your example).
I just add as many Threads as needed and let the framework handle them correctly.

I tried to decouple all the code in the way i don't need to use any lock.

I save to db all the log informations and I have another timer (30 minutes interval) eventually fetching the errors and mail them to me.

You should have no problems with a 1 second timer, assuming you are giving all the work to another thread, different from the timer one.

Alex Bagnolini
+1 Nice solution.
Walter
A: 

It's a hard questions to answer; your production environment could have a hardware configuration bigger than your comparative environment, so your queue can be processed faster than current values.

If you don't want to guess, try to implement a queue based winservice. That service will read a MSMQ queue and take a work item at time and process it. So you just need to ask that queue if you still got work items to process and you can do that in any frequency you want, let's say, 1 second.

Most important thing is to make sure you don't go get more work items while you still have work to do..

BTW, you should to read this: Why lock(this) is bad.

Rubens Farias
+1  A: 

Your service interval should be independent of the UI. One way to accomplish this is to have a flag for each work item in your database which marks them as "new", "in-process", or "done." Have your service pick up one item at a time and mark it as "in-process", do whatever it needs, and mark it as "done" when complete.

Your UI, then, would just poll that table and update the user with the status of each recently-modified row as necessary.

In this way, your timer interval becomes irrelevant for any consideration other than UI latency. You are also able to run multiple service threads without sacrificing efficiency (three threads could handle three items if you're processing them one at a time, instead of a single thread grabbing three), there's little or no opportunity for a race condition, etc.

Also, be sure to disable your timer while processing, or you run the risk of making your process method reentrant (interrupts itself).

David Lively
This is exactly how I've implemented this. "In this way, your timer interval becomes irrelevant for any consideration other than UI latency." --> True, but I do not want to make the user wait for too long.If each thread picks up 1 item, the thinking is that it will take minimum of 30 seconds (in the worst case) to process the 3rd item, so the 3rd user waits so long. It's possible that all 3 requests can be completed in 10 seconds, in that case, would'nt it have been better if a single thread picked 3 items?
I disagree that it's better to do 3 at a time. Ideally, your fetch/update time should be insignificant compared to the processing time required. I avoid arbitrarily picking X items unless there is a significant performance benefit. In the above example, the UI can show "in-process" items with some sort of spinning "working" indicator, further improving the UI experience. If you have 3 threads on 3 cores, each item will finish nearly simultaneously. Hope this helps.
David Lively
+1  A: 

...Also, does it make sense that I am picking only 3 items every time to be processed...

Having a hard number of items to pick up could be dangerous. You have the potential to have items enter the queue faster than they can be processed. Consider what would happen if more than three items get enqueued every ten seconds. You would begin to see that it takes longer and longer for an item to get processed.

Either spawning additional threads or using an event-driven approach might be better.

Steve Elmer