RDS Proxyのピン留めを許容できるケースについて考えてみる

ピン留め=絶対に回避すべきもの ではなく、場合によっては許容しても問題ないという話です
2022.07.06

MADグループ@大阪の岩田です。以前こんな記事を書きました。

このブログではピン留めを回避するための設定値について紹介していますが、今日はピン留めを回避する必要がないケースについて考えてみます。

RDS Proxyを使うメリット

RDS Proxyを使うとどのようなメリットがあるのでしょうか?私は以下の3点だと考えています。

  1. RDSのフェイルオーバーに対する対障害性が向上する
  2. クライアントの要求を処理できるRDSの接続オブジェクトが空いていない場合、即座にエラーを返却するのではなく接続が空くまで待機できるようになる ※これはユースケース次第ではデメリットにもなり得ます
  3. RDS <-> RDS Proxy間の1つの接続を複数のクライアントで共有利用できるようになり、論理的に接続を多重化できる

ピン留めが発生すると3つ目の論理的な接続の多重化というメリットが失われます。このあたりを詳しく見ていきましょう。例としてclient1,2という2つのクライアントがRDS Proxyを経由してRDSにクエリを発行するシーケンスを考えてみます。以下シーケンスはベージュ色で「接続中」を水色で「クエリ実行中」を表現しています。

  • まずclient1がRDS Proxyに接続します。この時点ではRDS ProxyとRDSの接続はOPENしていないこともあります
  • 続いてclient1がRDS Proxyに対してクエリを発行すると、RDS Proxyはコネクションプール内にチェックアウト可能なRDSとの接続が存在するかをチェックします。チェックアウト可能なRDSとの接続が存在しない場合はRDS ProxyからRDSに対して新しく接続をOPENし、コネクションプールに追加します
  • RDS Proxyはclient1向けにRDSとの接続をチェックアウトし、RDSにクエリを発行します
  • クエリの実行が完了するとRDS Proxyからclient1にクエリの実行結果を返却し、client1向けにチェックアウトしたRDSとの接続を解放します。この時RDS Proxy <-> RDSの接続はCLOSEされずにコネクションプール内に残り続けます
  • 続いてclient2がRDS Proxyに対してクエリを発行します。RDS Proxyのコネクションプールには先程client1のクエリを処理したRDSとの接続がプーリングされているため、このRDSとの接続を再利用してRDSにクエリを発行します

このシーケンスではclient1,2それぞれのクエリ実行タイミングがズレているため、「RDS Proxyとの接続」を1つ利用するだけで全てのクエリを処理できます。クライアント側からするとサーバー(RDS Proxy)との間に2つのセッションが存在しますが、実際にRDS側で使用する接続オブジェクトは1つだけで済んでいます。

ピン留めによる悪影響

ではピン留めが発生するとどうなるでしょうか?先程のシーケンスを少し修正し、クエリ実行時にピン留めが発生したパターンを見ていきます。先程利用したベージュ色、水色に加えてピンク色で「ピン留め中」を表現しています。

  • client1がRDS Proxyにクエリを発行するまでの流れは先程と同様です
  • RDS Proxyはclient1からの要求がピン留めの条件を満たしたことを検知するとRDSとの接続をclient1に対してピン留めします
  • クエリの実行が完了するとRDS Proxyからclient1にクエリの実行結果を返却します。RDSとの接続はピン留めされているので、そのままclient1向けにチェックアウトしっぱなしになります
  • 続いてclient2がRDS Proxyに対してクエリを発行します。client1のクエリを処理したRDSとの接続はclient2向けにチェックアウトできないため、他にチェックアウト可能なRDSとの接続が存在するかをチェックします。チェックアウト可能なRDSとの接続が存在しない場合はRDS ProxyからRDSに対して新しく接続をOPENします。以後はclient1からのクエリを処理する流れと同様です

こちらのシーケンスで「RDS」の列を確認するとクエリ実行中を表す水色の枠は小さいものの、RDS Proxyとの接続は2つ消費してしまっています。これがピン留めのデメリットです。

ピン留めを許容してもいいケース

ここまででRDS Proxyのメリットとピン留めによる悪影響が整理できました。RDS Proxyのメリットを説明したシーケンスではクエリの実行にあまり時間を要していませんでしたが、クエリの実行に長い時間が必要な場合はどうなるでしょうか?

  • RDSがclient1から発行されたクエリを処理するまでの流れはこれまでと同様です
  • client1から発行されたクエリの処理が完了していない間にclient2がRDS Proxyに対してクエリを発行します
  • 「RDS Proxyとの接続1」はclient1から発行されたクエリを処理中なので、client2にチェックアウトすることができません。そのためRDS Proxyは他にチェックアウト可能なRDSとの接続が存在するかをチェックします。チェックアウト可能なRDSとの接続が存在しない場合はRDS ProxyからRDSに対して新しく接続をOPENし、コネクションプールに追加します
  • 以後client2のクエリを処理する流れはこれまでのシーケンスと同様です

こちらのシーケンスではピン留めが発生しなくても、RDSとRDS Proxyの接続が2つ必要になります。クエリ実行中を表す水色の枠が2つ同一時間帯に重なっているためです。どうせclient1とclient2で共有できないのであれば、client1のクエリ処理でピン留めが発生しても特に気にする必要は無いでしょう。

説明を簡略化するために処理時間の長い単一のクエリ実行で説明しましたが、これは単一のクエリではなくトランザクションの場合も考え方は同じです。以下のトランザクションを例に考えてみます。

  • トランザクション開始
  • 軽量なクエリ実行
  • (アイドル状態)
  • 軽量なクエリ実行
  • ...色々
  • コミット or ロールバック

アイドル状態の時間帯にRDSはクエリを処理していませんが、トランザクション実行中のため他のクライアントに対して接続をチェックアウトすることはできません。もしチェックアウトを許可してしまうと、トランザクションの独立性が保てなくなってしまいますよね?

ということでクエリやトランザクションの実行が長時間に渡る場合は、無理にピン留めを回避しなくてもピン留めを許容するという選択を取るのも良いでしょう。具体例としては実行時間が長くなりがちな夜間バッチ処理などはピン留めの発生を許容して良いケースが多いのでは無いでしょうか?ただし、注意点としてクエリやトランザクションの実行完了後にはクライアントとRDS Proxyの接続を忘れずにCLOSEして下さい。クエリやトランザクションを処理していない接続がピン留めされてしまうのは無駄なので。

まとめ

RDS Proxyのピン留めを許容できるケースについて考えてみました。ピン留めが許容できるのであれば気兼ねなくPrepared Statementが利用できますし、バッチ処理の処理効率を上げるためにSET WORK_MEM to <デフォルト値より大きめの値>のようにセッションレベルで設定値を調整するといったテクニックも使えます。もし ピン留め=絶対に回避すべきもの と考えてメトリクスとにらめっこしている方がいれば、そのピン留めは許容できないかを改めて考えて見て下さい。もしかするとピン留めを許容しても問題ないかもしれません。