接続プールとは

DB接続をあらかじめ一定数作成しておき、リクエストごとに「借りて返す」仕組み。 Railsがデフォルトで採用している方式。

# Rails database.yml
production:
  pool: 5
  checkout_timeout: 5

比較

  接続プール(Rails等) リクエストごと生成・破棄
接続コスト 初回のみ。以降は借りる/返すだけ 毎回 connect + close
同時接続数 制御できる(pool_size=5 等) リクエスト数に比例して増える
メモリ使用 一定(プールサイズ分) リクエストに比例
実装の複雑さ プール管理が必要 シンプル
障害時の影響 プール内の不良接続が再利用されるリスク 毎回新規なので不良接続が残らない
向いている DB PostgreSQL, MySQL(接続が重い) SQLite(接続が軽い)

接続プールのメリット

接続確立が重い DB で効く。PostgreSQL や MySQL は接続時に TCP ハンドシェイク、認証、セッション初期化が走るので、毎回やると数十ms かかる。

接続プールのデメリット

  • プール枯渇: 全接続が貸出中だとリクエストがブロックされる
  • コネクションリーク: 返却し忘れるとプールが徐々に枯渇する
  • 不良接続: DB 再起動後に古い接続が残って壊れた接続を掴むことがある
  • 設定のチューニング: pool_size を適切に設定しないとパフォーマンスが出ない

DB別の接続コスト

PostgreSQL: ~20-50ms  → プール必須
MySQL:      ~10-30ms  → プール推奨
SQLite:     ~0.01ms   → プール不要

SQLiteはファイルをopen()するだけで、TCPもネゴシエーションもないので、プールの管理コストの方が高くつく。

FastAPI でのリクエストごと生成・破棄パターン

def get_db() -> Generator[sqlite3.Connection, None, None]:
    conn = get_connection()   # リクエストごとに新しい接続を作成
    try:
        yield conn            # ルートハンドラに接続を渡す
    finally:
        conn.close()          # レスポンス返却後に必ず接続を閉じる

FastAPIのyield依存関係がライフサイクルを保証する。yield前がセットアップ、yieldの値が注入対象、finallyがクリーンアップ。

PostgreSQLに変える場合(SQLAlchemy + プール)

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine("sqlite:///myweb.db", pool_size=5)
SessionLocal = sessionmaker(bind=engine)

def get_db():
    db = SessionLocal()  # プールから接続を借りる
    try:
        yield db
    finally:
        db.close()       # プールに返す(破棄ではない)

まとめ

  • 接続が重い DB(PostgreSQL, MySQL)→ 接続プール
  • 接続が軽い DB(SQLite)→ リクエストごと生成・破棄
  • Railsがプールをデフォルトにしているのは PostgreSQL/MySQL 前提だから