Amazon Web Services ブログ

Amazon Aurora DSQL における接続プーリング戦略

本記事は 2026 年 7 月 20 日 に公開された「Connection pooling strategies in Amazon Aurora DSQL」を翻訳したものです。

本記事では、Aurora DSQL の接続負荷を減らし、1 秒あたり 100 接続のレート制限を超えず、再接続が一斉に集中する thundering herd を避けるための、具体的な 4 つの戦略を解説します。読み終える頃には、大規模でも安定した性能を発揮する接続プールを構成するための、本番運用に使えるチェックリストが手に入ります。

接続プーリングの戦略次第で、Amazon Aurora DSQL アプリケーションが安定してスケールするか、負荷で破綻するかが決まります。Amazon Aurora DSQL 独自のアーキテクチャは、接続プーリングの効果をさらに大きくします。トランザクション単位の多重化と AWS Identity and Access Management (IAM) による認証により、Aurora DSQL では適切なプーリング戦略が、従来の PostgreSQL の接続管理では実現できない形で性能を高めます。

注: 本記事は、PostgreSQL の接続管理の概念と基本的な IAM 認証を理解していることを前提としています。Amazon Aurora DSQL を初めて使う場合は、これらの戦略を適用する前に、まず Getting Started ガイドから始めてください。

サーバーレス環境での接続プーリング

Amazon Aurora DSQL は運用の複雑さを自動的に処理しますが、それでも接続プーリングは欠かせません。新しい接続のたびに、コストの高い TLS ハンドシェイクと認証情報の交換が発生します。プーリングは確立済みの接続を再利用し、この負荷を抑えてレイテンシーを削減します。また、トラフィックが急増すると、1 秒あたり 100 接続 (バースト時 1,000) のレート制限に達することがあり、既存の接続を再利用すればサービスの制限内に安全に収まります。さらに、Amazon Aurora DSQL には接続の最大有効期間があり、クライアントは 1 時間で切断されます。適切に構成したプールはこの制限に達する前に接続をリサイクルし、セッションを透過的に維持します。

トランザクション単位のプーリング: アーキテクチャに組み込み済み

Amazon Aurora DSQL はトランザクションプーリングモデルを採用しています。接続を Query Processor に割り当てるのは、セッション全体ではなくトランザクションの実行中だけです。標準的な PostgreSQL では、接続はバックエンドのサーバープロセスと 1:1 のセッション関係を持続的に維持します。一方 Amazon Aurora DSQL はこの対応関係を切り離すため、少数の Query Processor ではるかに多くの接続を処理できます。

この設計は、接続の使用率が低いこと、つまりほとんどの接続が大半の時間アイドル状態であることを前提としています。使用率が低いほど、接続プールに必要な Query Processor は少なくて済みます。そのため、PgBouncer や pgpool-II のようなデータベース側のプロキシは使うべきではありません。トランザクション単位の接続多重化がデフォルトで組み込まれているため、これらのツールはサービスの接続アーキテクチャと重複します。

さらに、一部の PostgreSQL 機能はサポートされていません。SQL レベルの PREPARE/DEALLOCATE ステートメント (ただし拡張クエリプロトコル経由のプリペアドステートメントは正常に動作します)、WITH HOLD カーソル、セッションレベルのアドバイザリーロックです。

Amazon Aurora DSQL の 4 つの接続プーリング戦略

以下の 4 つの戦略は、それぞれ Amazon Aurora DSQL の接続管理の特定の側面に対応します。組み合わせて適用すれば、アプリケーションがサービスの制限内に収まり、負荷がかかっても安定した性能を保てます。新しいアプリケーションを構築する場合は戦略 1 から始め、既存の PostgreSQL アプリケーションを移行する場合はチェックリストとして活用してください。

戦略 1: 公式の AWS コネクタを使う

公式の Amazon Aurora DSQL コネクタの利用をお勧めします。トークンの生成、有効期間の 80% でのキャッシュ、透過的な更新など、IAM トークンのライフサイクル全体を自動的に処理します。

表 1: プログラミング言語別の推奨接続プールライブラリと主要な設定パラメータ

言語 プーリングライブラリ 主要な設定
Java HikariCP maximumPoolSize
Python psycopg ConnectionPool min_size
Node.js node-postgres (pg.Pool) max
Go pgxpool MaxConns

Java (HikariCP) — Spring Boot、Quarkus、Micronaut のアプリケーションでは HikariCP を使います。

HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://<cluster-endpoint>:5432/<db>?ssl=true&sslmode=verify-full&sslrootcert=<path>");
config.setMaximumPoolSize(20);
config.setMaxLifetime(55 * 60 * 1000); // 55 minutes
config.setIdleTimeout(10 * 60 * 1000); // 10 minutes
config.setConnectionTimeout(30 * 1000); // 30 seconds
config.setKeepaliveTime(5 * 60 * 1000); // 5 minutes
HikariDataSource ds = new HikariDataSource(config);

Python (psycopg ConnectionPool) — Django、Flask、FastAPI のアプリケーションでは psycopg ConnectionPool を使います。

from psycopg_pool import ConnectionPool

pool = ConnectionPool(
    conninfo="host=<endpoint> dbname=<db> sslmode=verify-full",
    min_size=2,
    max_size=20,
    max_lifetime=55 * 60,  # 55 minutes in seconds
    max_idle=10 * 60,  # 10 minutes idle timeout
    reconnect_timeout=30,  # 30 seconds to reconnect
)

Node.js (node-postgres) — Express.js、NestJS、サーバーレスの Node.js 関数では node-postgres の Pool を使います。

const { Pool } = require("pg");

const pool = new Pool({
    host: "<cluster-endpoint>",
    database: "<db>",
    ssl: { rejectUnauthorized: true },
    max: 20,
    idleTimeoutMillis: 10 * 60 * 1000, // 10 minutes
    connectionTimeoutMillis: 30 * 1000, // 30 seconds
});

Go (pgxpool) — Gin、Echo、AWS Lambda Go ランタイムで構築する Go アプリケーションでは pgxpool を使います。

connStr := "host=<cluster-endpoint> dbname=<db> sslmode=verify-full"
config, _ := pgxpool.ParseConfig(connStr)
config.MaxConns = 20
config.MinConns = 2
config.MaxConnLifetime = 55 * time.Minute // 55 minutes
config.MaxConnIdleTime = 10 * time.Minute // 10 minutes idle
pool, _ := pgxpool.NewWithConfig(ctx, config)

AWS Lambda: プールはハンドラーの外でインスタンス化する

AWS Lambda は Aurora DSQL のワークロードでよく使われるコンピューティングサービスです。Lambda の実行モデルには特有のプーリングパターンが必要です。接続プールをモジュールスコープ (ハンドラー関数の外) で作成し、同じ実行環境のウォーム呼び出し間で保持されるようにします。

Lambda が実行環境を再利用すると、モジュールスコープで作成したプールはアクティブなまま残ります。以降の呼び出しでは TLS ハンドシェイクと IAM トークンの交換を完全にスキップできます。コールドスタート時にはプールが一度だけ初期化され、その環境が存続する間は再利用されます。

同時に実行される各 Lambda 呼び出しはそれぞれ独立した実行環境で動くため、プールも個別に持ちます。多数の同時実行にわたって接続を過剰に確保しないよう、プールサイズは小さく (Lambda インスタンスあたり 1〜3 接続) 保ってください。

Lambda の主なガイドライン:

  • プールはハンドラーの外でインスタンス化する。これが最も重要なルールです。
  • 最大プールサイズを 1〜3 に設定する。各 Lambda インスタンスは (バッファ呼び出しを使わない限り) 一度に 1 リクエストしか処理しないため、大きなプールは無駄になります。
  • アイドルタイムアウトを設定しない。接続は実行環境が存続する間ずっと維持させます。
  • Lambda 以外のワークロードと同様に、1 時間のハード制限に達しないよう、最大有効期間を 55 分にしてジッターを加える。
  • コールドスタートのレイテンシーを考慮する。最初の呼び出しでは TLS と IAM 認証の負荷が発生します。以降のウォーム呼び出しではプールされた接続を再利用します。

Python (psycopg ConnectionPool) — Lambda

# pool is created ONCE at module scope, reused across warm invocations
from psycopg_pool import ConnectionPool
import boto3

pool = ConnectionPool(
    conninfo="host=<cluster-endpoint> dbname=<db> sslmode=verify-full",
    min_size=1,
    max_size=2,
    max_lifetime=55 * 60,  # 55 minutes
    reconnect_timeout=30,
)

def handler(event, context):
    """Lambda handler — pool is already initialized."""
    with pool.connection() as conn:
        result = conn.execute("SELECT * FROM orders WHERE id = %s", [event["order_id"]])
        return result.fetchone()

Node.js (node-postgres) — Lambda

// Pool is created ONCE at module scope, reused across warm invocations
const { Pool } = require("pg");

const pool = new Pool({
    host: "<cluster-endpoint>",
    database: "<db>",
    ssl: { rejectUnauthorized: true },
    max: 2, // small pool per Lambda instance
    idleTimeoutMillis: 0, // don't close idle connections
    connectionTimeoutMillis: 30 * 1000, // 30 seconds
});

exports.handler = async (event) => {
    // Pool is already warm on subsequent invocations
    const client = await pool.connect();
    try {
        const result = await client.query("SELECT * FROM orders WHERE id = $1", [event.orderId]);
        return result.rows[0];
    } finally {
        client.release();
    }
};

Go (pgxpool) — Lambda

package main

import (
    "context"
    "time"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/jackc/pgx/v5/pgxpool"
)

// Pool created at package scope — initialized once per execution environment
var pool *pgxpool.Pool

func init() {
    connStr := "host=<cluster-endpoint> dbname=<db> sslmode=verify-full"
    config, _ := pgxpool.ParseConfig(connStr)
    config.MaxConns = 2
    config.MinConns = 1
    config.MaxConnLifetime = 55 * time.Minute
    config.MaxConnLifetimeJitter = 5 * time.Minute

    pool, _ = pgxpool.NewWithConfig(context.Background(), config)
}

func handler(ctx context.Context, event map[string]string) (string, error) {
    row := pool.QueryRow(ctx, "SELECT name FROM orders WHERE id = $1", event["order_id"])
    var name string
    err := row.Scan(&name)
    return name, err
}

func main() {
    lambda.Start(handler)
}

ヒント: Lambda 関数の同時実行数が多い場合 (数百の同時実行)、各実行環境がそれぞれプールを作成します。最大プールサイズ 2 で 500 の Lambda が同時実行されると、Aurora DSQL への接続は最大 1,000 になり得ます。接続の総数を監視し、クラスターあたり 10,000 同時接続のクォータ内に収めてください。

重要: クライアント接続には sslmode=verify-full を設定し、証明書を完全に検証して経路上の攻撃 (on-path attack) を防ぎます。Aurora DSQL は verify-carequire といった弱いモードでも接続を受け付けますが、これらはサーバーの正当性を検証しません。

安定した接続プールを維持するには、IAM トークンのライフサイクルの理解が重要です。

新しい接続ごとに、新しい IAM 認証トークンを生成します。トークンの生成はローカルでの署名操作で、負荷はごくわずかです。公式の AWS コネクタはこれを自動的に処理します。独自のプールを使う場合は、トークンを手動でキャッシュするのではなく、接続ファクトリの beforeConnect フックで generate-db-connect-auth-token コマンドを呼び出してください。

トークンは、基となる IAM 認証情報より長くは有効になりません。1 時間のセッションでロールを引き受けた場合、--expires-in の値に関係なく、トークンは最大でも 1 時間で失効します。

データベースロールは最小権限に絞る。 IAM 認証は誰が接続できるかを制御しますが、接続後にそのセッションが何をできるかは制限しません。各アプリケーションのデータベースロールには、必要な最小限の権限だけを付与します。たとえば、読み取り専用のサービスには SELECT を付与し、書き込みアクセスは特定のスキーマやテーブルに限定します。アプリケーションの接続プールで管理者ロールを使うのは避けてください。

手順の詳細は、Authorizing database roles to use SQL in your database を参照してください。

戦略 2: 最大接続有効期間にジッターを設定して接続の失効を防ぐ

Amazon Aurora DSQL には、1 時間という接続の最大有効期間のハード制限があります。接続がこの期間に達すると、アイドル中でもトランザクションの途中でも、サービスはその接続を閉じます。プールはそうなる前に接続をリサイクルする必要があります。

ジッターが重要な理由: プール内のほとんどの接続がほぼ同じタイミング (たとえばアプリケーションの起動時) に作成された場合、それらはほぼ同時に失効します。すると新しい接続リクエストが一斉に集中する「thundering herd」が発生し、1 秒あたり 100 接続のレート制限を超えることがあります。最大有効期間にランダムなジッターを加えると、接続のリサイクルが時間的に分散されます。

// Go (pgxpool) — Use native per-connection jitter
config.MaxConnLifetime = 55 * time.Minute
config.MaxConnLifetimeJitter = 5 * time.Minute
// Each connection independently gets a lifetime between 55-60 minutes

// Java (HikariCP): maxLifetime already applies per-connection jitter automatically
// Python: use max_lifetime with a randomized offset in your pool factory

期待される結果: 接続のリサイクルが個々の接続にわたって時間的に均等に分散し、新しい接続リクエストが 1 秒あたり 100 のレートクォータを十分に下回った状態を保てます。

戦略 3: プールサイズを最適化する

Amazon Aurora DSQL は多数の同時接続でも高い性能を発揮しますが、スループット、レイテンシー、リソース消費のバランスをとれるようにプールサイズを設定する必要があります。

表 3: 接続プールのサイズ設定パラメータの推奨初期値

パラメータ 推奨初期値 理由
最小プールサイズ 2〜5 接続 低負荷時にリソースを無駄にせず、プールをウォームに保つ
最大プールサイズ アプリケーションインスタンスあたり 10〜20 控えめに始める。アプリケーションインスタンスを追加してスケールアウトする
接続タイムアウト 30 秒 トラフィックが多いイベント中に、DSQL の接続バーストキュー (100/秒) を消化できるようにする
アイドルタイムアウト 無効 (または MaxConnLifetime に合わせる) アイドル接続にコストはかからない。開いたままにしておけば、再接続時の不要な TLS/認証の負荷を避けられる

Amazon CloudWatch で DPU (Distributed Processing Unit) の消費を監視し、容量の追加が必要なタイミングを把握します。トラフィックの急増が予想される場合 (スケジュールされたバッチジョブやマーケティングキャンペーンなど) は、事前にプールをウォームアップしておきます。インスタンスあたりの接続数を増やすのではなく、小さめのプールを持つアプリケーションインスタンスを増やして水平方向にスケールアウトします。Amazon Aurora DSQL は、短期的な負荷増加には垂直スケーリングで、長期的な変動にはフリート全体の水平スケーリングで対応します。クラスターあたり 10,000 同時接続のクォータを活用し、必要であれば AWS Support に連絡して上限を引き上げてください。

期待される結果: ワークロードに合わせてプールが適切にサイズ設定され、接続の負荷とプール枯渇エラーの両方を最小限に抑えられます。

戦略 4: マルチリージョンでのプーリングの考慮事項

Amazon Aurora DSQL のマルチリージョン active-active デプロイを使うグローバル分散アプリケーションでは、接続プーリングにもう一段の検討が必要です。

このサービスのマルチリージョンアーキテクチャでは、SQL の実行、読み取り、書き込みのスプーリングをクライアントのリージョン内でローカルに処理します。リージョン間の通信は、コミット時に Adjudicator と Journal のレプリケーションプロトコルを通じてのみ発生します。マルチリージョンモードでも読み取りはローカルなので、読み取り専用トランザクションはリージョン間のレイテンシーなしで完了します。読み書きトランザクションでリージョン間レイテンシーが発生するのは COMMIT 時のみで、実行した SQL ステートメントの数に関係なく、リージョン間のラウンドトリップ約 1〜1.5 回分です。各ステートメントごとにラウンドトリップが発生する転送ベースの設計と比べ、レイテンシーを削減できます。リージョンの選択も重要です。接続性の良いリージョンの組み合わせほどコミットレイテンシーが低く、地理的に離れた組み合わせほどそれに比例して大きくなります。

マルチリージョンでのプーリングでは、リージョンごとに個別の接続プールを作成し、それぞれローカルの Amazon Aurora DSQL エンドポイントを指すようにします。トークン更新のロジックがリージョンごとの IAM エンドポイントを考慮していることを確認してください。アプリケーションは active-active アクセスを前提に設計します。このサービスにはプライマリリージョンという概念がなく、各リージョンは対称的に動作します。

期待される結果: リージョン間レイテンシーはコミット時にのみ発生し、読み取りはローカルリージョンの速度で完了します。

オブザーバビリティ: データベースだけでなく接続プールも監視する

Aurora DSQL の CloudWatch メトリクス (TotalTransactions や CommitLatency など) はデータベースレベルの挙動を示しますが、接続プールがボトルネックかどうかまではわかりません。プールの健全性を把握するには、アプリケーション側に計測を組み込む必要があります。次に挙げるクライアント側のメトリクスが、接続プーリングの問題を捉える重要なシグナルです。

アプリケーションに組み込むべきメトリクス

メトリクス 示す内容 アラートのしきい値 発生時のアクション
プール取得レイテンシー (.get() の所要時間) プールから接続を取得するまでアプリケーションが待つ時間 P95 > 500 ms プールが飽和している。最大プールサイズを増やすか、アプリケーションインスタンスをスケールアウトする
アクティブ接続数 現在トランザクションを実行中の接続数 最大プールサイズに張り付いている すべての接続が使用中。プールサイズを増やすか、トランザクションの実行時間を短くする
アイドル接続数 プール内で使われていない接続の数 0 のまま張り付いている トラフィックのバーストに対する余裕がない。最小プールサイズを増やすか、急増が予想される前にウォームアップする
リクエストの同時実行数 対 プールサイズ 処理中の同時リクエスト数と最大プールサイズの比率 同時実行数が最大プールサイズの 80% を超える プール枯渇に近づいている。スケールアウトするか、プールサイズを増やす
プール枯渇イベント 利用可能な接続を待って接続リクエストがタイムアウトした回数 > 0 直ちに対応が必要。プールサイズを増やす、トランザクションの実行時間を短くする、またはアプリケーションインスタンスを追加する
接続作成レート プールが 1 秒あたりに開く新規接続の数 100/秒 に近づく Aurora DSQL のレート制限に達するリスクがある。ジッターを加える、インスタンスの起動をずらす、または最小プールサイズを増やして接続の入れ替わりを減らす

ほとんどの接続プールライブラリは、これらの統計をネイティブに公開しています。

  • Java (HikariCP): HikariPoolMXBean を使って getActiveConnections()getIdleConnections()getThreadsAwaitingConnection()getTotalConnections() にアクセスします。Micrometer 経由で CloudWatch やアプリケーションパフォーマンスモニタリング (APM) ツールにエクスポートします。
  • Python (psycopg ConnectionPool): pool.get_stats() を使います。pool_minpool_maxpool_sizepool_availablerequests_waitingrequests_num を返します。
  • Node.js (node-postgres): pool.totalCountpool.idleCountpool.waitingCount に直接アクセスします。一定間隔で、またはリクエストごとに出力します。
  • Go (pgxpool): pool.Stat() を使います。AcquireCount()AcquiredConns()IdleConns()TotalConns()AcquireDuration() を提供します。

これらはカスタム CloudWatch メトリクスとして発行するか (PutMetricData API またはログ内の CloudWatch Embedded Metric Format を使用)、既存の APM ツール (AWS X-Ray、Datadog、Prometheus/Grafana など) に送信します。

補足: Aurora DSQL の CloudWatch メトリクス

主要なシグナルはクライアント側のメトリクスであるべきですが、プールサイズを決めるうえで役立つ Aurora DSQL のメトリクスが 1 つあります。

メトリクス プーリングに役立つ理由
TotalTransactions プール取得レイテンシーと関連付けて見ます。トランザクションが増えているのに取得レイテンシーが横ばいなら、プールサイズは適切です。両方が増えているなら、容量の追加が必要です。

よくある接続プールの問題のトラブルシューティング

アラートが発生したら、この表を使って最もよくある障害シナリオを診断し、対処してください。

表 5: よくある Amazon Aurora DSQL 接続プール問題のトラブルシューティングガイド

症状 考えられる原因 診断ステップ 対処
プール枯渇 (接続タイムアウトエラー) トラフィックに対して maxPoolSize が低すぎる アクティブ接続とアイドル接続を比較して監視する 最大プールサイズを増やすか、アプリケーションインスタンスを追加する
約 55 分後の認証失敗 トークンの失効が処理されていない トークン更新のログを確認する 公式コネクタを使っているか確認する。MaxConnLifetime の設定を確認する
新規接続レートのエラー 起動時の thundering herd 接続作成のタイムスタンプを確認する MaxConnLifetime にジッターを加える。アプリケーションインスタンスの起動をずらす

クイックリファレンス: 主要な制限と設定

接続の有効期間、トランザクションタイムアウト、接続レート、同時実行数などの最新の制限については、Cluster quotas and database limits を参照してください。

まとめ

本記事では、Amazon Aurora DSQL のトランザクション単位のプーリングモデルが従来の PostgreSQL とどう違うかを解説しました。また、コネクタの選択からマルチリージョンでのプーリングまで、アプリケーションの性能と回復力を保つための具体的な 4 つの戦略も紹介しました。PgBouncer や pgpool-II のようなデータベース側のプロキシは使わないでください。このサービスはトランザクション単位の多重化をネイティブに処理します。マルチリージョンのデプロイでは、リージョン間レイテンシーがコミット時にのみ発生することを覚えておいてください。トランザクションはローカルの読み書きを活かせるように設計しましょう。

これらの戦略に従えば、アプリケーションは Amazon Aurora DSQL の自動スケーリング、強整合性、高可用性を最大限に活用できます。接続の負荷を最小限に抑え、大規模でもレイテンシーを予測可能に保てます。

著者について

Tejas Dubey

Tejas Dubey

Tejas は、Tejas は AWS のテクニカルアカウントマネージャーで、エンタープライズのお客様が回復力とセキュリティに優れ、AI 対応のクラウドアーキテクチャを構築できるよう支援しています。新しいテクノロジーを実際のビジネス価値に変えることに情熱を注いでいます。仕事を離れているときは、息子を追いかけたり、ピックルボールをしたり、最新のテクノロジーをいじったりしています。

Dhvani Shah

Dhvani Shah

Dhvani は、Dhvani はテクニカルアカウントマネージャーで、クラウドの回復力、セキュリティ、生成 AI の導入に関する戦略的なガイダンスでエンタープライズのお客様を支援しています。デジタルトランスフォーメーションを加速する、スケーラブルで安全なソリューションの設計を支援しています。仕事以外では、旅行やバドミントン、家族と過ごす時間を楽しんでいます。


この記事は Kiro が翻訳を担当し、Solutions Architect の Arisa Izuno がレビューしました。