Nano Hash - криптовалюты, майнинг, программирование

EF Core и большой трафик приводят к ошибке максимального размера пула

Мы используем ASP.NET Entity Framework Core для запросов к нашей базе данных MSSQL в нашем приложении веб-API. Иногда, когда у нас большой трафик, запрос к БД заканчивается этой ошибкой:

Время ожидания истекло. Время ожидания истекло до получения соединения из пула. Это могло произойти из-за того, что все соединения в пуле использовались и был достигнут максимальный размер пула.

Интересно, верен ли наш шаблон использования DbContext и запросов, или мне не хватает какого-то шаблона использования/удаления, а ошибка вызвана некоторой утечкой памяти (после некоторых исследований, которые я прочитал, я не должен использовать использование, потому что время жизни управляется фреймворком ). Я следую документации...

Моя строка подключения:

"myConnection": "Server=xxx;Database=xxx;user id=xxx;password=xxx;Max Pool Size=200;Timeout=200;"

Мой Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
    .....
    // scoped context            
    services.AddDbContext<MyDbContext>(
            options => options.UseSqlServer(this.Configuration.GetConnectionString("myConnection")));
    }

затем в контроллерах я использовал dbcontext путем внедрения зависимостей:

public class MyController : Controller
   public MyController (MyDbContext context)
    {
        this.Context = context;            
    }

    public ActionResult Get(int id)
    {
        // querying
        return this.Context.tRealty.Where(x=>x.id == id).FirstOrDefault();
    }

Должен ли я использовать что-то вроде:

using (var context = this.Context)
        {
            return this.Context.tRealty.Where(x => x.id == id).FirstOrDefault();
        }

Но я думаю, что это плохой шаблон, когда я использую внедрение зависимостей DbContext.


  • Кажется, вы сами ответили на свой вопрос. 29.09.2016
  • Поэтому я должен использовать using, когда я запрашиваю dbcontext, который добавляется путем внедрения зависимостей... 29.09.2016
  • Контейнер внедрения зависимостей должен позаботиться об удалении контекста, поэтому нет. 29.09.2016

Ответы:


1

Я думаю, что проблема была вызвана сохранением объектов из запросов контекста базы данных в в кеше памяти. У меня был один большой запрос LINQ к контексту базы данных с некоторыми другими подзапросами внутри. Я вызвал FirstOrDefault() в конце основного запроса, но не внутри подзапросов. Контроллер с этим справился, он материализует запросы по умолчанию.

 return this.Context.tRealty.AsNoTracking().Where(
                x => x.Id == id && x.RealtyProcess == RealtyProcess.Visible).Select(
                s => new
                { .....

// subquery
videos = s.TVideo.Where(video => video.RealtyId == id && video.IsPublicOnYouTube).
                        Select(video => video.YouTubeId).ToList()), // missing ToList()
.....
 }).FirstOrDefault();

И была проблема - подзапросы удерживали соединение с контекстом базы данных, когда они сохранялись в в кеше памяти. Когда я реализовал распределенный кеш Redis, сначала он давал сбои из-за каких-то странных ошибок. Это помогает, когда я пишу ToList() или FirstOrDefault() для всех своих подзапросов, потому что распределенному кешу нужны материализованные объекты.

Теперь все мои запросы материализованы в явном виде, и у меня не возникает ошибка максимального размера пула. Так что нужно быть осторожным при сохранении объектов из запросов контекста базы данных в В кэше памяти. Необходимо материализовать все запросы, чтобы не хранить соединение где-то в памяти.

11.10.2016
  • ОЙ, просто нажмите это. Вот SQL-запрос для определения мошенников пула: 08.03.2019

  • 2

    Вы можете установить время жизни DbContext в файле startup.cs, посмотрите, поможет ли это:

        services.AddDbContext<MyDbContext>(options => options
                                           .UseSqlServer(connection), ServiceLifetime.Scoped);
    

    Кроме того, если ваш запрос является простым чтением, вы можете удалить отслеживание, используя .AsNoTracking().

    Еще один способ повысить пропускную способность — предотвратить блокировки, используя блок транзакций с IsolationLevel.ReadUncommitted для простых операций чтения. Вы также можете использовать уровень изоляции Snapshot, который является немного более строгим, если вы не хотите грязного чтения.

    TransactionOptions transactionOptions = new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted};
    using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
    {
      // insert magic here
    }
    

    Изменить. Как упоминал автор вопроса, приведенный выше код невозможен (пока?) в EF Core.

    Обходной путь можно найти здесь используя явную транзакцию:

        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
    
            using (var transaction = connection.BeginTransaction())
            {
               // transaction.Commit();
               // transaction.Rollback();
            }
        }
    

    Я не проверял это.

    Редактировать 2: еще один непроверенный фрагмент, в котором вы можете выполнять команды для установки уровня изоляции:

                    using (var c1= new SqlConnection(connectionString))
                    {
                        c1.Open();
                        // set isolation level
                        Exec(c1, "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
                        Exec(c1, "BEGIN TRANSACTION;");
                        // do your magic here
                    }
    

    С Исполнителем:

            private static void Exec(SqlConnection c, string s)
            {
                using (var m = c.CreateCommand())
                {
                    m.CommandText = s;
                    m.ExecuteNonQuery();
                }
            }
    

    Изменить 3. Согласно этой теме, транзакции будут поддерживаться, начиная с .NET Core версии 1.2. вперед.

    @mukundabrt это отслеживается dotnet/corefx#2949. Обратите внимание, что TransactionScope уже перенесен в .NET Core, но будет доступен только в .NET Core 1.2.

    29.09.2016
  • Время жизни DbContext ограничено по умолчанию (по определению метода AddDbContext)... Использование .AsNoTracking() - хорошая идея, потому что мой API доступен только для чтения, спасибо. Но я думаю, что проблема где-то в плохом закрытии соединений, AsNoTracking должен просто делать запросы быстрее. Я думаю, что в EF Core я не могу использовать TransactionScope, поскольку он пока не поддерживается ссылка 30.09.2016
  • Ваш тайм-аут происходит на уровне соединения или на уровне запроса? Вы также можете попробовать установить свойство Database.CommandTimeout в определенных случаях. 30.09.2016
  • Возникли ошибки, когда я использую какой-либо запрос LINQ. Я установил TimeOut=200 в своей строке подключения, я думаю, что это действительно большое значение для простого получения запроса (наши запросы выполняются быстро, пока не будут использованы все соединения, затем произошла ошибка, и приложение полностью мертво. Мы можем просто перезапустить его.) 30.09.2016
  • Тайм-аут в строке подключения относится к тайм-ауту подключения, а не к команде. Вы можете указать значение CommandTimeout в Context.CommandTimeout перед вашим запросом, если он большой, и восстановить его до значения по умолчанию после. msdn.microsoft .com/en-us/library/ 30.09.2016
  • Я отредактировал свой ответ, используя НЕПРОВЕРЕННЫЙ явный код транзакции из другого ответа SO, вы можете попробовать. 30.09.2016
  • Итак, теперь наш API работает без ошибок максимального размера пула. Я не знаю, есть ли какая-то связь, но мы использовали в Кэширование памяти в нашем приложении для кэширования результатов запросов к базе данных. Теперь мы используем Redis Distributed Кеш и все работает нормально. Так трудно сказать... 10.10.2016
  • Рад, что вам удалось разобраться в своей проблеме. Это странно, можно было бы ожидать, что кэширование памяти не повлияет на пул коллекций. Возможно ли, что при определенных условиях что-то в кеше препятствует удалению контекста БД? Каков был срок действия вашего кеша и повлияло ли его изменение? 10.10.2016
  • Да, я думаю, проблема была с сохранением объектов из запросов контекста базы данных в кэш памяти. Время экспирации составило 2 часа (абсолютное истечение), изменение не влияет. Я опишу свое решение в ответе... 11.10.2016

  • 3

    Я добавляю альтернативный ответ на случай, если кто-то попадет сюда с немного другой основной причиной, как это было в случае с моим приложением .NET Core MVC.

    В моем сценарии приложение выдавало эти ошибки «время ожидания истекло... достигнут максимальный размер пула» из-за смешанного использования async/await и Task.Result в одном и том же контроллере.

    Я сделал это, пытаясь повторно использовать код, вызвав определенный асинхронный метод в моем конструкторе для установки свойства. Поскольку конструкторы не допускают асинхронных вызовов, я был вынужден использовать Task.Result. Однако я использовал async Task<IActionResult> методов для await вызовов базы данных в одном и том же контроллере. Мы обратились в службу поддержки Microsoft, и инженер помог объяснить, почему это происходит:

    Похоже, мы делаем блокирующий вызов асинхронного метода внутри [...] конструктора.

    ...

    Итак, в вызове выделенного выше асинхронного метода что-то пошло не так, из-за чего блокируются все перечисленные выше потоки.

    Глядя на потоки, которые выполняют ту же операцию и заблокированы:

    ...

    85,71% тем заблокировано (174 темы)

    Мы должны избегать смешивания асинхронного и блокирующего кода. Смешанный асинхронный и блокирующий код может привести к взаимоблокировкам, более сложной обработке ошибок и неожиданной блокировке потоков контекста.

    https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

    https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result-in-main-context/

    План Действий

    Пожалуйста, попросите вашу команду приложения пересмотреть код приложения вышеупомянутого метода, чтобы понять, что происходит не так.

    Кроме того, я был бы признателен, если бы вы могли обновить логику своего приложения, чтобы не смешивать асинхронный и блокирующий код. Вы можете использовать await Task вместо Task.Wait или Task.Result.

    Итак, в нашем случае я вытащил Task.Result из конструктора и переместил его в приватный async метод, где мы могли await его использовать. Затем, поскольку я хочу, чтобы задача выполнялась только один раз при каждом использовании контроллера, я сохраняю результат в этом локальном свойстве и запускаю задачу из этого метода, только если значение свойства равно null.

    В свою защиту я ожидал, что компилятор по крайней мере выдаст предупреждение, если смешивание асинхронного и блокирующего кода будет настолько проблематичным. Однако, оглядываясь назад, мне это кажется достаточно очевидным!

    Надеюсь, это поможет кому-то...

    13.03.2019
  • Это действительно хороший момент. Спасибо @kevin-m-lapio за указание на это. У меня такая же проблема. ссылка эта запись в блоге помогла мне понять проблему и исправить ее. 11.02.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..