Reputation: 941
I'm developing an API where my endpoint accepts an ID and then performs a heavy operation to compose the result (generates a PDF).
It's possible that I will get the same request for the same resource multiple times in a short timeframe, so I'd like some sort of work queue that keeps track of IDs, and the first request would initiate the actual work and the following requests would (first check if it's already in the pipeline then) simply wait until the work is done and return the same result.
First I thought maybe a ConcurrentDictionary
would be useful, but I don't know how to handle the wait until the work is done part. I also looked at ObservableCollection
, but I'm not sure about its safety (without putting too much effort into making it safe manually).
Upvotes: 0
Views: 280
Reputation: 1038710
If you want to be able to scale this I would recommend an alternative architecture using a message queue (such as RabbitMQ). Your API will simply publish the messages to the queue and then you could have a Windows Service that will consume the messages and process them. The 2 applications could of course share a common data store in order to synchronize the information.
Upvotes: 2
Reputation: 10396
I would probably use a normal dictionary with a lock around it and Task
's inside, which complete when the result for the work item has been calculated.
That means doing each part of work is represented as a Task<WorkResult>
.
If a new request comes in you can do something like:
lock (currentWork)
{
Task<WorkResult> workItem = null;
if (currentWork.TryGetResult(workId, out workItem))
{
// Work is already in progress. Reuse that work item
return workItem; // The caller can await that
}
// Create a new workItem
workItem = FunctionThatStartsWork(); // Could also be a Task.Run(...) thing
// Store it in the map
currentWork[workId] = workItem;
// Return it to the caller so that he can await it
return workItem;
}
Now there's a question left on who removes the item from the dictionary. One approach could be to attach a continuation on creation of the work task that will remove it from the map once it's finished.
workItem.ContinueWith(wi => {
lock (currentWork) {
currentWork.Remove(workId);
}
});
Upvotes: 0