Функция Azure, создающая слишком много соединений с PostgreSQL - PullRequest
8 голосов
/ 10 июля 2019

У меня есть функция Azure Durable, которая взаимодействует с базой данных PostgreSQL, также размещенной в Azure.

База данных PostgreSQL имеет ограничение соединения 50, и, кроме того, моя строка соединения ограничивает размер пула соединений до 40, оставляя место для подключений суперпользователя / администратора.

Тем не менее, при некоторых нагрузках я получаю ошибку

53300: оставшиеся слоты подключения зарезервированы для подключений суперпользователя без репликации

Эта документация от Microsoft казалась актуальной, но не похоже, что я могу сделать статический клиент, и, как упоминается,

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

У меня есть этот метод

private IDbConnection GetConnection()
{
    return new NpgsqlConnection(Environment.GetEnvironmentVariable("PostgresConnectionString"));
}

, и когда я хочу взаимодействовать с PostgreSQL, я делаю вот так

using (var connection = GetConnection())
{
    connection.Open();
    return await connection.QuerySingleAsync<int>(settings.Query().Insert, settings);
}

Итак, я создаю (и уничтожаю) множество NpgsqlConnection объектов, но согласно this , это должно быть хорошо, потому что соединениеОбъединение происходит за кулисами.Но в функциях Azure может быть что-то недействительное.

Я заметил, что у меня много свободных соединений (из pgAdmin): pgAdmin connection graph На основании этого япробовал поиграться с параметрами соединения Npgsql , такими как Connection Idle Lifetime, Timeout и Pooling, но проблема слишком большого количества соединений, кажется, сохраняется в той или иной степени.Кроме того, я попытался ограничить число одновременных функций оркестровщика и функций (см. этот документ ), но это, кажется, частично противоречит цели масштабируемости функций Azure.Это помогает - я получаю меньше ошибок из-за слишком большого числа подключений).Предположительно, если я продолжу тестировать его с меньшими числами, я могу даже устранить его, но, опять же, похоже, что он побеждает точку, и может быть другое решение.

Как я могу использовать PostgreSQL с функциями Azure, не увеличивая до максимумасоединения?

Ответы [ 2 ]

0 голосов
/ 21 июля 2019

У меня нет хорошего решения, но я думаю, что у меня есть объяснение, почему это происходит.

Почему приложение-функция Azure максимизирует соединения?

Несмотря на то, что для размера пула указано ограничение в 40, оно учитывается только в одном экземпляре приложения функции .Обратите внимание, что функциональное приложение может масштабироваться в зависимости от нагрузки.Он может одновременно обрабатывать несколько запросов в одном и том же экземпляре приложения-функции, а также создавать новые экземпляры приложения.Параллельные запросы в одном и том же экземпляре учитывают настройку размера пула.Но в случае нескольких экземпляров каждый экземпляр в конечном итоге использует размер пула 40.

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

Как я могу использовать PostgreSQL с функциями Azure, не увеличивая количество подключений?

К сожалению, приложение-функция не предоставляет встроенного способа сделать это.Обратите внимание, что размер пула соединений управляется не временем выполнения функции, а кодом библиотеки npgsql.Этот библиотечный код, работающий в разных экземплярах, не может общаться друг с другом.

Обратите внимание, что это классическая проблема использования общих ресурсов.У вас есть 50 из этих ресурсов в этом случае.Наиболее эффективным способом поддержки большего количества потребителей было бы сокращение времени, в течение которого каждый потребитель использует ресурс. Существенное сокращение Connection Idle Lifetime, вероятно, является наиболее эффективным способом .Увеличение Timeout помогает уменьшить количество ошибок (и это хороший выбор), но не увеличивает пропускную способность.Это просто сглаживает нагрузку.Сокращение Maximum Pool size также хорошо.

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

Следует отметить, что сокращение Maximum Pool Size не обязательно ограничивает параллелизм вашего приложения.В большинстве случаев это просто уменьшает количество незанятых соединений - за счет - оплаты начального времени установки, когда позднее потребуется установить новое соединение.

Обновление

WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT может быть полезным.Вы можете установить это значение на 5, а размер пула на 8 или подобное.Я бы пошел по этому пути, если уменьшение Maximum Pool Size и Connection Idle Lifetime не помогает.

0 голосов
/ 11 июля 2019

Здесь Dependency Injection может быть действительно полезным. Вы можете создать клиент singleton, и он отлично справится с этой задачей. Если вы хотите узнать больше о сроке службы, вы можете прочитать его здесь в документах

  1. Сначала добавьте этот nuget Microsoft.Azure.Functions.Extensions.DependencyInjection

  2. Теперь добавьте новый класс, как показано ниже, и разрешите ваш клиент.

[assembly: FunctionsStartup(typeof(Kovai.Serverless360.Functions.Startup))]

namespace MyFunction
{
    class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            ResolveDependencies(builder);
        }
    }
    public void ResolveDependencies(IFunctionsHostBuilder builder)
    {
        var conStr = Environment.GetEnvironmentVariable("PostgresConnectionString");
        builder.Services.AddSingleton((s) =>
        {
            return new NpgsqlConnection(conStr);
        }
    }
}

Теперь вы можете легко использовать его из любой функции

public FunctionA
    {
        private readonly NpgsqlConnection _connection;
        public FunctionA(NpgsqlConnection conn)
        {
            _connection = conn;
        }

        public async Task<HttpResponseMessage> Run()
        {
            //do something with your _connection
        }
    }
...