10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Lambda+RDS Proxy】RDS Proxy利用時に'?'などのパラメータを用いているSQLである「プリペアドステートメント」を使った場合、ピン留めが発生して悪影響が出た

Last updated at Posted at 2021-06-11

公式ページに出てる中で、躓いたところです。「公式をよく読め」という話ですが、失敗例の一つとして掲載します。

読もう! RDS Proxyでのピン留め!

場合によっては、RDS Proxy は、現在のセッションの外でデータベース接続を再利用しても安全であると判断できません。このような場合、セッションが終了するまで、セッションは同じ接続で維持されます。このフォールバック動作は、ピン留めと呼ばれます。
ステートメントのテキストサイズが 16 KB を超える場合、プロキシはセッションをピン留めします。

プリペアドステートメントの場合、プロキシはセッションをピン留めします。このルールは、プリペアドステートメントで SQL テキストを使用するか、バイナリプロトコルを使用するかに関係なく、適用されます。

明示的な MySQL ステートメントである LOCK TABLE、LOCK TABLES、または FLUSH TABLES WITH READ LOCK が原因でプロキシによるセッションのピン留めが発生します。

ユーザー変数またはシステム変数 (例外あり) を設定した場合、プロキシはセッションをピン留めします。この状況によって接続の再利用が制限されすぎる場合は、SET オペレーションでピン留めを発生させないように選択できます。SessionPinningFilters プロパティを設定する方法については、「RDS Proxy の作成」を参照してください。

一時テーブルを作成した場合、プロキシはセッションをピン留めします。これにより、トランザクションの境界に関係なく、一時テーブルの内容がセッション全体で保持されます。

MySQL 関数 ROW_COUNT、FOUND_ROWS、および LAST_INSERT_ID を呼び出すと、ピン留めが発生する場合があります。

これらの関数によりピン留めが発生する正確な状況は、MySQL 5.6 および MySQL 5.7 と互換性のある Aurora MySQL バージョン間で異なる場合があります。

実際に起きた障害

Lambda+RDS Proxyを試してみて、実際に上手く行ったのでそのまま少人数で利用してみた。
ただある期間からたまーにDB接続失敗が出現することがあった。

報告を受けてRDSを確認してみるとdbconnectionが一定数のまま、CPU使用率が跳ね上がったまま維持されている。
ログを見ると

The client session was pinned to the database connection 
for the remainder of the session. The proxy can't reuse this connection until the session ends. 
Reason: A protocol-level prepared statement was detected.

といった警告とともにセッションがピン留め?され、connectionがずっと続いていることが分かった。

原因

RDS Proxyによるピン留めが発生し、connectionの肥大化によってDBの許容量オーバーを起こしていた。

プリペアドステートメントの場合、プロキシはセッションをピン留めします。このルールは、プリペアドステートメントで SQL テキストを使用するか、バイナリプロトコルを使用するかに関係なく、適用されます。

プリペアドステートメントを使用していたのが原因。

パラメータ設定の例を次に示します。conはアクティブな接続を表します。

   PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
                                     SET SALARY = ? WHERE ID = ?");
   pstmt.setBigDecimal(1, 153833.00)
   pstmt.setInt(2, 110592)

プリペアドステートメントの分かりやすい例だと上記のようなSQLと考えられる。 ?の箇所にパラメータを設定して実行できるような、プリコンパイルSQLという認識。
Sequelizeを利用しており、Raw Queriesによるprepare SQLを利用していたので、ソレが原因と考えられる。

解決法

RDS Proxyを利用せず、単純にRDSへの直接接続にした。少人数運用であるため、RDS Proxyの恩恵が単純に感じられなかったのもある。

終わりに

PrismaというORMの場合、以下のように明記されている。

PrismaはAWSRDSプロキシと互換性があります。ただし、RDSプロキシが接続を固定する方法が原因で、Prismaとの接続プーリングに使用するメリットはありません。

Prismaはすべてのクエリにプリペアドステートメントを使用するため、PrismaでRDSプロキシを使用してもメリットはありません。

RDS Proxyの可用性は各所で検証されている。だが「公式ドキュメントに書かれているようなことを良く読まなかったことによるアンチパターン」と思しきこともある。

  • AWS公式ドキュメントをよく読む
  • 今回のような「プリペアドステートメントを利用することがピン留め発生の原因となり、悪影響となる」といった場合、
    それらをもととして環境構築および利用ライブラリ、実装手法などを見直す必要が出てくると考えられる。
10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?