2

There is a method which is responsible for returning a single GitCommit object based on its hash from our git commit cache database table. We use Entity Framework Core, and when I've upgraded the nuget packages from 2.2.6 to 3.1.3 I've realized a strange performance issue. Beginning with the second call of the method with 2.2.6 the condition _dbContext.GitCommitCache.Local.Any() took 0-1ms to perform, on 3.1.3 is takes usually around 150-220ms. I'm running this method around 9 to 10 thousand times and its a huge performance bottleneck. Is there anything related that's changed under the hood? Does this have any trivial solutions to keep the original performance?

    public async Task<GitCommit> GetCommitAsync(string hash)
    {
        try
        {
            await _semaphore.WaitAsync();
            if (!_dbContext.GitCommitCache.Local.Any())
            {
                await _dbContext.GitCommitCache.LoadAsync();
            }
            var cacheEntry = await _dbContext.GitCommitCache.FindAsync(hash);

            if (cacheEntry != null)
            {
                return cacheEntry;
            }

            var commit = await GetCommitDataAsync(hash);

            try
            {
                _dbContext.GitCommitCache.Add(commit);
                await _dbContext.SaveChangesAsync();
            }
            catch (DbUpdateException)
            {
                _dbContext.Entry(commit).State = EntityState.Detached;
            }

            return commit;
        }
        finally
        {
            _semaphore.Release();
        }
    }
Gábor Almádi
  • 125
  • 2
  • 6
  • it's hard to believe that your query was taking 0-1 ms to be executed. the following quote is taken from MSDN book "It can take ~100ms to open a new connection to a database." I'm not saying that there is no reason/answer to your question, but i am just showing you this information that i read about a few days ago from a MSDN book. – Cata Hotea Apr 07 '20 at 06:54
  • As I mentioned the 0-1ms response time is resulted after the second run, first run is obviously slow. – Gábor Almádi Apr 07 '20 at 08:29
  • @CataHotea There's no db connection involved in calling `Local`. – Gert Arnold Apr 08 '20 at 12:06

1 Answers1

2

In issue #14001, i.e. EF core 3.0.0, it was added that DetectChanges is called when accessing Local:

public override LocalView<TEntity> Local
{
    get
    {
        CheckKey();

        if (_context.ChangeTracker.AutoDetectChangesEnabled)
        {
            _context.ChangeTracker.DetectChanges();
        }

        return _localView ??= new LocalView<TEntity>(this);
    }
}

DetectChanges is a complex and therefore relatively slow method. Fortunately, as you see, if you disable AutoDetectChangesEnabled temporarily, just for calling Local, you will be up to speed again.

I think it's safe do that, assuming that there are no undetected changes in _dbContext when you enter the method.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291