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

Accessing DbSet.Local with context pooling causes a memory leak #29164

Closed
Lawlzee opened this issue Sep 20, 2022 · 0 comments
Closed

Accessing DbSet.Local with context pooling causes a memory leak #29164

Lawlzee opened this issue Sep 20, 2022 · 0 comments
Labels
area-dbcontext closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Milestone

Comments

@Lawlzee
Copy link

Lawlzee commented Sep 20, 2022

LocalViewListener._viewActions are not reset when a context is returned to the context pool. This means, every time DbSet<T>.Local is called, a "viewAction" is inserted LocalViewListener._viewActions, which causes a memory leak.

.NET Interactive dib script to reproduce the leak

#!csharp

#r "nuget: Microsoft.EntityFrameworkCore.SqlServer, 6.0.9"

#!csharp

using Microsoft.EntityFrameworkCore;

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

public class BlogDbContext : DbContext
{
    public BlogDbContext(DbContextOptions<BlogDbContext> options) : base(options)
    {
    }

    public DbSet<Blog> Blogs { get; private set; }
}

#!csharp

using Microsoft.Extensions.DependencyInjection;

IServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddDbContextPool<BlogDbContext>(opt => opt.UseSqlServer("connectionString"));

var serviceProvider = serviceCollection.BuildServiceProvider();
var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();

#!csharp

using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;

var _viewActionsField = typeof(LocalViewListener).GetField("_viewActions", BindingFlags.NonPublic | BindingFlags.Instance);

for (int i = 0; i < 1000; i++)
{
    using var scope = scopeFactory.CreateScope();
    var context = scope.ServiceProvider.GetRequiredService<BlogDbContext>();

    //Force the creation a value in LocalViewListener._viewActions
    var _ = context.Blogs.Local;

    ILocalViewListener localViewListener = context.Blogs.GetService<ILocalViewListener>();
    var _viewActions = (List<Action<InternalEntityEntry, EntityState>>)_viewActionsField.GetValue(localViewListener);

    if (_viewActions.Count > 1)
    {
        return "Memory leak!";
    }
}
@ajcvickers ajcvickers self-assigned this Sep 21, 2022
@ajcvickers ajcvickers added this to the Backlog milestone Sep 22, 2022
ajcvickers added a commit that referenced this issue Sep 22, 2022
Fixes #29164

This change treats the LocalView as one of the resettable services and resets it rather than severing when the context is returned to the pool. This fixes a memory leak because the view was previously recreated and re-registered with _the same StateManager_ every time the context was re-used, these registrations were never cleared. But since the StateManager is reused, the local views can also be reused, meaning that the registration is retained rather than repeated across uses of the context instance.
@ajcvickers ajcvickers modified the milestones: Backlog, 7.0.0 Sep 22, 2022
@ajcvickers ajcvickers added Servicing-consider closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. and removed consider-for-current-release labels Sep 22, 2022
ajcvickers added a commit that referenced this issue Sep 22, 2022
Fixes #29164

This change treats the LocalView as one of the resettable services and resets it rather than severing when the context is returned to the pool. This fixes a memory leak because the view was previously recreated and re-registered with _the same StateManager_ every time the context was re-used, these registrations were never cleared. But since the StateManager is reused, the local views can also be reused, meaning that the registration is retained rather than repeated across uses of the context instance.
@ajcvickers ajcvickers modified the milestones: 7.0.0, 7.0.0-rc2 Sep 22, 2022
@Lawlzee Lawlzee closed this as completed Sep 22, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0-rc2, 7.0.0 Nov 5, 2022
@ajcvickers ajcvickers removed their assignment Aug 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-dbcontext closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Projects
None yet
Development

No branches or pull requests

2 participants