Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thread starvation due to JsonInputFormatter reading request stream synchronously #7606

Closed
azhmur opened this issue Feb 15, 2019 · 4 comments
Closed
Assignees
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates investigate

Comments

@azhmur
Copy link

azhmur commented Feb 15, 2019

Describe the bug

JsonInputFormater use JsonSerializer to read request stream synchronously, thus consuming many threads if many slow requests are coming in the same time. If threadpool was relatively small before that it may not expand in time resulting all thread pool threads are blocked.

JsonInputFormater has workaround with calling EnableBuffering and DrainAsync before passing stream to synchronous part of JsonSerilizer, but in case someone already called EnableBuffering or EnableRewind before that (in our case this is done for request tracing in custom middleware), this workaround doesn't work.

Synchrounous stream read because of calling synchronous JsonFormatter.Deserialize from async method:
https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonInputFormatter.cs#L210

Workaround which fails because Request Stream already seekable (but not drained asynchronously!):
https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/src/Microsoft.AspNetCore.Mvc.NewtonsoftJson/NewtonsoftJsonInputFormatter.cs#L132

As far as I understand NewtonsoftJson is going to deliver Pipe based asynchronous version in future which will remove nascesseity for this workaround.

My supposed fix for this workaround (can provide PR):

        if (!request.Body.CanSeek && !suppressInputFormatterBuffering)
        {
            // JSON.Net does synchronous reads. In order to avoid blocking on the stream, we asynchronously
            // read everything into a buffer, and then seek back to the beginning.
            request.EnableBuffering();
            Debug.Assert(request.Body.CanSeek);
        }

        if (!suppressInputFormatterBuffering)
        {
            await request.Body.DrainAsync(inputFormatterContext.HttpContext.RequestAborted);
            request.Body.Seek(0L, SeekOrigin.Begin);
        }
@pranavkm
Copy link
Contributor

Thanks for your report. We're tracking the work here via #6397.

@pranavkm pranavkm added ✔️ Resolution: Duplicate Resolved as a duplicate of another issue area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates labels Feb 15, 2019
@pranavkm pranavkm reopened this Feb 28, 2019
@pranavkm
Copy link
Contributor

@azhmur the bar to patch 2.1 or 2.2 is pretty high and it's very unlikely we would accept the PR you sent in particular if if there's a reasonable workaround you could use.
In your particular case, are you able to write a middleware that drains the request prior to invoking MVC? Would that would effectively solve your issue?

@pranavkm pranavkm added investigate and removed ✔️ Resolution: Duplicate Resolved as a duplicate of another issue labels Feb 28, 2019
@pranavkm pranavkm self-assigned this Feb 28, 2019
@azhmur
Copy link
Author

azhmur commented Feb 28, 2019

Yes, we have fixed issue within our code. I had created PR, because investigation have taken serious efforts (like all thread starvation bugs do), so I want to help others not to spend their time on debugging similar issues.

@mkArtakMSFT
Copy link
Member

Closing per @pranavkm's response.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates investigate
Projects
None yet
Development

No branches or pull requests

3 participants