I have an API that allows users to upload documents. To optimize the performance I save the documents to the database in parallel. After uploading has completed I return a list of URI's pointing to the document locations.
Here is what the code looks like:
As you can see in the code above, I combine a Task.WhenAll with a local function. Nothing special.
However when I executed this code under high load, I started to get errors back from the API. A look at the logs, showed the following exception:
Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: chunkLength
at System.Text.StringBuilder.ToString()
at Microsoft.AspNetCore.Mvc.Routing.UrlHelperBase.GenerateUrl(String protocol, String host, String path)
at Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper.Action(UrlActionContext urlActionContext)
at Microsoft.AspNetCore.Mvc.UrlHelperExtensions.ActionLink(IUrlHelper helper, String action, String controller, Object values, String protocol, String host, String fragment)
at DocumentStorage.API.Controllers.DocumentsController.<UploadDocuments>g__UploadDocument|4_0(IFormFile document) in D:\b\3\_work\129\s\DocumentStorage\DocumentStorage.API\Controllers\DocumentsController.cs:line 53
at DocumentStorage.API.Controllers.DocumentsController.UploadDocuments(List`1 documents)
It turns out that the Url.ActionLink()
method is using a StringBuilder
behind the scenes. And the StringBuilder
is not thread-safe which brings us into trouble when we start using it in combination with the TPL.
To fix the threading issue I moved the url generation logic outside the parallel execution path: