benaadams / Ben.BlockingDetector

Blocking Detection for ASP.NET Core
Apache License 2.0
731 stars 35 forks source link

Async DB call is reported as being a blocking call. #4

Closed mattwoberts closed 6 years ago

mattwoberts commented 6 years ago

Hi,

Firstly, thanks for this little tool :) I'm just playing around with it, while I try to figure some weird occasional errors with my asp.net core web app. I've noticed that a simple test, with an async call to Dapper's QueryAsync is tripping the warning - which seems slightly odd. Here's my test action method:

        public async Task<IActionResult> Index(int id)
        {
            var connection = new SqlConnection(_connString);
            var users = await connection.QueryAsync<User>("Select * from users where id = @id", new {id});
            var user = users.FirstOrDefault();

            return new ContentResult() {Content = "Hello, " + user.Fullname};
        }

Is that a false positive, or have I missed something? Here's the stack:

[16:44:31 WRN] Blocking method has been invoked and blocked, this can lead to threadpool starvation.
   at System.Threading.WaitHandle.WaitAny(WaitHandle[] waitHandles, Int32 millisecondsTimeout, Boolean exitContext)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.OpenAsync(CancellationToken cancellationToken)

Cheers :)

benaadams commented 6 years ago

Glad you like!

Might want to raise a bug in corefx as TryGetConnection is a blocking call:

private bool TryGetConnection(DbConnection owningObject, uint waitForMultipleObjectsTimeout, bool allowCreate, bool onlyOneCheckConnection, DbConnectionOptions userOptions, out DbConnectionInternal connection)
{
    // ...
   waitResult = WaitHandle.WaitAny(_waitHandles.GetHandles(allowCreate), unchecked((int)waitForMultipleObjectsTimeout));

Should probably use a async SemphoreSlim.WaitAsync for the async path or something similar.

Also link issue to here, so can follow progress

benaadams commented 6 years ago

Can you try with the latest package and see if its still reported? https://www.nuget.org/packages/Ben.BlockingDetector/0.0.3

mattwoberts commented 6 years ago

Aha - all gone now - now I'm left with actual valid messages about my razor views not using the async versions of the helpers 👍