Background:
I have a service which queries the db for historical results in batches. The batches are based on a start time and an end time. The data between all the batches is mutually exclusive, so for a given set of batches, they can be run in any order. Some batches can take more time to execute than others. If any batch fails then the entire process is aborted/stopped and the error is logged. When returning back data to the client, the data from all the batches need to be combined.
Problem:
Return the data to the client as fast as possible.
Initial solutions:
Initially I was executing the batches in order synchronously. This was not taking advantage of the fact that the data is mutually exclusive. After initial use, it was found that this load method was taking too long. After some debugging, I found the main cause for slowness was the execution time of the sql queries. So the next solution was to try to execute each batch asynchronously using BeginExecuteReader()
and EndExecuteReader()
. After calling all batches asynchronously, the service would wait in a while loop and poll every 300ms to check if any of the queries had completed. If it was, then it would be read.
int batchCount = 0, resultCount = 0;
List<AsyncDataResult> asyncCalls = new List<AsyncDataResult>();
while (true)
{
if (asyncCalls.Count == 0)
{
break;
}
if ((asyncCalls[resultCount]).AsyncResult.IsCompleted)
{
ReadAsyncResultsFromDb(asyncCalls[resultCount]);
asyncCalls.RemoveAt(resultCount);
}
resultCount++;
if (resultCount >= asyncCalls.Count)
{
resultCount = 0;
Thread.Sleep(300);
}
}
The above approach decreased the loading time for large data sets, but for very small data sets (but across many batches), the poll is actually adding to the loading delay.
Question:
- How do I execute the sql asynchronously, but not do polling?
- Start reading each batch as soon as it completes?
Update:
Sorry about this, but I forgot to add the part where I need to return in the same method that is calling the asynchronous calls. The reason for this is that the dataset that I need to populate is passed in as a parameter to this method. Using the IAsyncCallback
from the begin reader would require me to change the entire class. I was hoping that I wouldn't have to do that.