Amazon Web Services ブログ

AWS Advanced JDBC Wrapper のコネクションプーリングを設定アシスタントで構成する

本記事は 2026 年 8 月 10 日 に公開された「Configure AWS Advanced JDBC Wrapper connection pooling with the assistant」を翻訳したものです。

本記事では、Amazon Aurora および Amazon Relational Database Service 上で AWS Advanced JDBC Wrapper のコネクションプーリングを設定する方法を紹介します。ラッパーの外部プーリングと内部プーリングの違い、選択基準、JDBC-WRAPPER-CONFIGURATION-ASSISTANT による設定構築の方法も解説します。

AWS Advanced JDBC Wrapper は、コミュニティ JDBC (Java Database Connectivity) ドライバーの上に配置されるラッパーで、ドライバーを置き換えることなく Amazon Aurora、Amazon Relational Database Service (Amazon RDS)、AWS クラウドの機能を追加します。アプリケーションと実際のドライバーの間に単一のレイヤーを挿入し、すべての JDBC 呼び出しをインターセプトして Amazon Aurora や Amazon RDS 向けの機能を提供します。既存の SQL コードやツールを変更せずに、高速フェイルオーバー、Enhanced Failure Monitoring (EFM)、IAM 認証、リード/ライトスプリッティングなどの機能をプラグインとして有効化・無効化できます。

プラグインベースのアーキテクチャ

ラッパーのすべての機能はプラグインとしてパッケージ化されており、各 JDBC 呼び出しはプラグインチェーンを通過してから実際のドライバーに到達します。以下の図は、アプリケーションが connection.prepareStatement(...) のような JDBC メソッドを呼び出したときの動作を示しています。

A JDBC method call passing through the wrapper plugin chain before reaching the community driver

図 1: JDBC メソッド呼び出しがラッパーのプラグインチェーンを通過してコミュニティドライバーに到達する流れ

設計上の重要なポイントをいくつか紹介します。

  • コネクションごとに専用のインスタンス: 各 Connection オブジェクトは、固有のプラグインマネージャー、プラグインサービス、プラグインインスタンスを保持します。
  • サブスクリプションベースの実行: プラグインは getSubscribedMethods() を通じて関心のある JDBC メソッドのみをサブスクライブします。関心のないメソッドはチェーンをバイパスしてドライバーに直接渡されるため、オーバーヘッドを最小限に抑えられます。
  • チェーンの末端は常に DefaultConnectionPlugin: このプラグインが実際のコネクション作成を担当し、ConnectionProvider が呼び出されるポイントです。コネクションプーリングはここでフックされます。

デフォルトで有効なプラグインは initialConnectionauroraConnectionTrackerfailover2efm2 です。これら以外にも、リード/ライトスプリッティング、AWS Identity and Access Management (IAM) 認証、AWS Secrets Manager、Blue/Green デプロイメント、Limitless など、さまざまなプラグインが用意されています。

コネクションプーリングとは

データベースへの新しいコネクションの確立にはコストがかかります。TCP ハンドシェイク、TLS ネゴシエーション、認証、セッション初期化などを含めると、1 つのコネクションで数十~数百ミリ秒かかることがあります。リクエストごとに新しいコネクションを開閉すれば、毎回同じコストが発生します。

コネクションプールは、あらかじめ一定数のコネクションを作成して再利用する技術です。アプリケーションはプールからコネクションを「借り」、使い終わったらクローズする代わりにプールに「返却」します。返却されたコネクションは物理的にはクローズされず、次のリクエストで再利用されます。以下の図は、アプリケーションがプールからコネクションを借りて使用後に返却する流れを示しています。

An application borrowing a connection from the pool and returning it for reuse after the request completes

図 2: アプリケーションがプールからコネクションを借りて返却する仕組み

コネクションプーリングのメリット

リクエストごとに新しいコネクションを開くのではなく再利用することで、次のようなメリットが得られます。

  • レイテンシの低減: コネクション確立コストがなくなり、リクエストのレスポンスタイムが改善します。
  • スループットの向上: コネクション作成に消費されていた CPU やネットワークリソースを、実際のクエリ処理に使えるようになります。
  • リソース保護と上限制御: コネクションはサーバー上のメモリと CPU を消費するため、インスタンスが維持できるコネクション数には限りがあります。オープンなコネクション数を制限してデータベースの過負荷を防ぎます。
  • 負荷の平滑化: トラフィックスパイク時でもプールサイズが上限として機能し、データベースへの負荷を安定させます。
  • コネクション状態の管理: アイドルタイムアウト、最大ライフタイム、ヘルスチェック (バリデーション) によって無効なコネクションが除外されます。

ラッパーにおける外部プーリングと内部プーリングの動作

ラッパーのプーリングを理解するカギは、プラグインチェーンのどちら側にプールが位置するかです。以下の図は、外部プールと内部プールのプラグインチェーンに対する位置を示しています。

An external pool above the wrapper and an internal pool below the plugin chain, next to the target driver

図 3: プラグインチェーンに対する外部プールと内部プールの配置

外部プーリング

  1. プールの起動時に N 個のラッパーコネクションが作成されます。各ラッパーコネクションは、専用のプラグインマネージャーとプラグインサービスを持つ完全な論理コネクションです。
  2. アプリケーションがプールからコネクションを借りると、1 つの ConnectionWrapper を受け取ります。
  3. その上で実行されるすべてのクエリはプラグインチェーンを通過し、実際のドライバーに転送されます。
  4. アプリケーションが処理を完了してコネクションをクローズしても、物理的にはクローズされません。ConnectionWrapper がプールに戻り、次の借り出しで再利用されるため、同じ論理コネクションが複数のリクエストにまたがって存続します。
  5. フェイルオーバーが発生すると、ラッパーは同じ ConnectionWrapper 内で基盤となる物理コネクションを新しいインスタンスに再接続し、フェイルオーバー例外を発生させます。プールが保持するラッパーオブジェクト自体は変わらず、即座に再利用可能です。ただしプール側は例外しか認識しないため、例外をキャッチしつつコネクションを破棄せず、ラッパーが再接続済みのコネクションをそのまま保持するようプールに指示する必要があります。
    • フェイルオーバー例外を適切に処理してください。コネクションを無条件に破棄してはいけません。08S02 (トランザクション外でのフェイルオーバー成功) では同じコネクションが再接続済みで再利用可能です。ラッパーはデフォルトで追跡中のセッション状態 (autoCommitreadOnlyisolation など) を新しいコネクションに転送する (transferSessionStateOnSwitch=true) ため、最後のステートメントを再実行すれば済みます (実行中の処理はロールバックされます)。08007 (トランザクション内での失敗) の場合も同様にしたうえで、トランザクション全体をやり直してください。コミット状態が不明なためです。08001 (フェイルオーバー失敗) の場合のみコネクションが真に使用不可となるため、破棄して新しいものを開いてください。これらを汎用の SQLException としてキャッチし、finally/catchブロックでコネクションをクローズするのが、高速フェイルオーバーを無効にしてしまう典型的なミスです。

内部プーリング

  1. プールの分割方法。ラッパーは内部的に複数のコネクションプールを保持し、「接続先のインスタンス」と「接続ユーザー」の両方で区別します。デフォルト設定では、接続先インスタンスのアドレスとユーザー名を組み合わせてプールを分離します。同じインスタンスでもユーザーが異なれば別のプールになり、同じユーザーでもインスタンスが異なれば別のプールになります。実質的に、インスタンスとユーザーの組み合わせごとに 1 つのプールが作成されます。
  2. コネクションの貸し出し方法。アプリケーションがコネクションを要求すると、ラッパーは毎回新しい物理コネクションを作成するのではなく、該当する組み合わせのプールを探します。プールが存在すればアイドルコネクションを取り出して貸し出し、存在しなければ新規作成してからコネクションを渡します。使用後にクローズされたコネクションは実際には切断されず、プールに戻って再利用されます。
  3. 未使用プールのクリーンアップ。一定期間 (デフォルト 30 分) コネクションのリクエストがないプールはクリーンアップ候補としてマークされます。ただし候補になっても即座にクローズされるわけではありません。貸し出し中のコネクションがすべて返却されて初めて、プールが実際にクローズされリソースが回収されます。つまり、そのプールからまだコネクションを使用中のクライアントがいれば、有効期限を過ぎてもプールは存続し、使用中のコネクションが突然切断されることはありません。

内部プーリングのメリットが得られるケース

内部プーリングが最も効果を発揮するのは、いくつかの特定のシナリオで、最も重要なのがリード/ライトスプリッティングです。

リード/ライトスプリッティング: 内部プーリングの最大のメリットは、リード/ライトスプリッティングプラグインと組み合わせたときに現れます。

setReadOnly(true/false) の呼び出しに応じて、リード/ライトスプリッティングプラグインは 1 つの論理コネクションの接続先をライターとリーダーの物理コネクション間で切り替えます。

  • 内部プールなしの場合: setReadOnly(true) が初めて呼び出されるたびにリーダーへの新しい物理コネクションが開かれ、そのコネクションは論理コネクションの存続期間中のみキャッシュされます。他の論理コネクションからは再利用できません。
  • 内部プールありの場合: 各インスタンス (ライター/リーダー) への物理コネクションがプールに蓄積され、複数の論理コネクションが setReadOnly での切り替え時にこれらを再利用します。@Transactional(readOnly = true) のようにリーダーとライターを頻繁に行き来する Spring ワークロードでは特に効果的です。

JDBC-WRAPPER-CONFIGURATION-ASSISTANT とは

JDBC-WRAPPER-CONFIGURATION-ASSISTANT は、AWS Advanced JDBC Wrapper 4.0 以降で利用できる生成 AI ベースの設定アシスタントです。ラッパーの設定選択、既存設定のレビュー、設定の問題診断を支援します。ラッパーのソースコードや公式ドキュメントに基づいた回答を生成し、プラグインの動作、パラメータ名、デフォルト値、排他的な組み合わせなどの詳細を含みます。

Kiro を例にすると、JDBC-WRAPPER-CONFIGURATION-ASSISTANT スキルを有効化した後、会話の開始時に 3 つのエントリーポイントが提示されます。状況に最も合ったものを選択できます。

  1. Greenfield – 達成したいゴールを説明します。例: 「HikariCP を使用した Aurora PostgreSQL クラスターのフェイルオーバーを設定したい」。アシスタントがフォローアップの質問をし、段階的に設定を絞り込みます。
  2. Review – 現在の設定を貼り付けるか、スタックを説明します。アシスタントがレビューして改善点を提案します。
  3. Skip the interview – 動作する設定を直接リクエストします。例: 「デフォルトの Aurora PostgreSQL + HikariCP 設定をください」。アシスタントが使用可能な設定を返し、前提条件を明示します。

内部プーリングの設定オプションと例

内部プーリングを有効にする方法は 2 つあります。

オプション A — 組み込み設定パラメータ (connectionPoolType)

最も直接的な方法です。コネクションプロパティで connectionPoolType を指定し、プール固有の設定は cp- プレフィックスで渡します。以下に例を示します。

# HikariCP 内部プールの有効化 + プールパラメータ
connectionPoolType=hikari&cp-MaximumPoolSize=20&cp-MinimumIdle=1

# c3p0 内部プール
connectionPoolType=c3p0&cp-MaxConnectionAge=3600&cp-MaxPoolSize=15
  • 指定可能な値: hikaric3p0
  • cp- の後の名前は com.zaxxer.hikari.HikariConfig または com.mchange.v2.c3p0.ComboPooledDataSource のプロパティに直接マッピングされます。例: cp-MaximumPoolSizeHikariConfig.maximumPoolSize に対応します。
  • 内部的には InternalConnectionPoolServiceclusterId ごとに PooledConnectionProvider を作成してキャッシュします。つまり、異なる clusterId を持つクラスターは別々のプールを使用します。

JDBC-WRAPPER-CONFIGURATION-ASSISTANT の使用例

質問: connectionPoolType=hikari のみ有効にしました。プールサイズなどの詳細設定はどこに記述すればよいですか?

回答: 詳細なプール設定は cp- プレフィックスで渡します。プレフィックスの後のキーは HikariCP の HikariConfig プロパティと 1 対 1 で対応します。例えば、最大 20 コネクション、アイドル 2 コネクションを維持する場合は以下のように設定します。

connectionPoolType=hikari&cp-MaximumPoolSize=20&cp-MinimumIdle=2

内部プールは clusterId で分離・キャッシュされるため、1 つのアプリケーションが複数のクラスターに接続する場合は、プールの混在を避けるため各クラスターに一意の clusterId を割り当ててください。プールサイズは控えめに設定してください。内部プーリングではインスタンスごとに複数のプールが作成される可能性があるため、プール全体の最大値の合計がデータベースインスタンスの max_connections 制限を超えないようにしてください。

オプション B — カスタム HikariPooledConnectionProvider

より細かい制御が必要な場合に使用します。プール設定関数 (HikariPoolConfigurator) と、オプションでプールキー関数 (HikariPoolMapping) を提供します。

// 1) プール設定関数
private static HikariConfig getHikariConfig(HostSpec hostSpec, Properties props) {
    HikariConfig config = new HikariConfig();
    config.setMaximumPoolSize(10);
    config.setMinimumIdle(1);
    config.setIdleTimeout(60_000);
    return config;
}

// 2) プールキー関数 --- インスタンス URL + ユーザー + カスタム値の組み合わせでプールを分離
private static String getPoolKey(HostSpec hostSpec, Properties props) {
    final String user = props.getProperty(PropertyDefinition.USER.name);
    final String somePropertyValue = props.getProperty("somePropertyValue");
    return hostSpec.getUrl() + user + somePropertyValue;
}

// 3) 登録
final HikariPooledConnectionProvider connProvider =
        new HikariPooledConnectionProvider(
                MyApp::getHikariConfig,
                MyApp::getPoolKey);
Driver.setCustomConnectionProvider(connProvider);

// 4) アプリケーションシャットダウン時
ConnectionProviderManager.releaseResources();

プールを正しく動作させるため、ラッパーは以下の 4 つの設定を自動的にオーバーライドします: jdbcUrl (ホスト/ポート/データベースを含む)、exceptionOverrideClassName、ユーザー名、パスワード。そのため、HikariPoolConfigurator でこれらの値を指定しても無視されます。コンストラクタは、プールキー、クリーンアップ、適格性を制御するいくつかのオプションパラメータも受け付けます。以下の表にまとめます。

パラメータ 意味
hikariPoolConfigurator HikariPoolConfigurator (必須) プール設定を返す関数。追加設定がない場合は空の HikariConfig を返します。
mapping HikariPoolMapping (オプション) プールキーを生成する関数。キーが一意になるたびに新しいプールが作成されます。デフォルトはユーザー名です。
acceptsUrlFunc AcceptsUrlFunc (オプション) どのコネクションに内部プールを適用するか決定します。デフォルトはインスタンスエンドポイントのみです。
poolExpirationNanos long プールがクリーンアップ候補になるまでのアイドル時間 (デフォルト 30 分)。
poolCleanupNanos long 期限切れプールをクリーンアップする間隔。

外部プーリングと内部プーリングの違い

以下の表は、配置、トポロジー認識、設定、シャットダウン動作の観点で両方のプーリングアプローチの主な違いをまとめたものです。

観点 外部プーリング (HikariCP など) 内部プーリング (ラッパー)
プールの配置 ラッパーの上 (アプリケーションとラッパーの間) ラッパーの内部 (ラッパーとターゲットドライバーの間)
プール対象 論理コネクション (ConnectionWrapper) 物理コネクション (インスタンスごと)
トポロジー認識 なし (接続先インスタンスを認識しない) あり (インスタンスごとにプールが分離)
プールキー 通常は単一のデータソース (エンドポイント) (インスタンス URL、ユーザー/カスタムキー)。プールプロバイダーは clusterId ごとにキャッシュ
クラスターエンドポイント そのままプールされる (注意が必要) プールされない (インスタンスエンドポイントのみプール)
設定の記述場所 プールライブラリ + AwsWrapperDataSource connectionPoolType または HikariPooledConnectionProvider
setReadOnly 切り替え プールは切り替えられたリーダーコネクションを認識しない → オブジェクト間での共有が限定的 インスタンスプールから再利用 → オブジェクト間で共有
leastConnections 戦略 利用不可 利用可能
シャットダウンのクリーンアップ プールライブラリの close ConnectionProviderManager.releaseResources() が必要

外部プーリングと内部プーリングの長所と短所

外部プーリングの長所

外部プーリングにはいくつかの利点があり、フレームワーク駆動のアプリケーションで特に効果的です。

  • フレームワークとの親和性: Spring Boot などのフレームワークはデフォルトで外部プールを管理します。
  • アプリケーションレベルでの合計同時コネクション上限 (maximumPoolSize) を直感的に制御できます。
  • チューニングのノウハウやトラブルシューティングのリソースが豊富です。

外部プーリングの短所

一方で、フェイルオーバーやリード/ライトスプリッティングではトレードオフがあります。

  • setReadOnly ベースのリード/ライトスプリッティングでは、プールレベルでのリーダーコネクション再利用が難しくなります。
  • フェイルオーバー直後、プール内の多くのコネクションが無効化され、プール枯渇や再接続の急増を招くことがあります。
  • leastConnections のようなプール対応のホスト選択戦略は使えません。

内部プーリングの長所

内部プーリングには独自の利点があり、多くはトポロジー認識に関連しています。

  • トポロジー認識: インスタンスごとにプールを保持するため、リード/ライトスプリッティングでリーダーへの切り替えコストが大幅に削減され、複数の Connection オブジェクトがプールを共有します。
  • クラスターエンドポイントプーリングの落とし穴を構造的に回避します (インスタンスエンドポイントのみプール)。
  • leastConnections のようなプール対応戦略でリーダー負荷をより均等に分散します。
  • フェイルオーバー時にラッパーがインスタンスプールを直接管理するため、切り替えがスムーズです。

内部プーリングの短所

一方で、運用上のトレードオフもいくつかあります。

  • 設定と運用の概念が増えます (HikariPoolConfiguratorHikariPoolMappingreleaseResources())。
  • プールキーからユーザー名を省略すると権限分離が壊れ、キャッシュされたプールコネクションがパスワードを再検証しません。
  • プール数がインスタンス × ユーザーで増加するため、メモリとコネクションのオーバーヘッドに注意が必要です。

外部プーリングと内部プーリングのプラグイン互換性の違い

以下の表は、外部プーリングと内部プーリングで各プラグインや機能がどう動作するかを示し、推奨事項を記載しています。

プラグイン / 機能 外部プーリング 内部プーリング 備考
Read/Write Splitting (readWriteSplitting) 動作するがリーダーの再利用は限定的 推奨 内部プールがオブジェクト間でリーダーコネクションを再利用
@Transactional(readOnly=true) (Spring) 非推奨 (切り替えごとにオーバーヘッド) 推奨 内部プールと組み合わせた場合のみ推奨
leastConnections ホスト選択戦略 利用不可 利用可能 プールのアクティブコネクション数の読み取りが必要なため、内部プールが必須
lowestLoad / highestLoad / roundRobin / weightedRandom / random 利用可能 (戦略による) 利用可能 内部プールプロバイダーが直接サポート
Failover / Failover2 利用可能 (プール側で例外処理が必要) 利用可能
EFM / EFM2 利用可能 利用可能 Amazon RDS Proxy エンドポイントとは互換性なし (監視対象のインスタンストポロジーがないため)
IAM / Secrets Manager 認証 利用可能 利用可能 (キャッシュされたコネクションがパスワード再検証をスキップする可能性あり) ユーザーをプールキーに含める必要あり
Blue/Green 利用可能 利用可能 スイッチオーバー時に Blue コネクションを切断

外部プーリングと内部プーリングの選択時の考慮事項

選択はどちらが普遍的に優れているかではなく、ワークロードと運用環境への適合度で決まります。以下のセクションでは主な判断ポイントをまとめ、Greenfield モードでアシスタントを活用する方法を示します。

ワークロード / 機能の観点

  • リード/ライトスプリッティングを使用するかどうか: setReadOnly ベースの R/W スプリッティングを多用する場合 → 内部プールが有利です。使用せず、シンプルな単一インスタンス/単一エンドポイント構成の場合は、外部プールで十分です。
  • リード負荷分散戦略: leastConnections でリーダーを分散したい場合 → 内部プールが必須です。
  • フェイルオーバーの頻度と感度: フェイルオーバーが頻繁に発生する場合や高速リカバリが重要な場合、内部プールならラッパーがインスタンスプールを直接管理するため切り替えがスムーズです。外部プールでは、古いコネクションの処理やプール枯渇への対策が必要です。
  • トランザクションパターン: @Transactional(readOnly=true) を多用する Spring アプリケーションでは、内部プール + R/W スプリッティングの組み合わせが推奨されます。

JDBC-WRAPPER-CONFIGURATION-ASSISTANT の使用例 (Greenfield)

質問: Spring アプリケーションで @Transactional(readOnly = true) を頻繁に使用しています。どちらのプーリングアプローチを使うべきですか?

回答: リード/ライトスプリッティングのユースケースですね。まず 2 点確認させてください。(1) 対象は Aurora クラスターですか? (2) リードモードの切り替えはフレームワーク経由で @Transactional(readOnly = true) を使用していますか?

質問: 両方 Yes です。Aurora PostgreSQL を使用しています。

回答: その場合、readWriteSplitting プラグインとラッパーの内部プールを組み合わせてください。内部プーリングがないと、論理コネクションでリードモードが初めて有効化されるたびに新しい物理リーダーコネクションが開かれ、コネクションチャーンが増加しインスタンスのコネクション上限を圧迫します。まず以下の設定をベースにしてください。

data-source-properties:
  wrapperPlugins: initialConnection,readWriteSplitting,failover2,efm2
  wrapperDialect: aurora-pg
  readerHostSelectorStrategy: random
  connectionPoolType: hikari
  cp-MaximumPoolSize: 20
  cp-MinimumIdle: 2

この構成では auroraConnectionTracker を追加しないでください。内部プールが既に clusterId によってインスタンスコネクションを追跡・無効化するため、トラッカーは冗長になります。また、トレースログで @Transactional(readOnly = true) が実際にラッパーまで伝播されているか検証することをお勧めします。

エンドポイント / トポロジーの観点

  • 接続 URL の種類: クラスター/リーダークラスターエンドポイント経由で接続する場合、外部プールは古い DNS のリスクがあるため、initialConnection プラグインとバリデーション設定が重要です。内部プールはインスタンス単位で動作するため、この問題を構造的に回避します。
  • マルチクラスター接続: 1 つのアプリケーションが複数のクラスターに接続する場合、クラスターごとに異なる clusterId を割り当てる必要があります。内部プールは clusterId でプールを分離するため、この設定は特に重要です。
  • カスタムドメイン/IP/プロキシ: 非標準の URL では、clusterIdclusterInstanceHostPattern の設定を確認してください。

JDBC-WRAPPER-CONFIGURATION-ASSISTANT の使用例 (Greenfield)

質問: 1 つのサービスが 2 つの Aurora クラスターに接続しています。プールをどう分離すべきですか?

回答: マルチクラスター構成では、プーリングアプローチに関係なく各クラスターに一意の clusterId を割り当ててください。両方のクラスターがデフォルト値を共有すると、トポロジーキャッシュが衝突し、間違ったクラスターへのフェイルオーバーやモニター間の干渉が発生する可能性があります。内部プーリングでは、clusterId がプール分離の境界にもなります。

a.addDataSourceProperty("clusterId", "cluster-a-prod");
b.addDataSourceProperty("clusterId", "cluster-b-prod");

質問: クラスターライターエンドポイント経由で接続し、外部プーリングを使用する場合、何に注意すべきですか?

回答: フェイルオーバー直後、古い DNS によりクラスターエンドポイントが一時的に旧ライターに解決される場合があります。外部プーリングのみを使用する場合は、initialConnection プラグインを含めることで最初のコネクションが実際のインスタンスエンドポイントに着地するようにしてください。EFM がクラスターエンドポイントではなくインスタンスエンドポイントを監視するためにも有効です。内部プーリングはインスタンスエンドポイントのみをプールするため、この問題を構造的に回避します。

セキュリティの観点

  • マルチユーザーの権限分離: HikariPoolMapping にユーザー名を必ず含めてください (省略すると、異なる権限を持つユーザー間でプールが共有されるリスクがあります)。
  • IAM トークン認証: トークン TTL とプールコネクションのライフタイムの関係を考慮してください。内部監視ワーカーが IAM プラグインを失わないように ConfigurationProfile の設定が推奨されます。

JDBC-WRAPPER-CONFIGURATION-ASSISTANT の使用例 (Greenfield)

質問: HikariCP で IAM 認証を使用していますが、15 分あたりから一部のコネクションが失敗し始めます。どう対処すべきですか?

回答: 直感的な対処法は HikariCP の maxLifetime を短くすることですが、ここでは効果がありません。iam プラグインがサブスクライブするのは connectforceConnect のみで、トークンはハンドシェイク時に一度だけ提示され、以降の呼び出しにフックはありません。認証に成功したコネクションがトークンの経過時間によって失敗することはありません。maxLifetime はプールの衛生管理の観点で設定し、トークンとは切り離して考えてください。

重要なのは iamExpiration です。ラッパーはトークンを (region, host, port, user) ごとにキャッシュし、iamExpiration はそのキャッシュの TTL で、デフォルトは 870 秒 (サーバー側のトークンウィンドウ 15 分に対して) です。900 を超える値は設定しないでください。ドライバーは値をクランプせずに読み取るため、大きな TTL を設定するとキャッシュが RDS によって既に無効化されたトークンを配信します。プラグインのドキュメントでは 600 を例として使用しています。

hikari:
  max-lifetime: 1800000
  data-source-properties:
    wrapperPlugins: initialConnection,auroraConnectionTracker,iam,failover2,efm2
    iamRegion: us-east-1
    iamExpiration: 600

キャッシュされたトークンが拒否された場合、プラグインはトークンを再生成して接続を1回リトライするため、完全な障害にはなりません。ただし、影響を受けるコネクションはそれぞれ最初のハンドシェイクが一度失敗するため、間欠的な接続遅延やエラーとして症状が現れます。

コンテナや Lambda 環境では、iamRegion を明示的に設定するほうが安全です。内部プーリングを使用し、ユーザーごとに権限が異なる場合は、キャッシュされたコネクションがユーザー間で再利用されないよう、プールキーにユーザーを含めてください。

運用 / フレームワークの観点

  • フレームワーク標準: Spring Boot のようなフレームワークはデフォルトで外部プールを管理します。チーム標準が外部プールで R/W スプリッティングを使用しない場合、外部プールのほうが運用がシンプルです。
  • プールサイジング: 内部プールはインスタンス × ユーザーの数だけプールが増える可能性があり、合計コネクション数が増加します。各インスタンスの maximumPoolSize を控えめに設定し、データベースの max_connections 制限内に収まるようにしてください。
  • メモリ/スレッドコスト: プール数が増えるほど、アイドルコネクションやヘルスチェックスレッドのコストが増加します。有効期限/クリーンアップ間隔 (poolExpirationNanospoolCleanupNanos) と minimumIdle を適切にチューニングしてください。
  • コネクションライフタイムの制御: リーダーのキープアライブ (cachedReaderKeepAliveTimeoutMs) をプールの maxLifetime/idleTimeout ポリシーと合わせて設計してください。

JDBC-WRAPPER-CONFIGURATION-ASSISTANT の使用例 (Greenfield)

質問: チーム標準は Spring Boot + HikariCP です。リード/ライトスプリッティングは使用していません。内部プーリングは必要ですか?

回答: いいえ。リード/ライトスプリッティングを使用しないシンプルな単一エンドポイントの Spring アプリケーションでは、おなじみの外部 HikariCP プールで十分で、運用もシンプルです。ただし、HikariCP + フェイルオーバーで 1 つ重要な設定があります: exception-override-class-namesoftware.amazon.jdbc.util.HikariCPSQLException を設定し、ラッパーがフェイルオーバー後に再接続済みのコネクションを HikariCP が破棄しないようにしてください。

hikari:
  maximum-pool-size: 30
  exception-override-class-name: software.amazon.jdbc.util.HikariCPSQLException
  data-source-properties:
    wrapperPlugins: initialConnection,auroraConnectionTracker,failover2,efm2
    wrapperDialect: aurora-pg

質問: リードデータソースとライトデータソースを完全に分離すれば、内部プーリングを避けられますか?

回答: はい。リードがリーダークラスター URL、ライトがライタークラスター URL を使い、2 つの別々のデータソースを構成する場合、リード/ライトスプリッティングプラグインと内部プーリングの両方を回避できます。各データソースに固定のロールを付与し、failoverModestrict-reader または strict-writer に設定してください。リーダーデータソースに readWriteSplitting を追加しないでください。切り替えるロールがないためです。

判断のまとめ (クイックガイド)

  • シンプルな単一エンドポイント + 標準的な Spring アプリ、R/W スプリッティングなし → 外部プール (HikariCP)。フェイルオーバー SQLState マッピングに注意。
  • リード/ライトスプリッティング + リーダー負荷分散 (leastConnections) が必要 → 内部プール。
  • データソースを分離してリードはリーダークラスター URL、ライトはライタークラスター URL を使える場合 → R/W プラグインと内部プールの両方をスキップし、2 つの外部プールで対応可能。

まとめ

AWS Advanced JDBC Wrapper のコネクションプーリングを理解するカギは、「プールがラッパーの上 (外部) か下 (内部) か」という位置関係です。

  • 外部プーリング論理コネクションをプールします。ツールが成熟しておりフレームワークとの親和性が高い一方、リード/ライトスプリッティングには制約があります。
  • 内部プーリング物理コネクションをインスタンス単位でプールします。トポロジーを認識するため、リード/ライトスプリッティング、リーダー負荷分散 (leastConnections)、フェイルオーバー切り替えに優れています。その代わり、クラスターエンドポイントはプールされず (インスタンスエンドポイントのみ)、プールキー、パスワード再検証、リソースクリーンアップなどの運用概念が追加されます。

最終的に、ラッパーのプラグインアーキテクチャとプーリングアプローチは別々の選択ではなく、一体として設計すべきものです。まず JDBC-WRAPPER-CONFIGURATION-ASSISTANT を開いてワークロードを説明し、アプリケーションにそのまま組み込める初期設定を提案してもらいましょう。ワークロードの読み書き比率、フェイルオーバー感度、セキュリティ要件、運用標準を総合的に検討すれば、Aurora の高可用性とプーリングによるパフォーマンス向上の両方を活用できます。

プールがラッパーのどちら側に位置すべきか迷ったら、ワークロード (エンジン、エンドポイント、フレームワーク、読み書きパターン) を JDBC-WRAPPER-CONFIGURATION-ASSISTANT に伝えてください。初期設定の提案に加え、内部プールなしでリード/ライトスプリッティングプラグインを使用している、HikariCP の例外オーバーライドが不足しているなど、よくあるアンチパターンも指摘してくれます。

著者について

Dave Cramer

Dave Cramer

Dave は、Amazon Web Services のプリンシパルソフトウェアエンジニアです。PostgreSQL JDBC ドライバーのメンテナーとして PostgreSQL にも大きく貢献しています。クライアントインターフェースとクライアントとの連携に情熱を注いでいます。

Youngdong Lee

Youngdong Lee

Youngdong は、データベースを専門とするソリューションアーキテクトです。Amazon データベースサービスに関する技術的な問い合わせや課題の分析を支援し、データとインフラストラクチャの安定した導入・運用に取り組んでいます。


この記事はSolutions ArchitectのShinya Sugiyama が翻訳を担当しました。