Проблема с подключением при взаимодействии с функцией SQL от Azure - PullRequest
0 голосов
/ 05 августа 2020

Мы говорим с Azure SQL Db, используя функцию Azure. Мы используем последнюю версию Azure функций, которая является 3.1 вместе с. net ядром 3 и ядром EF.

Недавно мы видели следующее сообщение об ошибке в нашем журнале Microsoft.Data.SqlClient.SqlException (0x80131904): Идентификатор ресурса: 1. Предел запросов для пула elasti c составляет 420 и был достигнут. См. "http://go.microsoft.com/fwlink/?LinkId=267637" для помощи. Также, изучив аналитику приложений, мы обнаружили, что за это время количество подключений достигло примерно 1400.

введите описание изображения здесь

Мы добавляем DbContext к службам, используя следующий оператор в файле запуска.

    builder.Services.AddDbContextPool<OurDbContext>(options =>
    {
        options.UseSqlServer("connectionstring");
    });

Класс OurDbContext имеет следующий конструктор,

public OurDbContext(DbContextOptions<OurDbContext> options)
    : base(options)
{
}

И затем мы внедрить класс OurDbContext в разные репозитории, который использует этот контекст для связи с SQL. Аналогично приведенному ниже:

public class Repo : IRepo
{
  public Repo(OurDbContext ourDbContext)
  {

  }
  
  public async Task AddAsync(Entity entity)
  {
    ourDbContext.AddAsync(entity);
    ourDbContext.SaveChangesAsync()
  }
}

Мы вводим эти репозитории в классы функций и вызываем указанные выше методы, такие как

await _repo.AddAsync()

Видите ли вы какие-либо проблемы в приведенной выше структуре, поскольку количество подключенных соединений было открыто много?

Я считаю, что по какой-то причине контекст / соединение не удаляются после того, как функция завершает выполнение.

Подумывал использовать оператор using для dbContext, чтобы он был удален как только мы закончим выполнение запроса к базе данных. Но тогда я не уверен, можем ли мы использовать AddDbContextPool в классе запуска, поскольку ему нужны параметры. Также к этим параметрам привязан OurDbContext. Возможно, придется использовать метод onConfiguring в классе OurDbContext, который я не знаю, как использовать вместе с AddDbContextPool?

Любые предложения были бы очень полезны. Спасибо!

Обновление 1 Обновлено для получения более подробной информации, связанной с вопросом в комментарии: Вот что имеет мой метод:

public async Task<List<EntityXyz>()
{
    var xyzToBeUpdated = new EntityXyz()
    {
        mapping of few fields;
    }
    
    OurDbContext.Update(xyzToBeUpdated);
    
    var xyzToBeAdded = new EntityXyz
    {
        mapping of few fields;
    }
    
    await OurDbContext.AddRangeAsync(xyzToBeAdded);

    await OurDbContext.SaveChangesAsync();

    xyzToBeAdded.Add(xyzToBeUpdated);
    return xyzToBeAdded;
}

1 Ответ

1 голос
/ 05 августа 2020

Ваши экземпляры DbContext и / или их удаление - это НЕ ваша проблема здесь.

Предел запросов для пула elasti c составляет 420 и был достигнут

A запрос в SQL Azure условия - это однократное выполнение Запрос , не путайте это с сеансами , которые примерно соответствуют вашему соединению DbContext.

  • Я говорю примерно потому, что пул соединений происходит под капотом.

В этой статье все изложено простым языком: https://techcommunity.microsoft.com/t5/sql-server-support/tasks-workers-threads-scheduler-sessions-connections-requests/ba-p/333990

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

Это простой сценарий, с которым можно столкнуться с Azure Функции , Logi c приложения и другие микросервисные архитектуры, особенно если у вас есть рабочие процессы, включающие длинные цепочки сервисов, которые могут вызывать ea ch другой или последовательности, в которых одно входное сообщение генерирует много выходных сообщений.

Вам следует проанализировать поток сообщений через ваше решение, например, сколько экземпляров каждой функции выполняется в то время, когда вы достигаете предела ресурсов - кто виноват? Что заставляет эту функцию так часто вызываться?

Я использую следующий простой запрос, чтобы получить снимок фактических запросов, которые сейчас выполняются. Фактический оператор SQL может быть полезен при отслеживании связанного кода, но появление слишком большого количества или неожиданных хостов может указывать на то, что сценарий масштабирования ios стал безудержным:

SELECT
    SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1,
    ((CASE statement_end_offset
        WHEN -1 THEN DATALENGTH(st.text)
        ELSE QS.statement_end_offset END
            - QS.statement_start_offset)/2) + 1) AS statement_text
       , sess.login_name, sess.login_time, sess.host_name
       , conn.client_net_address
       , QS.session_id, QS.connection_id, QS.command, QS.blocking_session_id, QS.deadlock_priority, QS.estimated_completion_time
     FROM sys.dm_exec_requests AS QS
     -- Join on connections for specifics about how the connection was established
     INNER JOIN sys.dm_exec_connections conn ON conn.connection_id = QS.connection_id
     -- Join on sessions to get the login name
     INNER JOIN sys.dm_exec_sessions sess ON sess.session_id =QS.session_id
     -- parse the SQL text for the running query
     CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST
ORDER BY start_time DESC;

Вы должны всегда видеть этот выполняющийся запрос в списке. Если вы в настоящее время достигли максимума, запустите это несколько раз, пока не зажмете (ваши приложения должны начать выпадать из-за исключений)

Общий сценарий выполнения / разработки ios, который может привести к высокой частоте одновременных запросов:

Без результатов вашего анализа причины подобных проблем можно разделить на следующие общие категории:

Ленивая загрузка контекста базы данных Иногда это просто вызывается DbContext или Data Repos, для которых включена отложенная загрузка. Ленивая загрузка может значительно упростить кодирование, особенно в цепочках входа в систему logi c, потому что вам не нужно заранее знать, какие записи и поля извлекать в память. Однако в системе, в которой много пользователей или много экземпляров приложения, вызывающего одну и ту же базу данных, как это обычно бывает с Azure Функции , простые циклы logi c могут вызывать повторное преобразование данных. -запрашивается с гораздо большей частотой, чем вы ожидаете, особенно если вы используете асинхронную или параллельную обработку.

  • Для архитектуры службы / функции подумайте о Lazy Loading как о Ленивое программирование ... жадная загрузка сократит количество обращений к базе данных до кода, в котором вы ожидаете, что это произойдет.

Длинная цепочка взаимосвязанных асинхронных событий Если ваши функции вызывают другие службы или могут запускать другие функции, которые, в свою очередь, могут запускать другие ... Попытайтесь уменьшить сценарий ios, в котором необходимо вызвать базу данных. Если в конвейере вы видите много одинаковых запросов или знаете, что у каждого вызова функции есть процесс инициализации, который загружает записи или сообщения из базы данных перед обработкой, подумайте об изменении logi c, чтобы первый вызов извлекал данные и передает некоторые или все эти данные на следующий шаг.

  • Помните, что asyn c обработка может запускать несколько процессов одновременно , возможно вы можете уменьшить параллелизм, переместив некоторые logi c на более синхронную обработку.
  • См. следующий пункт о концентраторах событий и очередях ...

Попытка обработки сообщений / телеметрии в реальном времени: Это обычное дело в проектах Интернета вещей, но может возникать во многих местах, мы часто чувствуем себя обязанными выполнить операцию немедленно, когда это потребуется. Если вы действительно получаете сотни законных одновременных запросов к своим функциям, вам следует рассмотреть возможность использования концентраторов событий или очередей , чтобы отделить выполнение ваших функций от их запросов.

  • Простой способ сделать это - изменить текущую функцию так, чтобы она помещала входящий запрос в очередь (или в концентратор, если вам нужно поддерживать более высокие частоты или много уровней параллелизма). Затем вы перемещаете предыдущий logi c в новую функцию, которая работает с триггером Queue или EventHub , эти механизмы триггеров позволяют настроить максимальное количество одновременных процессов и пакет размер обрабатываемых сообщений эффективно регулирует конвейер процессов, чтобы дать вам некоторый контроль над количеством запросов, попадающих в вашу базу данных в любой момент времени.

Пользовательский сценарий или сценарий ведения журнала в реальном времени ios: Если вы реализовали настраиваемые процедуры ведения журнала, которые регистрируют запросы, состояние или исключения в одной и той же базе данных (или базе данных в том же пуле elasti c), то эти операции могут легко выйти за пределы ваших требований, рассмотреть возможность ведения журнала в другой базе данных или использования структура ведения журналов, которая кэширует журналы и периодически сбрасывает их в базу данных, NLog ничем не хуже других, но вы также должны учитывать Azure App Insights, поскольку вы уже интегрируетесь с Azure ресурсами.

  • При нормальной нагрузке вы можете не заметить никаких проблем, однако, если вы агрессивно регистрируете исключения, как только вы начнете нажимать DTU или запрос, ограничивает каждый новый запрос в это время будет регистрироваться исключение, и, что еще хуже, регистрация исключения также может завершиться неудачно и вызвать другой журнал ... (я видел, как это произошло). Это может быстро вызвать экспоненциальные избыточные запросы к базе данных, которые еще больше задушат ваше приложение

Агрессивный мониторинг / опрос: Последний сценарий, который приходит на ум, - это если ваше расширенное приложение имеет информационные панели или отчеты, пользовательский интерфейс или настраиваемые шаблоны очереди или таймера, которые означают, что что-то часто (возможно, по таймеру) выполняет запросы к базе данных. Возможно, это вообще не ваши Функции ... По возможности, этот тип логики c должен быть ограничен, чтобы был единственный экземпляр вашего кода, который выполняет опрос и организует другие операции.

  • Код опроса обычно легко преобразовать в обработку на основе Azure Queue , опрос очереди намного эффективнее опроса базы данных, и не учитывается количество ваших запросов.

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

...