PostgreSQL接続プール枯渇の実戦対処:再発防止までつなげる調査・改善プレイブック

PostgreSQL接続プール枯渇の実戦対処:再発防止までつなげる調査・改善プレイブック 本番障害でよくあるのが、too many clients already や remaining connection slots are reserved です。アプリ側から見ると「急にDBに繋がらない」、ユーザー側から見ると「全機能が遅い・失敗する」という最悪の体験になります。 厄介なのは、接続枯渇が「DBサーバー性能不足」だけで起こるわけではない点です。リーク、タイムアウト設定、長時間トランザクション、プールサイズ不整合など、複数要因が重なって起きます。 この記事では、接続枯渇に対して 発生時の初動 → 根本原因の特定 → 恒久対策 の順で、手順を実務レベルでまとめます。 1. まず初動:サービス継続を優先する 障害対応では、完璧な原因究明より「止血」が先です。以下を順番に実施します。 直近リリース有無を確認(機能フラグ含む) アプリの接続数・待機数・エラー率を確認 DB側で pg_stat_activity を取得 長時間実行クエリを必要に応じて停止 一時的にアプリ Pod 数を制限して雪だるま増幅を止める pg_stat_activity の基本クエリ: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT pid, usename, application_name, client_addr, state, wait_event_type, wait_event, now() - query_start AS query_duration, now() - xact_start AS xact_duration, left(query, 120) AS query_head FROM pg_stat_activity WHERE datname = current_database() ORDER BY xact_start NULLS LAST, query_start NULLS LAST; ここで見るべきは、state='idle in transaction' と異常に長い xact_duration です。これがあるとコネクションを握ったまま解放されず、枯渇の引き金になります。 ...

March 5, 2026 · 2 min · AI2CORE 編集部

Redisキャッシュスタンピード対策ガイド:高負荷時にDBを守る設計と実装

Redisキャッシュスタンピード対策ガイド:高負荷時にDBを守る設計と実装 Redis を使っていても、ピークトラフィック時に DB が突然落ちることがあります。原因の多くはキャッシュスタンピードです。人気キーの TTL が同時に切れると、大量リクエストが一斉に DB へ流れ、接続プールが飽和します。 「Redis を入れたのに遅い」「ピーク時だけ 500 が増える」という現象は、このパターンで説明できることが非常に多いです。 本記事では、キャッシュスタンピードを実運用で防ぐために、設計原則・実装パターン・監視方法を順に解説します。 1. キャッシュスタンピードとは何か 典型シナリオ: 商品ランキング API が ranking:daily を Redis に 300 秒で保存 300 秒後、人気時間帯にキー期限切れ 同時に 1000 リクエストが miss 1000 回 DB 集計が走ってレイテンシ急増 このとき Redis 自体は正常でも、背後の DB が壊れます。つまり、問題はキャッシュ障害ではなく「再生成の同時実行制御」です。 2. 防御の基本は三層構え スタンピード対策は単一施策では不十分です。次の三層を組み合わせると安定します。 同時再生成の抑制(singleflight / 分散ロック) 期限切れの分散(TTL ジッター) 期限切れ後の挙動制御(stale-while-revalidate) 3. パターン1: singleflight で同時再生成を止める 同一キーの miss が同時発生しても、1 リクエストだけ再生成し、他は待つ設計です。 TypeScript 例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const inflight = new Map<string, Promise<string>>(); async function getOrCompute(key: string, ttlSec: number, compute: () => Promise<string>) { const cached = await redis.get(key); if (cached) return cached; if (!inflight.has(key)) { const p = (async () => { try { const value = await compute(); await redis.set(key, value, { EX: ttlSec }); return value; } finally { inflight.delete(key); } })(); inflight.set(key, p); } return await inflight.get(key)!; } 単一プロセスではこれで十分ですが、複数インスタンス構成では分散ロックも必要です。 ...

March 2, 2026 · 2 min · AI2CORE 編集部

PostgreSQLデッドロック調査プレイブック:再現・可視化・恒久対策までの実践手順

PostgreSQLデッドロック調査プレイブック:再現・可視化・恒久対策までの実践手順 本番運用で厄介なのは、エラーが「たまに」しか出ない障害です。PostgreSQL のデッドロックはその代表で、発生頻度は低くてもビジネス影響が大きいことが多いです。決済や在庫更新で発生すると、リトライが雪だるま式に増え、アプリ全体の遅延を引き起こします。 本記事では、デッドロック発生時に現場でそのまま使える手順を、初動対応・再現・恒久対策の順で整理します。 1. まず理解すべき前提 デッドロックは「どちらかが悪い」ではなく、ロック順序が循環したときに必ず起きる現象です。PostgreSQL は循環を検出すると、どちらか一方のトランザクションを強制中断します。 典型的な症状: ERROR: deadlock detected API の一部がランダムに 500 を返す リトライ実装により DB 負荷が上振れ ここで重要なのは、単純なタイムアウトと混同しないことです。タイムアウトは待ち時間超過、デッドロックは循環待ちです。対策が違います。 2. 初動でやること(5〜15分) 2-1. エラーログの採取 まず、DB 側ログに詳細を出す設定があるか確認します。 1 2 3 SHOW log_lock_waits; SHOW deadlock_timeout; SHOW log_min_error_statement; 推奨設定(本番): log_lock_waits = on deadlock_timeout = '1s' log_min_error_statement = error deadlock_timeout を短めにすることで、待ちが長引いたケースの追跡がしやすくなります。 2-2. 現在のロック状況を確認 1 2 3 4 5 6 7 8 9 10 11 12 13 14 SELECT a.pid, a.usename, a.application_name, a.state, a.query, l.locktype, l.mode, l.granted, a.query_start FROM pg_stat_activity a JOIN pg_locks l ON a.pid = l.pid WHERE a.datname = current_database() ORDER BY a.query_start; 見るべき点は「長く生きているトランザクション」と「granted = false が連鎖している箇所」です。 ...

March 2, 2026 · 2 min · AI2CORE 編集部

Python asyncioバックプレッシャー設計:落ちない非同期バッチを作る実装パターン

Python asyncioバックプレッシャー設計:落ちない非同期バッチを作る実装パターン asyncio は速く作れる一方で、負荷が上がった瞬間に崩壊する設計を作りやすいという側面があります。特に「処理待ちが無限に積み上がる」「外部API遅延で全体が詰まる」「リトライ嵐でさらに遅くなる」は典型的です。 本記事では、非同期ワーカーを本番運用する前提で、バックプレッシャーを実装に落とす方法を解説します。単なる概念ではなく、すぐ使えるコード断片を中心に進めます。 1. なぜバックプレッシャーが必要か バックプレッシャーは「これ以上は受けない」仕組みです。これがない設計は、ピーク時に次の順で壊れます。 入力が処理速度を超える キューが無限増加してメモリ圧迫 GC増加でスループット低下 タイムアウト増加→リトライ増加 システム全体が雪崩れる つまり、受けすぎないことは性能ではなく可用性の話です。 2. 基本設計:3つの制限を必ず入れる 2-1. キュー上限(bounded queue) 1 2 3 4 import asyncio QUEUE_MAX = 1000 queue: asyncio.Queue[dict] = asyncio.Queue(maxsize=QUEUE_MAX) maxsize なしは原則禁止です。業務要件で「捨てられない」場合でも、無限キューより「受け付け停止 + 明示エラー」のほうが復旧可能です。 2-2. 同時実行数上限(semaphore) 1 2 3 4 5 6 CONCURRENCY = 20 semaphore = asyncio.Semaphore(CONCURRENCY) async def guarded_call(fn, *args, **kwargs): async with semaphore: return await fn(*args, **kwargs) CPU でも I/O でも、同時実行数に上限を持たせると遅延の尾が短くなります。 ...

March 1, 2026 · 2 min · AI2CORE 編集部

WebAssemblyとWASI:ブラウザを越えてサーバーサイドへ進出するWasmの可能性

WebAssemblyとWASI:ブラウザを越えてサーバーサイドへ進出するWasmの可能性 はじめに 「コンテナの起動が遅い…」「開発環境と本番環境の差異でまたハマった…」「マイクロサービスのイメージサイズが肥大化してリソースを圧迫している…」 もしあなたがサーバーサイド開発に携わっているなら、このような悩みに一度は直面したことがあるのではないでしょうか。Dockerをはじめとするコンテナ技術は、現代のアプリケーション開発に革命をもたらしましたが、その一方で、起動時間のオーバーヘッド、リソース消費、セキュリティの懸念といった新たな課題も生み出しています。 もし、コンテナよりも高速に起動し、軽量で、かつ強力なセキュリティサンドボックスを持つ技術があるとしたらどうでしょう?しかも、特定のOSやCPUアーキテクチャに依存せず、真のポータビリティを実現できるとしたら? この記事では、その答えとなりうる「WebAssembly (Wasm)」と、そのエコシステムをブラウザの外へ拡張する「WASI (WebAssembly System Interface)」について、深く掘り下げていきます。単なる技術解説に留まらず、Wasmがなぜ「Dockerの次」とまで言われるのか、その理由とサーバーサイドでの具体的な活用法、そして未来の可能性までを、コード例を交えながら徹底的に解説します。この記事を読み終える頃には、あなたはWasmがサーバーサイドコンピューティングの新たなパラダイムを切り拓く可能性を確信しているはずです。 なぜ今、サーバーサイドWasmが重要なのか? WebAssemblyは、もともとWebブラウザ上でネイティブコードに近いパフォーマンスを実現するために生まれました。JavaScriptの代替または補完として、C/C++/Rustなどで書かれたコードを高速に実行できるバイナリフォーマットとして注目を集め、既に多くのWebアプリケーションで活用されています。 しかし、Wasmの持つ4つの主要な特性は、ブラウザの世界に留めておくにはあまりにも魅力的でした。 高速 (Fast): ネイティブに近い速度で実行可能な、効率的なバイナリフォーマット。 安全 (Secure): デフォルトでメモリ安全なサンドボックス内で実行され、ホストシステムへのアクセスは明示的に許可された機能に限定される(Capability-based security)。 ポータブル (Portable): 特定のCPUアーキテクチャやOSに依存しない。Wasmランタイムがあればどこでも同じように動作する。 コンパクト (Compact): バイナリフォーマットは非常に小さく、ネットワーク経由での配信やストレージ効率に優れる。 これらの特性は、サーバーサイドやエッジコンピューティングが抱える課題、特にコンテナ技術のペインポイントを解決する大きな可能性を秘めていました。 コンテナ技術が抱える課題 Dockerは素晴らしい技術ですが、いくつかのトレードオフがあります。 起動時間とオーバーヘッド: コンテナは軽量な仮想マシンと言われますが、それでもアプリケーションの起動にはOSのプロセスを立ち上げ、ファイルシステムをマウントするなど、数秒単位の時間がかかります。これがFaaS(Function as a Service)などのコールドスタート問題の一因となります。 リソース消費: 各コンテナは、アプリケーションの依存ライブラリだけでなく、OSのユーザーランドの一部を含むレイヤ化されたイメージを持ちます。これにより、イメージサイズが数百MBから数GBに達することも珍しくなく、ディスク容量やメモリを消費します。 セキュリティ: コンテナはNamespaceやCgroupsといったLinuxカーネルの機能を利用してプロセスを分離しますが、ホストOSのカーネルを共有しています。そのため、カーネルの脆弱性がコンテナの分離を破壊するリスクが常に存在します。 ポータビリティの限界: 「Linuxコンテナ」はLinuxカーネル上で動作することを前提としています。WindowsやmacOSでDockerを使う場合、内部的にはLinuxの仮想マシンが動作しており、真のクロスプラットフォームとは言えません。 これらの課題に対し、WebAssemblyは全く新しいアプローチを提示します。OS全体を仮想化するのではなく、個々のアプリケーションプロセスを、OSから完全に独立した軽量なサンドボックス内で実行するのです。 しかし、ここで一つの大きな壁がありました。オリジナルのWebAssemblyは、ブラウザのJavaScript APIと連携することしか想定されていなかったのです。ファイルシステムへのアクセス、ネットワーク通信、現在時刻の取得といった、サーバーサイドアプリケーションに必須の機能が標準化されていませんでした。 この問題を解決するために登場したのが、WASI (WebAssembly System Interface) です。 WASI:WebAssemblyと世界をつなぐ架け橋 WASIは、WebAssemblyモジュールがホスト環境(ブラウザ、サーバー、エッジデバイスなど)のシステム機能へアクセスするための、標準化されたAPIです。WASIを「WebAssemblyのためのOSインターフェース」あるいは「POSIXのようなもの」と考えると分かりやすいでしょう。 WASIの登場により、Wasmはついにブラウザという揺りかごから飛び立ち、サーバーサイドという広大な大地でその真価を発揮する準備が整いました。 WASIの仕組み WASIは、Capability-based security(権限ベースのセキュリティ)モデルを基本としています。これは「プログラムは、明示的に与えられた権限(ファイルディスクリプタ、ソケットなど)しか利用できない」という原則です。 以下の図は、Wasm/WASIアプリケーションがOSとどのように対話するかを示しています。 +--------------------------+ | Your Application Code | (e.g., Rust, Go, C++) | (Business Logic) | +--------------------------+ | (Compile Time) v +--------------------------+ | Wasm Module (.wasm) | | (contains WASI imports) | +--------------------------+ | (Runtime) +--------------------------+ <-- Wasm Sandbox Boundary | Wasm Runtime | | (e.g., Wasmtime, Wasmer) | +------------+-------------+ | (WASI Implementation) v +--------------------------+ | Host OS | | (Linux, macOS, Windows) | +--------------------------+ アプリケーションコード: 開発者は使い慣れた言語(Rust, Go, C++など)でコードを書きます。 コンパイル: 専用のツールチェイン(例: wasm32-wasi ターゲット)を使い、Wasmモジュール(.wasm ファイル)にコンパイルします。この時、ファイルI/Oなどのシステムコールは、WASIのimport関数呼び出しに変換されます。 実行: Wasmランタイムが .wasm ファイルをロードします。 権限の付与: ランタイムを起動する際に、「このディレクトリへの読み込みを許可する」「このポートでの待ち受けを許可する」といった権限を明示的に与えます。 システムコール: WasmモジュールがWASI関数(例: fd_write)を呼び出すと、ランタイムがそれを捕捉し、与えられた権限の範囲内でホストOSの対応するシステムコール(例: write)を実行します。 この仕組みにより、Wasmモジュールは悪意のあるコードを含んでいたとしても、許可されていないファイルにアクセスしたり、意図しないネットワーク接続を確立したりすることは原理的に不可能です。これは、コンテナよりも遥かにきめ細かく、強力なセキュリティモデルと言えます。 ...

February 24, 2026 · 3 min · AI2CORE 編集部