Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

サーバーレスの課題:データベース接続

サーバーレス用のデータベースを設計する際の最大の課題は、リクエストごとの価格設定を収益性の高い方法でサポートするインフラストラクチャを構築することでした。 Upstashはこれを達成したと信じています。製品を発売した後、データベース接続というもう1つの大きな課題があることがわかりました。

ご存知のように、サーバーレス関数は0から無限大までスケーリングします。つまり、関数が大量のトラフィックを受け取ると、クラウドプロバイダーは新しいコンテナー(ラムダ関数)を並行して作成し、バックエンドをスケールアウトします。関数内に新しいデータベース接続を作成すると、データベースの接続制限にすばやく到達できます。

ラムダ関数の外部で接続をキャッシュしようとすると、別の問題が発生します。 AWSがLambda関数をフリーズしても、接続は閉じられません。そのため、多くのアイドル/ゾンビ接続が発生する可能性がありますが、それでも脅威となる可能性があります。

この問題はRedisに固有のものではなく、TCP接続に依存するすべてのデータベース(Mysql、Postgre、MongoDBなど)に当てはまります。サーバーレスコミュニティがserverless-mysqlのようなソリューションを作成していることがわかります。これらはクライアント側のソリューションです。 Upstashとして、サーバー側を実装および保守できるという利点があります。そこで、接続を監視し、アイドル状態の接続を削除することで、問題を軽減することにしました。したがって、ここでのアルゴリズムは次のとおりです。max-concurrent-connectionとして、データベースにはソフト制限とハード制限の2つの制限があります。データベースがソフト制限に達すると、アイドル状態の接続の終了を開始します。ハードリミットに達するまで、新しい接続要求を受け入れ続けます。データベースがハード制限に達すると、新しい接続の拒否を開始します。

接続排除アルゴリズム
if( current_connection_count < SOFT_LIMIT ) {
    ACCEPT_NEW_CONNECTIONS
}

if( current_connection_count > SOFT_LIMIT && current_connection_count < HARD_LIMIT ) {
    ACCEPT_NEW_CONNECTIONS
    START_EVICTING_IDLE_CONNECTIONS
}

if( current_connection_count > HARD_LIMIT ) {
    REJECT_NEW_CONNECTIONS
}

Upstashドキュメントに記載されている最大同時接続制限はソフト制限であることに注意してください。

エフェメラル接続

上記のアルゴリズムを導入した後、すべてのリージョンで拒否された接続の数が大幅に減少しました。しかし、それでもあなたが安全な側になりたいのであれば、あなたはあなたの側でも問題を解決することができます。接続を再利用する代わりに、関数内でRedis接続を開くことができますが、以下のようにRedisを使い終わったらいつでも閉じることができます:

exports.handler = async (event) => {
  const client = new Redis(process.env.REDIS_URL);
  /*
    do stuff with redis
    */
  await client.quit();
  /*
    do other stuff
    */
  return {
    response: "response",
  };
};

上記のコードは、同時接続数を最小限に抑えるのに役立ちます。人々は、新しい接続の遅延オーバーヘッドについて質問します。 Redis接続は非常に軽量であることが知られています。

Redis接続は本当に軽量ですか?

ベンチマークテストを実行して、Redis接続がどれほど軽量かを確認しました。このテストでは、2つのアプローチのレイテンシ数を比較します。

1-一時的な接続:接続を再利用しません。代わりに、コマンドごとに新しい接続を作成し、すぐに接続を閉じます。クライアント作成、ping()、client.quit()のレイテンシーを一緒に記録します。 benchEphemeral()を参照してください 以下のコードセクションのメソッド。

2-接続の再利用:接続を1回作成し、すべてのコマンドで同じ接続を再利用します。ここでは、ping()のレイテンシを記録します 手術。 benchReuse()を参照してください 以下の方法。

async function benchReuse() {
  const client = new Redis(options);
  const hist = hdr.build();
  for (let index = 0; index < total; index++) {
    let start = performance.now() * 1000; // to μs
    client.ping();
    let end = performance.now() * 1000; // to μs
    hist.recordValue(end - start);
    await delay(10);
  }
  client.quit();
  console.log(hist.outputPercentileDistribution(1, 1));
}

async function benchEphemeral() {
  const hist = hdr.build();
  for (let index = 0; index < total; index++) {
    let start = performance.now() * 1000; // to μs
    const client = new Redis(options);
    client.ping();
    client.quit();
    let end = performance.now() * 1000; // to μs
    hist.recordValue(end - start);
    await delay(10);
  }
  console.log(hist.outputPercentileDistribution(1, 1));
}

ベンチマークを自分で実行する場合は、リポジトリを参照してください。

このベンチマークコードは、AWSEU-WEST-1リージョンで2つの異なるセットアップで実行されました。最初のセットアップは、クライアントとデータベースが同じアベイラビリティーゾーンにあるSAMEZONEです。 2番目のセットアップは、クライアントがデータベースとは異なるアベイラビリティーゾーンで実行されるINTERZONEです。データベースサーバーとしてUpstashStandardタイプを使用しました。

新しい接続(一時的なアプローチ)を作成して閉じるオーバーヘッドは、わずか75マイクロ秒(99パーセンタイル)です。オーバーヘッドは、ゾーン間セットアップ(80マイクロ秒)でも非常に似ています。

次に、AWSLambda関数内で同じテストを繰り返すことにしました。結果は異なっていました。特にLambda関数のメモリを低く(128Mb)設定すると、Redis接続のオーバーヘッドが大きくなります。 AWS Lambda関数内で最大6〜7ミリ秒のレイテンシオーバーヘッドが見られました。

Redis接続に関する結論:

  • Redis接続は、適度なCPUパワーを備えたシステムでは非常に軽量です。 t2.microでも。
  • デフォルトのAWSLambda構成でのCPUパワーは非常に低く、Lambda関数の合計実行時間に関してTCP接続のコストが大幅に増加します。
  • デフォルト/最小メモリでLambda関数を使用する場合は、関数の外部にRedis接続をキャッシュすることをお勧めします。
凍結されたコンテナ=>ゾンビ接続

一部のAWSLambdaセットアップでは接続に顕著なオーバーヘッドが発生する可能性があることに気付いた後、reusing connectionについてさらにテストを行うことにしました。 AWSLambdaで。別の問題が検出されました。これは、まだ誰も報告していないエッジケースでした。

これがどのように発生するかについてのタイムラインです:

STEP1-タイマー-0秒: ラムダ関数の外部で接続をキャッシュして、リクエストを送信します。

if (typeof client === "undefined") {
  var client = new Redis("REDIS_URL");
}

module.exports.hello = async (event) => {
  let response = await client.get("foo");
  return { response: response + "-" + time };
};

ステップ2-タイマー-5秒: AWSは短時間でコンテナをフリーズします。

ステップ3-時間-60秒: Upstashには、アイドル状態の接続に対して60秒のタイムアウトがあります。したがって、接続を切断しますが、フリーズしているため、クライアントからACKを取得できません。したがって、サーバー接続は状態FIN_WAIT_2になります。

STEP4-時間-90秒: Upstashサーバーは接続を完全に切断し、FIN_WAIT_2状態を終了します。

STEP5-時間-95秒: クライアントは同じリクエストを送信し、ETIMEDOUT例外を受け取ります。クライアントは接続が開いていると想定しますが、そうではないためです。 🤦🏻🤦🏻🤦🏻

STEP6-時間-396秒: 最後のリクエストから5分後、AWSはコンテナを完全に強制終了します。

STEP7-時間-400秒: コンテナは最初から作成され、初期化ステップがスキップされないため、クライアントは今回も同じリクエストを送信します。新しい接続が作成されます。

上記のように、AWSはコンテナを解凍し、接続を再利用します。しかし、サーバー側からの接続が切断されており、機能がフリーズしたため通信できませんでした。そのため、Upstashがアイドル状態の接続を削除することとAWSがアイドル状態の機能を処理することの間には同期の問題があります。したがって、AWSが関数を終了した後にのみアイドル状態の接続を切断しても、問題は発生しません。

AWSがアイドル機能を300秒で終了すると仮定して、Upstash接続タイムアウトを310秒に変更しました。この変更後、問題は解消されました。ここでの問題は、AWSがアイドル機能を終了するときに透過的ではないことです。したがって、テストを続行し、問題が再び発生するかどうかを検出する必要があります。

この問題は、serverless-mysqlライブラリで見られる問題と非常によく似ています。コメントでは、ETIMEDOUT例外が発生したときにリクエストを再試行することが提案されています。ただし、再試行には2つの欠点があります。最初に、実際のネットワークの問題で処理およびタイムアウトされた可能性のある書き込み要求を再試行できます。 2番目の問題は、失敗したリクエストの余分なレイテンシです。

GraphQLも役立ちます

コネクションレス型APIを使用してコネクションレス型APIを使用するための接続の問題を取り除く1つの方法。 Upstashは、Redisプロトコルに加えてGraphQLAPIをサポートしています。 GraphQLはHTTPベースであるため、接続制限の問題はありません。サポートされているコマンドについては、ドキュメントを確認してください。 GraphQL APIには、Redisプロトコルよりもレイテンシのオーバーヘッド(約5ミリ秒)があることに注意してください。

結論

サーバーレスアプリケーションのスムーズなエクスペリエンスのために、Upstashデータベースをカスタマイズします。新しいサーバー側アルゴリズムは、AWSLambdaが大量に作成する非アクティブな接続を削除します。 Lambda関数内でRedisクライアントを開閉することで接続数を最小限に抑えることができますが、関数のメモリが1GB未満の場合、レイテンシのオーバーヘッドが発生する可能性があります。

結論として、サーバーレスのユースケースに関する推奨事項:

  • ユースケースがレイテンシーに敏感な場合(たとえば、6ミリ秒が大きい場合)、Redisクライアントを再利用します。
  • 非常に多くの同時クライアント(1000を超える)が発生した場合は、Redisクライアントを再利用してください。
  • ユースケースがレイテンシに敏感でない場合は、関数内でRedisクライアントを開いたり閉じたりします。
  • 関数に1GB以上のメモリがある場合は、関数内のRedisクライアントを開閉します。

TwitterまたはDiscordでフィードバックをお寄せください。


  1. サーバーレスデータベース間のレイテンシーの比較:DynamoDBとFaunaDBとUpstash

    この記事では、一般的なWebユースケースについて、3つのサーバーレスデータベースDynamoDB、FaunaDB、Upstash(Redis)のレイテンシーを比較します。 サンプルのニュースWebサイトを作成し、Webサイトへのリクエストごとにデータベース関連のレイテンシーを記録しています。ウェブサイトとソースコードを確認してください。 7001のNYTimesの記事を各データベースに挿入しました。記事はNewYorkTimes Archive API(2021年1月のすべての記事)から収集されます。私は各記事をランダムに採点しました。各ページリクエストで、Worldの下の上位10件の記事

  2. LinkedIn で接続収集を停止する方法

    LinkedIn はときどきイライラすることがあります。あなたとのつながりが希薄なランダムな人々が、あなたのネットワークに参加したいと思っています。聞いたこともない人が接続を要求しています。あなたのネットワークに参加したいだけで、何かを売りたいと思っている人もいますが、それは通常、彼らが提供しているサービスです. 彼らはしつこく、対応するようにあなたに嫌がらせをします。一部のコネクションは、あなたに仕事を提供したくないリクルーターですが、あなたの LinkedIn プロフィールに気づき、あなたのビジネスで採用するために候補者をあなたに提供したいと考えています。 LinkedIn はビジネスの