RDS Proxy を MySqlConnector の前段に置き、Dapper でいつも通り QueryFirstOrDefault などを使っている。CloudWatch の DatabaseConnectionsCurrentlySessionPinned が立ち、Proxy ログに pinning 警告が出る——そんなときの整理。
この記事は Dapper(従来どおりの書き方)+ MySqlConnector + RDS Proxy(MySQL / Aurora) を前提に、FirstOrDefault で実際にどんなクエリが飛ぶかと、どう直すかをざっくり書く。
アプリ
└ Dapper(QueryFirstOrDefault など)
└ MySqlConnector
└ RDS Proxy
└ Aurora / RDS MySQL
FirstOrDefault そのものではなく、Proxy が「セッション状態が残る」と判断したときQueryFirstOrDefault は SQL を自動で LIMIT 1 付きに書き換えないLIMIT 1 を付け、Proxy ログの Reason で pinning の正体を確認するProxy は DB 接続をトランザクション単位で使い回す。ただしセッション固有の状態(prepared statement、SET、一時テーブルなど)が残ると、他クライアントへ貸せなくなる。このとき接続を特定クライアントにピン留めする。
ログ例:
Reason: A protocol-level prepared statement was detected.
「FirstOrDefault だから pin する」わけではない。Reason に書いてある操作を潰すのが近道。
「FirstOrDefault だから DB も1件だけ読む」と思いがちだが、Dapper + MySqlConnector でも SQL 文そのものは書き換わらない。
var user = connection.QueryFirstOrDefault<User>(
sqlWithoutLimit,
new { Id = id });
LIMIT 1 は付かない)@Id 等はパラメータとしてバインド(文字列結合はしない)Prepare() を呼ばないQueryFirstOrDefault は CommandBehavior.SingleRow を付ける(MySqlConnector 0.57 以降は sql_select_limit 最適化あり)。ただし SQL 文に LIMIT 1 が付くわけではない「First」はコード側の取り方であって、クエリ文の書き換えではない。
QueryFirstOrDefault … 先頭行だけ読む。SQL は書き換えないQuery(...).FirstOrDefault() … 既定でバッファする。LIMIT 1 なしだと無駄が大きい// Before
var user = connection.QueryFirstOrDefault<User>(
sqlWithoutLimit,
new { Id = id });
// After: SQL だけ LIMIT 1 付きにする
var user = connection.QueryFirstOrDefault<User>(
sqlWithLimit1,
new { Id = id });
DB / Proxy に飛ぶクエリ自体が「最大1行」になる。FirstOrDefault 利用ではほぼ必須寄り。pinning だけの対策にはならないが、まずここから直す。
var user = connection.Query<User>(
sqlWithLimit1,
new { Id = id })
.FirstOrDefault();
LIMIT 1 なしだと「たくさん取って先頭1件」になりやすい。API の好みで選べばよい。
QueryFirstOrDefault … 0件 or 1件想定QuerySingleOrDefault … 0件 or 1件。2件以上なら例外LIMIT 1 は付けるSQL を直しても SessionPinned が下がらない場合は、Proxy ログの Reason を見て MySqlConnector / Proxy 側を合わせる。
Prepare( を探す。必要なら接続文字列に IgnorePrepare=true も試せる(補助的な手段)Pipelining=falseSET 起因なら Proxy の Initialization query で全接続を同一初期化にするDatabaseConnectionsCurrentlySessionPinned を確認LIMIT 1 があるか確認LIMIT 1 は自動付与されないLIMIT 1、並行して Proxy ログの Reason を確認IgnorePrepare=true は prepared statement が Reason のときの補足オプション