I'm working on a service which is responsible for logging requests sent to our service. The service is working offline ( is being fired and forget ).
We are saving the requests to different databases based on some input parameter(product id). We don't want to save to the database every time someone do a request - we would rather like to build some "batch" to be inserted and execute InsertMany
every N
amount of time ( let's say 10 seconds ). I've started implementing that and now I'm struggling about 2 things:
- Do i need to use
ConcurrentDictionary
? It seems like i would achieve the same with normal Dictionary - If answer to above is "no, in your case there are no benefits from
ConcurrentDictionary
" - is there a way to re-write my code to "properly" useConcurrentDictionary
so i can avoid using lock and ensure thatAddOrUpdate
won't have "collisions" with clearing the batch ?
Let me paste the snippet and explain further:
// dictionary where key is ProductId and value is a list of items to insert to that product database
ConcurrentDictionary<string, List<QuoteDetails>> _productDetails;
public SaverService(StatelessServiceContext context)
: base(context)
{
_productDetails = new ConcurrentDictionary<string, List<QuoteDetails>>();
}
// this function will be fired and forgotten by the external service
public async Task SaveRecentRequest(RequestOptions requestData, Response responseData)
{
await Task.Run(() => {
foreach (var token in requestData.ProductAccessTokens)
{
// this function will extract the specific product request ( one request can contain multiple products )
var details = SplitQuoteByProduct(requestData, responseData, token);
_productDetails.AddOrUpdate(token, new List<QuoteDetails>() { details }, (productId, list) =>
{
list.Add(details);
return list;
});
}
});
}
// this function will be executed by a timer every N amount of time
public void SaveRequestsToDatabase()
{
lock (_productDetails)
{
foreach (var item in _productDetails)
{
// copy curent items and start a task which will process them
SaveProductRequests(item.Key, item.Value.ToList());
// clear curent items
item.Value.Clear();
}
}
}
public async Task SaveProductRequests(string productId, List<QuoteDetails> productRequests)
{
// save received items to database
/// ...
}
My main concern is that without lock following scenario occurs:
SaveRequestsToDatabase
is fired - and is starting to process the data- Just before calling
item.Value.Clear();
inSaveRequestsToDatabase
function, the external service fires anotherSaveRecentRequest
function which executesAddOrUpdate
with the same key - which will add request to the collection SaveRequestsToDatabase
is finishing and therefore clearing the collection - but initially object added by 2. was not in the collection so was not processed