web-dev-qa-db-ja.com

PostgreSQLへの接続が多すぎるAzure関数

PostgreSQLデータベースとやり取りするAzure Durable Functionがあり、これも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 Functionsについて何かがあるかもしれません。

(pgAdminからの)アイドル接続が多くなることに気づきました: pgAdmin connection graph それに基づいて、Connection Idle LifetimeTimeoutPoolingなどの Npgsql接続パラメーター をいじってみました、しかし、接続が多すぎるという問題はある程度持続するようです。さらに、同時オーケストレーターとアクティビティ関数の数を制限しようとしましたが( このドキュメント を参照)、Azure Functionsのスケーラブルな目的が部分的に無効になっているようです。それは役立ちます-私はあまりにも多くの接続エラーを受け取りません)。おそらく、より低い数値でテストを続けると、それを排除することさえできるかもしれませんが、やはり、それは要点を打ち負かしているようで、別の解決策があるかもしれません。

接続を最大化せずにAzure FunctionsでPostgreSQLを使用するにはどうすればよいですか?

9
Scotty H

良い解決策はありませんが、なぜこれが起こるのかについての説明はあると思います。

Azure Function Appが接続を使い果たすのはなぜですか?

プールサイズに40の制限を指定しても、これは、関数appの1つのインスタンスでのみ適用されます。関数アプリは、負荷に基づいてスケールアウトできることに注意してください。同じ機能アプリインスタンスで複数のリクエストを同時に処理できるほか、アプリの新しいインスタンスを作成することもできます。同じインスタンスでの同時リクエストは、プールサイズの設定に従います。ただし、複数のインスタンスの場合、各インスタンスは最終的に40のプールサイズを使用します。

インスタンス全体ではなく単一のインスタンス内でのみスロットルするため、耐久性のある関数の同時実行スロットルでもこの​​問題は解決しません。

接続を最大化せずにAzure FunctionsでPostgreSQLを使用するにはどうすればよいですか?

残念ながら、関数アプリはこれを行うネイティブの方法を提供していません。接続プールのサイズは関数ランタイムではなく、npgsqlのライブラリコードによって管理されることに注意してください。異なるインスタンスで実行されているこのライブラリコードは、互いに通信できません。

これは、共有リソースを使用する際の典型的な問題です。この場合、これらのリソースは50個あります。より多くの消費者をサポートする最も効果的な方法は、各消費者がリソースを使用する時間を短縮することです。 Connection Idle Lifetime実質的にはおそらく最も効果的な方法ですTimeoutを増やすとエラーを減らすのに役立ちます(これは適切な選択です)が、スループットは向上しません。負荷を滑らかにするだけです。削減Maximum Pool sizeも良いです。

共有リソースのロックの観点から考えてください。最小限の時間でロックを取得する必要があります。接続が開かれると、合計50の接続の1つがロックされます。一般に、SQLライブラリはプーリングを行い、接続を開いたままにして、新しい接続ごとに必要な初期セットアップ時間を節約します。ただし、これにより同時実行性が制限される場合は、アイドル状態の接続をできるだけ早く強制終了するのが最善です。アプリの単一インスタンスでは、最大プールサイズに達すると、ライブラリがこれを自動的に実行します。ただし、複数のインスタンスでは、別のインスタンスの接続を強制終了することはできません。

注意すべきことの1つは、Maximum Pool Sizeは、必ずしもアプリの同時実行性を制限するものではありません。ほとんどの場合、それはidle接続の数を減らすだけです-代償として-後で新しい接続を確立する必要があるときに初期セットアップ時間を支払います。

更新

WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT が役立つ場合があります。これを5に設定し、プールサイズを8または同様に設定できます。 Maximum Pool SizeおよびConnection Idle Lifetimeは役に立ちません。

2
Turbo

静的HttpClientの使用例を次に示します。これは、接続を明示的に管理する必要がなく、クライアントに許可するために考慮すべきものです。

public static class PeriodicHealthCheckFunction
{
    private static HttpClient _httpClient = new HttpClient();

    [FunctionName("PeriodicHealthCheckFunction")]
    public static async Task Run(
        [TimerTrigger("0 */5 * * * *")]TimerInfo healthCheckTimer,
        ILogger log)
    {
        string status = await _httpClient.GetStringAsync("https://localhost:5001/healthcheck");

        log.LogInformation($"Health check performed at: {DateTime.UtcNow} | Status: {status}");
    }
}

これが_Dependency Injection_が非常に役立つ場所です。 singletonクライアントを作成すると、完全に機能します。サービスの有効期間について詳しく知りたい場合は、ここ docs で読むことができます。

  1. 最初にこのナゲットを追加します_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
        }
    }
_
0
HariHaran