问题
Below is a picture of some profiling with Azure Application Insights. It comes from the execution of that simple request:
public HttpResponseMessage Login(LoginRequest loginRequest)
{
var t = new DependencyTracker();
using (var context = t.TrackDependency(() => new SocialDbContext(), "NewContext"))
{
var user = t.TrackDependency(() => context.Users.AsNoTracking().SingleOrDefault(x => x.Email == loginRequest.Id || x.Name == loginRequest.Id), "GetUser");
if (user == null) return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsUser");
var sid = t.TrackDependency(() => context.IdMappers.AsNoTracking().SingleOrDefault(x => x.Provider == "custom" && x.UserId == user.Id)?.Sid, "GettingSid");
var account = t.TrackDependency(() => context.Accounts.AsNoTracking().SingleOrDefault(a => a.Id == sid), "GettingAccount");
if (account == null) return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsAccount");
var incoming = t.TrackDependency(() => Hash(loginRequest.Password, account.Salt), "Hash");
var isLoginOk = t.TrackDependency(() => SlowEquals(incoming, account.SaltedAndHashedPassword), "SlowEquals");
if (isLoginOk && account.TempPasswordExpiry != null)
{
if (DateTimeOffset.Now.CompareTo(account.TempPasswordExpiry.Value) > 0)
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsPassword");
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.TemporaryPassword), "WrongCredentialsExpiredTempPassword");
}
return isLoginOk
? t.TrackDependency(() => SendToken(user.Id, sid), "SendToken")
: t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.Unauthorized, ApiError.WrongCredentials), "WrongCredentialsPassword");
}
}
There are 3 SingleOrDefault calls. In the profiler results I see these 3 calls.
- The first one takes 3325ms (3017ms waiting for the connection)
- The second one takes 2356ms (2349ms waiting for the connection)
- Then the last one takes 0.28ms
What are these incredibly long wait times for the connection?
What causes them and how to get ride of it?
UPDATE
I may be wrong but I think I know what is happening in a mixture of lazy loading and lock on Metdata from that line as explained in this documentation.
Account will need sid but it locked the metadata. So sid waits. Then sid needs user but it locked the metadata. So user waits. Then user gets executed then sid then account.
Although I would have thought that user == null and .Sid would have forced the execution.
Is there a way to turn lazy loading to test what I'm saying?
UPDATE 2
I just tried with this.Configuration.LazyLoadingEnabled = false; in my context ctor but the problem remains :-(
Note that I can only reproduce the issue every 5 min or so. In between it seems there is some caching as the queries are immediately executed.
UPDATE 3
I replaced the code with one query instead of three:
public HttpResponseMessage Login(LoginRequest loginRequest)
{
var t = new DependencyTracker();
using (var context = t.TrackDependency(() => new SocialDbContext(), "NewContext"))
{
var query = from a in context.Users
join b in context.IdMappers on a.Id equals b.UserId
join c in context.Accounts on b.Sid equals c.Id
where b.Provider == "custom" && (a.Email == loginRequest.Id || a.Name == loginRequest.Id)
select new { Sid = c.Id, c.Salt, c.SaltedAndHashedPassword, c.TempPasswordExpiry, UserId = a.Id };
var account = t.TrackDependency(() => query.SingleOrDefault(), "GettingAccount");
if (account == null) return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsAccount");
var incoming = t.TrackDependency(() => Hash(loginRequest.Password, account.Salt), "Hash");
var isLoginOk = t.TrackDependency(() => SlowEquals(incoming, account.SaltedAndHashedPassword), "SlowEquals");
if (isLoginOk && account.TempPasswordExpiry != null)
{
if (DateTimeOffset.Now.CompareTo(account.TempPasswordExpiry.Value) > 0)
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.WrongCredentials), "WrongCredentialsPassword");
return t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.BadRequest, ApiError.TemporaryPassword), "WrongCredentialsExpiredTempPassword");
}
return isLoginOk
? t.TrackDependency(() => SendToken(account.UserId, account.Sid), "SendToken")
: t.TrackDependency(() => Request.CreateResponse(HttpStatusCode.Unauthorized, ApiError.WrongCredentials), "WrongCredentialsPassword");
}
}
But the issue remains even though now there is only one wait.
But again, it only happens every 5 minutes.
Note: I can't reproduce it locally, it only happens on Azure.
UPDATE 4
The issue remains even when invoking a stored procedure through the context. So I guess it's not related to Query Plan.
What I don't understand is that the issue remains as well even if I call the stored procedure with an SqlCommand hence whithout Entity Framework...
UPDATE 5
This is not an Entity framework issue. The problem is waiting for the sql connection. Whether using EF or using SqlCommand the same slow wait time occurs every 5 minutes. I'll close that issue and open a new one with the appropriate question.
回答1:
This is not an Entity framework issue. The problem is waiting for the sql connection. Whether using EF or using SqlCommand the same slow wait time occurs every 5 minutes.
来源:https://stackoverflow.com/questions/42422941/entity-framework-6-blocked-time-waiting-for-a-connection