question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

6.0 regression: pooled uninitialized context leaks state across usages

See original GitHub issue

Hi. After updating EF Core to 6.0.0-preview I have started to encounter an issue that happens around 50% of the time its code is run.

The error is: The instance of entity type 'LoginSession' cannot be tracked because another instance with the same key value for {'AccessToken'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

And the code is:

private async Task UpdateSessionAndSaveChanges(LoginSession session)
{
    var dbEntry = this.mainContext.Entry(session);
    if (dbEntry.State == EntityState.Detached)
        dbEntry.State = EntityState.Modified; // this line throws the exception
    await this.mainContext.SaveChangesAsync();

    dbEntry.State = EntityState.Detached;
}

Explanation: LoginSession is only ever fetched once per run and it’s always fetched with .AsNoTracking(). One LoginSession is fetched on every server call for server endpoints that are decorated with a custom attribute, that fetches the session from a database (via EF Core) and verifies it.

When the client app is resumed or booted, the server session’s last access time is modified and UpdateSessionAndSaveChanges is called.

UpdateSessionAndSaveChanges sets the entity’s state to Modified if it’s Detached (which it should be) and saves changes, so the last access time in the session entity is updated.

This has worked flawlessly until I updated EF Core to any of the 6.0.0-preview releases (I’m currently using preview4), but now around 50% of the times it’s run I get the following error:

The instance of entity type 'LoginSession' cannot be tracked because another instance with the same key value for {'AccessToken'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

There should only be one instance tracked as it’s only ever fetched via AsNoTracking, and should only be done once per server call (where all the database contexts and controllers etc. are defined with a ‘scoped’ dependency injection scope).

Full stack trace:

System.InvalidOperationException:
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry.set_State (Microsoft.EntityFrameworkCore, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at MyApp.MobileAppService.Services.AccountService+<UpdateSessionAndSaveChanges>d__26.MoveNext (MyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullMyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: /Users/myname/Projects/myproject/MyApp.MobileAppService/Services/AccountService.csMyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: 309)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at MyApp.MobileAppService.Services.AccountService+<UpdateLoginSessionAccessTime>d__8.MoveNext (MyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullMyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: /Users/myname/Projects/myproject/MyApp.MobileAppService/Services/AccountService.csMyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: 67)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at MyApp.MobileAppService.Controllers.AccountController+<RefreshSession>d__13.MoveNext (MyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullMyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: /Users/myname/Projects/myproject/MyApp.MobileAppService/Controllers/AccountController.csMyApp.MobileAppService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null: 107)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at lambda_method290 (Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+AwaitableResultExecutor+<Execute>d__0.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker+<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker+<<InvokeNextActionFilterAsync>g__Awaited|10_0>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker+<<InvokeInnerFilterAsync>g__Awaited|13_0>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker+<<InvokeNextExceptionFilterAsync>g__Awaited|25_0>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker+<<InvokeFilterPipelineAsync>g__Awaited|19_0>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker+<<InvokeAsync>g__Logged|17_1>d.MoveNext (Microsoft.AspNetCore.Mvc.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware+<<Invoke>g__AwaitRequestTask|6_0>d.MoveNext (Microsoft.AspNetCore.Routing, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Private.CoreLib, Version=5.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__9.MoveNext (Microsoft.AspNetCore.Diagnostics, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)

Additional information: Server is deployed to Azure and is running Azure SQL, so the SQL Server database provider is used. Server project uses .NET 5. I have not seen this issue with the SQLite provider, but this is not conclusive.

Any pointers greatly appreciated! Thank you.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
rojicommented, Jun 13, 2021

Thanks for the good repro @Executor-Cheng and for the bug analysis - everything seems to be correct. See more minimal repro below (this isn’t actually non-deterministic/random).

Minimal repro
await using ServiceProvider services = new ServiceCollection()
    .AddDbContextPool<SimpleDbContext>(options => options.UseInMemoryDatabase("poc"), 1)
    .BuildServiceProvider();

DbContext ctx;
using (var scope = services.CreateScope())
using (var ctx1 = scope.ServiceProvider.GetRequiredService<SimpleDbContext>())
{
    await ctx1.DisposeAsync();
    ctx = ctx1;
}

using (var scope = services.CreateScope())
using (var ctx2 = scope.ServiceProvider.GetRequiredService<SimpleDbContext>())
{
    Console.WriteLine(ReferenceEquals(ctx, ctx2));
    ctx2.SimpleEntitys.Add(new SimpleEntity());
}

using (var scope = services.CreateScope())
using (var ctx3 = scope.ServiceProvider.GetRequiredService<SimpleDbContext>())
{
    Console.WriteLine(ReferenceEquals(ctx, ctx3));
    Console.WriteLine(ctx3.ChangeTracker.Entries().Count()); // Should be zero
}

public class SimpleEntity
{
    public int Id { get; set; }
}

public class SimpleDbContext : DbContext
{
    public DbSet<SimpleEntity> SimpleEntitys { get; set; } = null!;
    public SimpleDbContext(DbContextOptions<SimpleDbContext> options) : base(options) {}
}
2reactions
Executor-Chengcommented, Jun 12, 2021

@AndriySvyryd Yes.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

public class SimpleEntity
{
    public int Id { get; set; }
}

public class SimpleDbContext : DbContext
{
    public DbSet<SimpleEntity> SimpleEntitys { get; set; } = null!;

    public SimpleDbContext(DbContextOptions<SimpleDbContext> options) : base(options)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<SimpleEntity>(entity =>
        {
            entity.HasKey(p => p.Id);
        });
    }
}

public static class Program
{
    static async Task Main()
    {
        while (true)
        {
            await using ServiceProvider services = new ServiceCollection()
            .AddDbContextPool<SimpleDbContext>(options => options.UseInMemoryDatabase("poc"), 1)
            .BuildServiceProvider();
            IServiceScope scope1 = services.CreateScope(),
                          scope2 = services.CreateScope(),
                          scope3 = services.CreateScope();
            try
            {
                scope1.ServiceProvider.GetRequiredService<SimpleDbContext>();
                // Do nothing, so DbContext._contextServices would not be initialized.
            }
            finally
            {
                await ((IAsyncDisposable)scope1).DisposeAsync();
            }
            try
            {
                var ctx1 = scope2.ServiceProvider.GetRequiredService<SimpleDbContext>();
                ctx1.Attach(new SimpleEntity { Id = 1 }); // Attach an entity.
            }
            finally
            {
                await ((IAsyncDisposable)scope2).DisposeAsync();
            }
            // After scope2 was disposed, the SimpleDbContext should be returned to the pool, so the ChangeTracker should be reset.
            try
            {
                var ctx2 = scope3.ServiceProvider.GetRequiredService<SimpleDbContext>();
                if (ctx2.ChangeTracker.Entries().Any())
                {
                    Debugger.Break();
                }
            }
            finally
            {
                await ((IAsyncDisposable)scope3).DisposeAsync();
            }
        }
    }
}

I guess when the IServiceScope is disposing, it will call DbContext.ResetStateAsync, but DbContext._contextServices is still null. https://github.com/dotnet/efcore/blob/7105f84c5d6aeeaf79e0fbd2ab4307ffa24b7327/src/EFCore/DbContext.cs#L824-L825 So the local field services will be null, then StateManager and others will not be added to resettableServices.

Read more comments on GitHub >

github_iconTop Results From Across the Web

4. Memcheck: a memory error detector
Using a size value of 0 with realloc. Memory leaks. Problems like these can be difficult to find by other means, often remaining...
Read more >
Memory leaks - CVE - Search Results
CVE-2022-45887, An issue was discovered in the Linux kernel through 6.0.9. drivers/media/usb/ttusb-dec/ttusb_dec.c has a memory leak because of the lack of ...
Read more >
Detecting memory leaks through introspective dynamic ...
This paper expands staleness-based memory leak detection by presenting a machine learning-based framework. The proposed framework is based on an idea that ...
Read more >
Search Results - CVE
CVE-2023-28967, A Use of Uninitialized Resource vulnerability in the Border Gateway Protocol (BGP) software of Juniper Networks Junos OS and Junos OS ...
Read more >
Debugging Valgrind Errors
Valgrind actually detects a number of memory errors other than leaks, such as uses or accesses of uninitialized memory.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found