Утечка памяти в ASP.NET Core EF-Core Sqlite - PullRequest
0 голосов
/ 18 октября 2019

Окей, Dokey, так что я решил, что прыгну в ASP.Net Core для сервера отчетов. Теперь цель этого состояла в том, чтобы кэшировать и агрегировать отчеты, которые затем можно было бы распространять среди клиентов.

Поэтому, прежде чем я настрою все, чтобы начать выполнять вызовы API для опроса данных, я полагал, что построю строительные леса с использованием CSV,и потому, что это в основном формат данных, в котором они уже находятся.

В природе модели MVC я построил простую модель

public class MyModel {
    public string ComputerName { get; set; }
    public string IPAddress { get; set; }
    public string SMART_Data { get; Set; }
}

Теперь мой простой dbcontext

public class MyContext: DbContext {
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Filename=bigfix.db");
    }

    public DbSet<MyModel> MyDatabase { get; set; }
}

My Setup.cs

public class Startup
{
    public IConfiguration Configuration { get; }
    public IHostingEnvironment Environment;

    public Startup(IConfiguration configuration, IHostingEnvironment env)
    {
        Configuration = configuration;
        Environment = env;

        using(var context = new MyContext())
        {
            context.Database.EnsureCreated();
        }
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddEntityFrameworkSqlite().AddDbContext<MyContext>();
        services.AddHostedService<MyService>();
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
        }

        app.UseMvc();
    }
}

My Service, с очень дерзким чтением CSV

public class MyService : IHostedService, IDisposable
{
    private Timer timer;
    private ILogger<MyService> logger;
    private readonly IServiceScopeFactory scopeFactory;

    public MyService(ILogger<MyService> logger, IServiceScopeFactory scopeFactory)
    {
        this.scopeFactory = scopeFactory;
        this.logger = logger;
    }

    public Task StartAsync(CancellationToken stoppingToken)
    {
        timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken stoppingToken)
    {
        timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void readCsv(StreamReader reader,Action<List<string>> processLine)
    {
        string line;
        List<string> cells = new List<string>();
        StringBuilder sb = new StringBuilder();

        int lineCount = 0;
        while ((line = reader.ReadLine()) != null)
        {
            cells.Clear();

            bool escaped = false;
            char c;
            int cIndex = -1;
            lineCount++;

            for (int index = 0; index < line.Length; index++)
            {
                cIndex++;
                c = line[index];

                if (escaped)
                {
                    if (c == '"')
                    {
                        if (index + 1 < line.Length && line[index + 1] == '"')
                        {
                            index++;
                        }
                        else
                        {
                            escaped = false;
                        }
                    }

                    if (escaped)
                    {
                        sb.Append(c);
                    }
                }
                else
                {
                    switch (c)
                    {
                        case ',':
                            cells.Add(sb.ToString());
                            sb.Clear();
                            cIndex = -1;
                            break;
                        case '"':
                            if (cIndex == 0)
                            {
                                escaped = true;
                                break;
                            }

                            goto default;
                        default:
                            sb.Append(c);
                            break;
                    }
                }
            }

            cells.Add(sb.ToString());
            sb.Clear();
            processLine(cells);
        }
    }

    private async void DoWork(object state)
    {
        using (IServiceScope scope = scopeFactory.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<>();
            context.ChangeTracker.AutoDetectChangesEnabled = false;

            await context.Database.ExecuteSqlCommandAsync("DELETE FROM MyDatabase");
            await context.SaveChangesAsync();

            string path = "MyReport.csv";
            using (StreamReader reader = new StreamReader(path))
            {
                readCsv(reader, async (List<string> cells) =>
                {
                    Models.BigFix r = new Models.BigFix();
                    r.ComputerName = cells[0];
                    r.IPAddress = cells[1];
                    r.SMARTIDData = cells[2];

                    await context.MyDatabase.AddAsync(r);
                });
            }

            await context.SaveChangesAsync();

            GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
            GC.Collect(2, GCCollectionMode.Default, true, true);
        }

    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

Теперь, чтобы заполнить некоторые пробелы, причина, по которой я добавил читатель janky CSV:потому что, когда я использовал CSVHelper Просто прочитал использование CSV с шипами в диапазоне 1GB, и, насколько я могу судить, это из-за LOH.

Итак, я добавил кучу вещей GC, но это неСделать разницу. Затем я добавил построчное чтение CSV и ОГРОМНОЕ использование оперативной памяти прекратилось, но в тот момент, когда я начал вставлять данные в базу данных, они возвращались.

CSV, который я читаю, составляет около 26 тысяч строк. и 5 МБ в размере. Причиной повторной обработки CSV каждые 5 секунд было подтверждение утечки.

Я не знаю, что делать, удаление или повторное добавление данных объемом 5 МБ в SQLite приводит к тому, что использование оперативной памяти возрастает до1GB. Дайте мне знать, что вы думаете.

...