Aurora / RDS PostgreSQL 13→16・12→16 アップグレード前 必須確認事項(プレフライト チェックリスト)
対応範囲: Aurora PostgreSQL(13→16)と **RDS PostgreSQL(12→16)**の両方。付属スクリプトは dispatcher が Aurora / RDS を自動判定する(
--kind auto)。RDS 固有の差分(DB parameter group・instance endpoint・rds.accepted_password_auth_method・クロスリージョン read replica の Blue/Green 非対応 等)は各章に注記。
作成日: 2026-06-17 担当: SRE(yusaku.ishizawa) 関連ドキュメント:
本書について
- Aurora PostgreSQL(13系→16系)および RDS PostgreSQL(12系→16系)を Blue/Green でメジャーアップグレードする前に必ず確認すべき事項を、実行コマンド・期待値つきでまとめたプレフライト チェックリスト。Aurora/RDS で差が出る箇所は各章に注記する。
- 内容は eligibility-verification(staging)で実機確認した #13098 の事前調査が正本。各項目に eligibility での実機結果を併記する。
- サービス横断で使える。対象サービスごとに同じチェックを実行し、結果を Issue に記録すること。
- 実際の作業手順(Clone 作成・CPG 付け替え・Switchover 等)は Runbook を参照。
自動実行(スクリプト)
📦 スクリプト/skill は横断ツールとして別 PR で管理: terraform_for_aws #2209(Issue: mental-online-karte #13841・#13044 配下)。本 doc とツール PR の両方が develop にマージされると、下記パスのスクリプトが揃う。
本チェックリストの A〜H は、付属スクリプトで一括実行できる(読み取り専用・手動実行を最小化)。Aurora / RDS PostgreSQL の両方に対応(dispatcher が自動判定)。
docs/sre/scripts/pg16-preflight-check.sh \
--target <cluster-or-instance-id> [--kind auto|aurora|rds-instance] [--secret <name>] [--dbname <db>] \
--bastion <到達可能な i-xxxx> --profile <profile> --region <region> [--target-version 16.13]スクリプト構成:
pg16-preflight-check.sh(dispatcher・--kind autoで Aurora/RDS 判定)pg16-preflight-aurora.sh(Aurora・DB Cluster 単位)/pg16-preflight-rds.sh(RDS・DB Instance 単位)lib/pg16-preflight-common.sh(共通関数)/lib/pg16-preflight-db-common.sql(共通 DB チェック SQL)SSM トンネル起動 → psql で A〜C/G の SQL 実行 →
describe-*/digで D/E/H → PASS/WARN/FAIL/INFO レポート+MATRIX:1行(kind/risk/blocker/next付き。集めると #13044 のマトリクスが埋まる)。Aurora と RDS の違い: Aurora=cluster 単位・cluster endpoint が正常・Global DB/Reader 確認/RDS=instance 単位・instance endpoint が正常・MultiAZ/Read Replica/storage 確認・target version は
--engine postgres。SET default_transaction_read_only=onで二重ガード。パスワードは Secrets Manager→PGPASSWORD(非表示)。トラブル時は手動手順(下記 A〜H)で個別確認。dbname は secret の
DATABASE_URLから自動導出(クラスタ名からの推定は不正確なため。例:online-karte-serviceの DB 名はonline_karte)。DB 系チェックは「指定 bastion から対象 RDS に到達できる(SG 許可・経路あり)」ことが前提。到達不可なら DB 系は自動スキップ(WARN・誤 FAIL は出さない)=到達可能な
--bastionを指定する。インフラ系(engine/backup/preload/Global/Proxy/DNS/接続方式)はトンネル不要で常に取得できる。app 面(ORM/生SQL/バッチ)はサービスの repo が必要なため本スクリプト対象外(F 参照)。
以下は手動実行・トラブルシュート用の個別コマンド。
前提(接続方法)
- 踏み台(bastion)から SSM ポートフォワードで対象 Aurora に接続する。
- DB 接続情報は Secrets Manager から取得(
DB_USERNAME/DB_PASSWORD)。パスワードはコマンドや出力に出さずPGPASSWORD経由で扱う。
# 1) トンネル(別ターミナル)。<bastion> / <cluster-endpoint> は対象に合わせる
aws ssm start-session --target <bastion-instance-id> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["<cluster-endpoint>"],"portNumber":["5432"],"localPortNumber":["15432"]}' \
--region $R --profile $P
# 2) psql 接続(user は Secret の DB_USERNAME)
psql "host=localhost port=15432 dbname=<db_name> user=<DB_USERNAME> sslmode=require"A. DB 内部チェック(Blue/Green 阻害要因)
A-1. 主キー(PK)の無いテーブル
論理レプリケーションは PK(または REPLICA IDENTITY FULL)が必要。0行が期待値。
SELECT format('%I.%I', n.nspname, c.relname) AS no_pk
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind='r' AND n.nspname NOT IN ('pg_catalog','information_schema')
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE i.indrelid=c.oid AND i.indisprimary);- 期待値: 0行。PK なしテーブルがあれば PK 追加 or
REPLICA IDENTITY FULLを検討。 - eligibility 実機: 0行(
ocr_results/prompt_definitionsとも PK あり)。
A-2. Unlogged / prepared_xacts / replication slot / Large Object / matview
SELECT (SELECT count(*) FROM pg_class WHERE relpersistence='u' AND relkind='r') AS unlogged,
(SELECT count(*) FROM pg_prepared_xacts) AS prepared_xacts,
(SELECT count(*) FROM pg_replication_slots) AS repl_slots,
(SELECT count(*) FROM pg_largeobject_metadata) AS large_objects,
(SELECT count(*) FROM pg_matviews) AS matviews;- 期待値: すべて 0。
- eligibility 実機: unlogged=0 / prepared_xacts=0 / repl_slots=0 / large_objects=0 / matviews=0。
⚠️ 外部CDC(Datastream / DMS / 外部 logical replication)連携サービスは要注意(
repl_slots > 0、またはpg_publicationが存在する場合)。
- Blue/Green では外部レプリ用の replication slot / publication は新 Green(PG16)へ引き継がれないため、Switchover 後に再作成・再開が必要。
- slot を作り直すとレプリ位置(LSN)が失われ、CDC ツールが バックフィル(全テーブル再読込) を始める場合がある。本番はデータ量が大きいとメンテ枠内に終わらないおそれ=停止/ダウンタイム扱いになりうる。
- 事前判定SQL(read-only・どのDBでも共通): 下記で外部CDCの有無を確定する。
sqlいずれかに行が出る/SELECT slot_name, plugin, slot_type, active, restart_lsn FROM pg_replication_slots; -- logical slot があるか SELECT pubname, puballtables, pubinsert, pubupdate, pubdelete FROM pg_publication; -- 購読リスト(FOR ALL TABLES 等) SELECT application_name, state, client_addr, sync_state FROM pg_stat_replication; -- 配信中の walsender 接続 SHOW rds.logical_replication; -- CDC サービスは on(logical off のサービスは外部CDC無しの可能性が高い)onなら外部CDCありとして下記の対応へ。eligibility は全て 0 行・off= 非該当。- 対応:
- 対象サービスが外部CDCを使うかを事前判定(上記SQL + IaC・連携設定)。
- staging で BG→Switchover→slot/publication 再作成を実施し、バックフィルの有無・所要時間を実測する。
- 枠内に収まらなければ枠設計を見直す(別枠/事前に CDC を一時停止→切替後に再作成・再開 等のコンティンジェンシー)。
- 切替中・切替後の監視経路(CDC ツールのエラー通知チャンネル・管理コンソール)を用意し、停止/エラー/ラグを確認。
- サービス固有値(CDC プロジェクト名・監視チャンネル・slot/publication 名・staging 実測結果・手順)は
db-upgrade/aurora-pg16/services/<service>.mdに記載する(例: online-karte は GCP Datastream を利用)。- eligibility: 外部CDC無し(
repl_slots=0/ publication 無し)=本項は非該当。
A-3. 長時間トランザクション / 進行中の重い DDL(実施直前)
SELECT pid, state, now() - xact_start AS xact_age, left(query,80) AS query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL AND state <> 'idle'
ORDER BY xact_age DESC LIMIT 5;- 期待値: 長時間 tx / 進行中 DDL が無いこと(Blue/Green 作成・Switchover を妨げる)。
A-4. サポートされない reg* データ型
regproc / regprocedure / regoper / regoperator / regconfig / regdictionary 型のカラムは pg_upgrade 非対応。0 期待。
SELECT count(*) FROM pg_class c, pg_namespace n, pg_attribute a
WHERE c.oid = a.attrelid AND NOT a.attisdropped
AND a.atttypid IN (
'pg_catalog.regproc'::pg_catalog.regtype,
'pg_catalog.regprocedure'::pg_catalog.regtype,
'pg_catalog.regoper'::pg_catalog.regtype,
'pg_catalog.regoperator'::pg_catalog.regtype,
'pg_catalog.regconfig'::pg_catalog.regtype,
'pg_catalog.regdictionary'::pg_catalog.regtype)
AND c.relnamespace = n.oid
AND n.nspname NOT IN ('pg_catalog', 'information_schema');- 期待値: 0。
- eligibility 実機: 0。
A-5. 無効なデータベース(invalid database)
SELECT datname FROM pg_database WHERE datconnlimit = -2;- 期待値: 空(
datconnlimit=-2は drop 途中などの無効 DB)。 - eligibility 実機: 0行。
A-6. template0 / template1 の状態
SELECT datname, datistemplate FROM pg_database WHERE datname IN ('template0','template1');- 期待値: 両方
datistemplate = t。 - eligibility 実機: template0=t / template1=t。
A-7. パーティション / FDW / イベントトリガ(BG 挙動に影響)
-- パーティション親テーブル(BG 中は新規パーティション作成=CREATE TABLE が複製されない。pg_partman 等の自動作成にも注意)
SELECT n.nspname, c.relname FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE c.relkind='p' AND n.nspname NOT IN ('pg_catalog','information_schema') ORDER BY 1,2;
-- FDW: 外部サーバ(BG では endpoint 名で設定必須・IP直指定不可)
SELECT srvname, srvoptions FROM pg_foreign_server;
SELECT extname FROM pg_extension WHERE extname IN ('postgres_fdw','dblink');
-- イベントトリガ(DDL 発火。Green は read-only のため複製・整合に影響しうる)
SELECT evtname, evtevent, evtenabled FROM pg_event_trigger;- 判定:
- パーティション親があれば、BG 期間中は新規パーティションを作らない(pg_partman 等の自動 DDL を停止・凍結。既存パーティション/データは複製される)。
- FDW 外部サーバがあれば、
srvoptionsの host が IP 直指定でなく endpoint 名であることを確認(Switchover 後も有効にするため。AWS 公式の BG 制限)。 - イベントトリガがあれば内容を確認(DDL 連動処理。多くは影響小だが要認識)。
- eligibility 実機: いずれも該当なし(partition 0 / FDW 0 / event trigger 0)。
B. Extension(拡張)
-- インストール済み拡張とそのバージョン
SELECT extname, extversion FROM pg_extension ORDER BY extname;
-- インストール済み拡張の installed_version と(稼働中バージョンでの)default_version
-- installed_version < default_version の拡張は、アップグレード後に ALTER EXTENSION ... UPDATE が必要
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
ORDER BY name;- 確認: PostGIS / pg_repack / pg_partman / pg_cron / pgaudit 等の有無。
- 上記2つ目で、各拡張の installed_version と default_version の差を把握(差があれば事後に
ALTER EXTENSION ... UPDATE対象)。- eligibility 実機(2026-06): installed は
plpgsql 1.0 / 1.0のみ・drift なし(pg_available_extensions総数は 93件=インストール可能な拡張。本クエリはinstalled_version IS NOT NULLでインストール済みのみに絞っている)。 - ⚠️ source(PG13)で実行すると PG13 の default_version が出る。ターゲット(PG16)での実際の既定版・更新要否は Green(PG16)作成後に確認する(→ Clone Runbook 8-3/8-4)。本チェックは「どの拡張が入っているか」の事前棚卸しが主目的。
- PostGIS は事前更新、pg_repack は事前 DROP→事後再作成、pg_partman / pg_cron は preload 必須(PG16 ターゲット CPG に含める)等、拡張ごとに事前/事後対応が異なる(→ Runbook 8-8)。
- eligibility 実機(2026-06): installed は
- eligibility 実機:
plpgsql 1.0のみ(partman/cron/postgis/pgaudit/pg_repack なし)=拡張起因の追加対応なし。
pg_stat_statements は別途
CREATE EXTENSIONする(→ Runbook 3-4)。
事前対応が必要な Extension(該当する場合のみ)
下記がインストールされている場合のみ、メジャーアップグレード前後で対応が必要。入っていなければ対応不要。
| Extension | 事前更新 | 備考 |
|---|---|---|
postgis | Yes | 関連(postgis_raster / postgis_topology 等)も全て同時更新 |
postgis_raster | Yes | PostGIS と同時更新 |
postgis_tiger_geocoder | Yes | PostGIS と同時更新 |
postgis_topology | Yes | PostGIS と同時更新 |
address_standardizer | Yes | PostGIS と同時更新 |
address_standardizer_data_us | Yes | PostGIS と同時更新 |
pgRouting | Yes | バージョン間スキップ時に必要 |
pg_repack | — | アップグレード前に DROP、アップグレード後に再作成 |
-- PostGIS 関連の事前更新(全データベースで実行・該当する場合)
ALTER EXTENSION postgis UPDATE;
ALTER EXTENSION postgis_raster UPDATE;
ALTER EXTENSION postgis_tiger_geocoder UPDATE;
ALTER EXTENSION postgis_topology UPDATE;
ALTER EXTENSION address_standardizer UPDATE;
ALTER EXTENSION address_standardizer_data_us UPDATE;
-- pg_repack は事前 DROP(アップグレード後に再作成)
DROP EXTENSION IF EXISTS pg_repack;
-- アップグレード後: CREATE EXTENSION pg_repack;- ⚠️ 複数 DB がある場合は全データベースで実行する。
- preload 必須拡張(
pg_partmanの bgw /pg_cron/pgaudit等)は、PG16 ターゲット CPG のshared_preload_librariesに事前に含める(→ Runbook 8-8 / E)。 - eligibility 実機: 上記 Extension はいずれも未インストール=事前対応不要。
C. PG16 非互換
アップグレードは 13→16(Aurora)/ 12→16(RDS)に直接実行でき、中間バージョン経由は不要。ただし中間版(13/14/15)で変更された内容は一括で適用されるため、それらの非互換も併せて確認する。該当が無ければ対応不要。
影響度: 高(アプリケーションエラーを引き起こす可能性) — 以下を確認(本章 C-1〜C-6):
EXTRACT()の戻り値型がfloat8→numericに変更- 階乗演算子
!!!が削除 - 包含演算子
@~が削除(幾何型・cube 等) - public スキーマの CREATE 権限がデフォルトで剥奪
password_encryptionデフォルトがmd5→scram-sha-256に変更
C-1. アプリSQL(EXTRACT 戻り値型 / 階乗 ! !! / 生SQL)
cd <service>/api
grep -rniE '\b(extract|date_part)\s*\(' src --include='*.ts' | grep -viE '\.spec\.|\.test\.'
grep -rniE 'factorial|\w\s*!!' src --include='*.ts' | grep -viE '\.spec\.|!==|!='
grep -rniE '\$queryRaw|\$executeRaw|queryRawUnsafe|executeRawUnsafe' src --include='*.ts'- 期待値: いずれも 該当なし。
- ⚠️
EXTRACT()の戻り値型はdouble precision(float8)(PG12-13)→numeric(PG16)に変更。float8 前提の計算(乗算・比較・JSON 変換等)が numeric で意図せぬ精度/型エラーになり得る。float8 が必要なら明示キャストする。
-- 変更前(PG12-13): float8 を返す
SELECT EXTRACT(EPOCH FROM now()) * 1000.0;
-- 変更後(PG16): numeric を返す → float8 が必要なら ::float8 を付ける
SELECT EXTRACT(EPOCH FROM now())::float8 * 1000.0;- 階乗演算子
!!!は PG16 で廃止。 - eligibility 実機: すべて該当なし(生SQL も無く Prisma 生成のみ)。
C-2. DB 内のユーザー定義関数 / ビューでの EXTRACT・階乗
SELECT n.nspname, p.proname FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname NOT IN ('pg_catalog','information_schema')
AND p.prosrc ~* '(extract|date_part|factorial|!!)';
SELECT viewname FROM pg_views WHERE definition ~* '(extract|date_part|factorial)';- 期待値: 0件(
pg_catalogの組み込み関数は対象外。ユーザースキーマに限定して見る)。 - eligibility 実機: ユーザースキーマ 0件(組み込み 19件はシステム関数で対象外)。
C-3. public スキーマの CREATE 権限(ACL)
SELECT nspname, nspacl FROM pg_namespace WHERE nspname='public';- 確認: PG16 では新規 DB の public への CREATE 権限既定が変わるが、既存 DB の ACL は維持されるためアップグレードのブロッカーではない。
- eligibility 実機:
PUBLIC=UC(全ユーザーが public に CREATE 可能・PG13 既定維持)。 - 方針: PG16 移行ブロッカーではない。ただし
PUBLIC=UCはセキュリティ観点で別途見直し候補(今回のアップグレード作業では変更しない=作業中に変えるとリスク増)。
C-4. password_encryption / 認証方式
SHOW password_encryption;
SHOW rds.accepted_password_auth_method; -- ※下記注意(Aurora PG13 では露出しない場合あり)- 確認の考え方: PG16 は
password_encryption既定がmd5→scram-sha-256に変わるが、これは新規パスワードのハッシュ方式の話。既存 md5 ハッシュは、サーバが md5 認証を受け付ける限り認証可。RDS PostgreSQL ではrds.accepted_password_auth_method(既定md5+scram=両方受け入れ)で制御。scram単独だと md5 ユーザーが接続不能になり得る。 - ⚠️
rds.accepted_password_auth_methodは Aurora PG13 では確認できなかった(SHOWでunrecognized configuration parameter・cluster/instance パラメータグループにも無し・実機確認済み)。RDS PostgreSQL 向けパラメータで、Aurora 13 では露出しない。 - ⚠️ MD5 ユーザー一覧(
pg_authid WHERE rolpassword LIKE 'md5%')も RDS/Aurora では実行不可(pg_authid/pg_shadowが permission denied)。 - → Aurora では **
password_encryptionの値 +「既存 md5 資格情報で実際に接続できること」**で代替確認する。eligibility 実機:password_encryption=md5/md5 資格情報での psql 接続が成立=ブロッカーでない(SCRAM 化は別タスク)。
C-5. 包含演算子 @ ~ 削除(幾何型・cube 等)
PG14 で組み込み幾何型・cube 等の @(〜に含まれる)/ ~(〜を含む)演算子が削除された。幾何型カラムや cube/seg/postgis を使うサービスのみ該当(~ は正規表現マッチ演算子とは別物)。
-- 幾何型 / cube / seg のカラムが無いこと(0期待)
SELECT n.nspname, c.relname, a.attname, t.typname
FROM pg_attribute a
JOIN pg_class c ON c.oid=a.attrelid
JOIN pg_namespace n ON n.oid=c.relnamespace
JOIN pg_type t ON t.oid=a.atttypid
WHERE c.relkind='r' AND a.attnum>0 AND NOT a.attisdropped
AND n.nspname NOT IN ('pg_catalog','information_schema')
AND t.typname IN ('point','line','lseg','box','path','polygon','circle','cube','seg');
-- cube/seg/postgis 拡張が無いこと(0期待)
SELECT name, installed_version FROM pg_available_extensions
WHERE name IN ('cube','seg','postgis') AND installed_version IS NOT NULL;- 期待値: どちらも 0件(該当型・拡張を使っていれば、
@/~演算子の使用箇所を要修正)。 - eligibility 実機(2026-06): 幾何型/cube/seg カラム 0行・cube/seg/postgis 拡張 0件 =該当なし。
C-6. PL/Python2(plpython2u)など廃止言語
SELECT p.proname FROM pg_proc p JOIN pg_language l ON l.oid=p.prolang
WHERE l.lanname IN ('plpython2u','plpythonu');- 期待値: 0件(PG16 で plpython2u は廃止)。
- eligibility 実機: 0件。
影響度: 中(挙動・運用に影響する可能性。多くは RDS 管理 or 要認識)
C-7. バックアップ関数の名称変更(pg_start_backup → pg_backup_start)
PG15 で pg_start_backup() / pg_stop_backup() が pg_backup_start() / pg_backup_stop() に改名(旧名は削除)。ユーザー定義関数 / アプリで旧名を使っていないか確認する。
-- ユーザースキーマ限定(pg_catalog 組み込みは誤検出になるため除外)。0期待
SELECT n.nspname, p.proname FROM pg_proc p JOIN pg_namespace n ON n.oid=p.pronamespace
WHERE n.nspname NOT IN ('pg_catalog','information_schema')
AND (p.prosrc ILIKE '%pg_start_backup%' OR p.prosrc ILIKE '%pg_stop_backup%');- 期待値: 0件。アプリ側も
pg_start_backupの grep を推奨。 - eligibility 実機: 0件(pg_catalog 除外前は組み込み3件を誤検出するため必ずユーザースキーマ限定で見る)。
C-8. その他の挙動変更(要認識)
| 変更 | 内容 | eligibility への影響 |
|---|---|---|
| TLS 最低バージョン | TLSv1.2 に引き上げ | 接続は実機 TLSv1.3=問題なし。古い TLS のクライアントが無いか確認 |
pg_hba.conf clientcert=1/0 廃止 | verify-ca/verify-full 表記へ | RDS 管理(pg_hba は直接編集不可)=実害なし |
CREATEROLE 権限の制限強化 | CREATEROLE が他ロールを無制限に管理できなくなる | ロール運用を CREATEROLE に依存していないか確認(通常影響小) |
hash_mem_multiplier 既定 1.0 → 2.0 | ハッシュ結合・集約系のメモリ上限が増える | 実機 現状 1。PG16 で 2.0 になりメモリ使用が増える。移行後の監視対象: DBLoad / FreeableMemory / Temp file 使用 / slow query / ハッシュ結合を含む主要クエリ |
D. 接続元・接続先の棚卸し
Connection Pooler(PgBouncer / RDS Proxy)
Connection Pooler とは: アプリと DB の間で接続を再利用(プーリング)する中間サーバ(PgBouncer / RDS Proxy 等)。PostgreSQL は接続1つにつき1プロセスを生成するため、接続数が多い環境で負荷軽減に使う。
なぜ確認が必要か: PG16 はパスワード暗号化の既定が
md5→scram-sha-256に変わる。古いバージョンの PgBouncer 等は SCRAM 非対応でアップグレード後に接続失敗する可能性がある。Pooler を使っていなければ本確認はスキップ可。
確認対象: PgBouncer / RDS Proxy / libpq(アプリのドライバ)/ JDBC(PostgreSQL) のバージョンが SCRAM 対応か。
aws rds describe-db-proxies --region $R --profile $P --query "DBProxies[].DBProxyName" --output text- 接続元: ECS task / worker / batch / cron。DB URL の直書きが無いか(Secrets Manager 参照か)。
- 接続先: cluster (Writer) endpoint を使い、reader/instance endpoint や IP 直指定が無いこと(Switchover で endpoint 名は維持されるため接続先変更は原則不要)。
- eligibility 実機: RDS Proxy / PgBouncer なし(=SCRAM 懸念なし・本セクションは該当なし)/ 接続元は ECS task のみ / cluster Writer endpoint 利用・IP 直指定なし。
接続方式と DNS TTL の確認
| 接続方式 | Switchover 時の挙動 | 確認 / 対応 |
|---|---|---|
| RDS/Aurora エンドポイント(推奨) | 自動切替・アプリ変更不要 | DNS TTL が 5 秒であることを確認 |
| IP アドレス直指定 | 切替後も旧環境に接続し続ける | エンドポイント使用に変更が必要 |
| RDS Proxy 経由 | 自動切替・最も安全 | Proxy のバージョン(SCRAM 対応)を確認 |
確認方法:
# ① 接続文字列(DATABASE_URL)の host を確認(パスワードは出さず host/port/scheme のみ表示)
CREDS=$(aws secretsmanager get-secret-value --secret-id <secret> --query SecretString --output text --region $R --profile $P)
echo "$CREDS" | python3 -c "import sys,json,urllib.parse as u; d=json.load(sys.stdin); p=u.urlparse(d['DATABASE_URL']); print('scheme=',p.scheme,'host=',p.hostname,'port=',p.port)"
unset CREDS
# → host が cluster エンドポイント名であること(`<id>.cluster-xxxx.<region>.rds.amazonaws.com`)。IP(数値)でないこと。
# ② RDS Proxy 有無
aws rds describe-db-proxies --region $R --profile $P --query "DBProxies[].DBProxyName" --output text
# ③ DNS TTL(cluster endpoint・5秒期待)
dig +noall +answer <cluster-endpoint>
# → 行頭の数値(TTL)が 5 であること。Aurora DNS ゾーンの既定値。
# ④ DATABASE_URL の host が実在の cluster endpoint と一致するか突合
aws rds describe-db-clusters --db-cluster-identifier <cluster> --query "DBClusters[0].Endpoint" --output text --region $R --profile $P- ⚠️ クライアント側(アプリ/コネクションプーラ/OS リゾルバ/JVM
networkaddress.cache.ttl)の DNS キャッシュ TTL も 5 秒以下であること(長いと切替後に旧環境を参照)。 - eligibility 実機(2026-06):
scheme=postgresql/ host=eligibility-verification.cluster-ctb7v7xkimcr.ap-northeast-1.rds.amazonaws.com(=cluster endpoint・IP直指定でない)/ RDS Proxy なし / DNS TTL=5(CNAME・A とも)。=推奨方式・自動切替の条件を満たす。
外部 DB クライアントの棚卸し(A-0・Trocco / ReDash / ReTool / BI / バッチ / Airflow 等)
なぜ確認が必要か: アプリ(ECS等)は cluster endpoint 据え置きで自動追従するが、DB を直接参照する外部ツール(Trocco / ReDash / ReTool / Metabase / Looker / Tableau / 各種バッチ / Airflow / DMS / 手元運用スクリプト)は、接続先が instance endpoint・IP 直指定だと Switchover 後に旧 Blue(
-old1)へ残り続ける。管理しきれていないことが多いため、事前棚卸し(CPG 作成・再起動より前・read-only)+切替後の実地確認が要る。⚠️
pg_stat_activityだけでは不十分。Trocco の定期実行ジョブや ReDash の低頻度クエリは「その瞬間に接続していなければ見えない」。DB 実接続の観測(サンプリング)と 設定値の棚卸しの両方を行う。
① 現在の接続元を確認(read-only・CPG 変更前で可)
SELECT now() AS checked_at, datname, usename, application_name, client_addr, client_hostname,
state, count(*) AS connections, min(backend_start) AS oldest_backend,
max(now()-backend_start) AS max_connection_age
FROM pg_stat_activity
WHERE pid <> pg_backend_pid() AND client_addr IS NOT NULL
GROUP BY datname, usename, application_name, client_addr, client_hostname, state
ORDER BY connections DESC, max_connection_age DESC;見るもの: application_name(例 redash / trocco / Retool / PostgreSQL JDBC Driver / DBeaver / prisma / psql。空のツールもある)・usename・client_addr・接続数・長時間接続か。怪しい接続は left(query,300) 付きで詳細確認。
② client_addr を AWS リソースに逆引き(VPC private IP → ENI)
aws ec2 describe-network-interfaces \
--filters "Name=addresses.private-ip-address,Values=<client_addr>" \
--query "NetworkInterfaces[].{ENI:NetworkInterfaceId,Desc:Description,IP:PrivateIpAddress,Groups:Groups[].GroupId,Instance:Attachment.InstanceId,Type:InterfaceType,Managed:RequesterManaged}" \
--output table --region $R --profile $P→ ECS task ENI / Lambda VPC ENI / EC2・踏み台 / NAT 経由 / VPC Endpoint 等が分かる。NAT・踏み台経由だとツール名までは分からないことがある。
③ DB SG inbound(接続「できる」経路=最強の AWS 側証拠)
DB_SGS=$(aws rds describe-db-clusters --db-cluster-identifier $SOURCE_CL \
--query "DBClusters[0].VpcSecurityGroups[].VpcSecurityGroupId" --output text --region $R --profile $P)
aws ec2 describe-security-groups --group-ids $DB_SGS \
--query "SecurityGroups[].IpPermissions[?FromPort==\`5432\` || ToPort==\`5432\`]" --output json --region $R --profile $P見るポイント: どの SG/CIDR から 5432 が許可されているか・CIDR で広く開いていないか・bastion/ECS/Lambda 以外(Redash/Retool 用)SG が無いか。SG 参照があれば、その SG を使う ENI を --filters "Name=group-id,Values=<sg>" で逆引き。
④ Secrets Manager / SSM / Terraform / ECS / Lambda を棚卸し(IaC 管理の接続先)
# Secret 名(password は出さず host/dbname/username のみ)
aws secretsmanager list-secrets --query "SecretList[?contains(Name,'eligibility')||contains(Name,'redash')||contains(Name,'retool')||contains(Name,'trocco')].[Name,ARN]" --output table --region $R --profile $P
# IaC / リポジトリ grep
rg -n "eligibility-verification|cluster-|DATABASE_URL|DB_HOST|DB_NAME|redash|retool|trocco|embulk|airflow|postgres" .
# ECS task def / Lambda env の DB 参照
aws ecs describe-task-definition --task-definition <td-arn> --query "taskDefinition.containerDefinitions[].{Name:name,Env:environment,Secrets:secrets}" --output json --region $R --profile $P
aws lambda get-function-configuration --function-name <fn> --query "{Fn:FunctionName,Env:Environment.Variables}" --output json --region $R --profile $P⑤ 各ツール管理画面で接続先を確認(AWS だけでは見えない・必須)
- Trocco: Connections(PostgreSQL connection)/転送元・転送先設定/Job schedule/接続 host・DB・使用 Secret
- ReTool: Resources(PostgreSQL resource)/host・database・user/環境別設定
- ReDash: Data Sources(PostgreSQL)/host・database・user/query schedule
- BI 系: Looker / Tableau / Metabase / Google Sheets connector / 社内分析ツール
⑥ 低頻度ジョブを拾うためのサンプリング(瞬間値の取りこぼし対策)
while true; do date -u; psql "$DATABASE_URL" -c "
SELECT datname, usename, application_name, client_addr, state, count(*)
FROM pg_stat_activity WHERE pid<>pg_backend_pid() AND client_addr IS NOT NULL
GROUP BY 1,2,3,4,5 ORDER BY 6 DESC;"; sleep 60; done | tee /tmp/db_connection_watch.log本番は 低トラフィック帯と通常時間帯で複数回、または 1営業日程度サンプリングして定期ジョブを拾う。
棚卸し表(結果をまとめる)
| Client | Evidence | Endpoint | DB user | Owner | Action | Switchover後確認 |
|---|---|---|---|---|---|---|
| Trocco / ReTool / ReDash / ECS / Lambda / batch … | pg_stat_activity / SG / Secret / IaC / 管理画面 | cluster / reader / instance / IP | 接続ユーザー | 管理者 | 変更不要 / 接続先修正 | 必要 / 不要 |
判定(endpoint 種別):
- A. cluster endpoint → 接続先変更は基本不要。Switchover 後に再接続確認。
- B. reader endpoint → 読み取り用途なら基本追従。再接続確認。
- C. instance endpoint → 危険(旧環境へ残る)。cluster endpoint へ修正。
- D. IP 直指定 → 危険。必ず修正。
- E. 不明 → owner 確認が終わるまで GO しない。
⚠️ 管理しきれない接続は Switchover 後に Blue(
-old1)側のpg_stat_activity/ full query log(log_connections/pgaudit)で実地に洗い出す(アプリ以外が残っていたら接続先を新エンドポイントへ修正)。
- eligibility 実機(2026-06・SG/IaC/既存観測ベースの机上判定): DB SG inbound は app(
eligibility-verification-sg)+踏み台(fd-office-connection)のみ / IaC・Secret に外部ツール連携なし / Datastream なし(publication・logical slot=0)/ 観測接続は EVS app のみ = 外部 DB クライアントの接続先になっていない。※ Trocco/ReDash/ReTool の管理画面での最終確認と継続サンプリングは未実施(要すれば別途)。
E. パラメータ差分(PG13 → PG16)
PG16 用のカスタムパラメータグループを事前に作成する。まず現在ユーザーが設定している値を書き出す(Source=='user' で絞る)。
# Aurora(cluster パラメータグループ)
aws rds describe-db-cluster-parameters \
--db-cluster-parameter-group-name <current-aurora-pg13-cluster-pg> \
--query "Parameters[?ParameterValue!=null && Source=='user'].{Name:ParameterName,Value:ParameterValue}" \
--output table --region $R --profile $P
# RDS(instance パラメータグループ)の場合
aws rds describe-db-parameters \
--db-parameter-group-name <current-pg-param-group> \
--query "Parameters[?ParameterValue!=null && Source=='user'].{Name:ParameterName,Value:ParameterValue}" \
--output table --region $R --profile $PPG13(〜PG16)で削除・名称変更されたパラメータ(カスタムPGで使っている場合のみ、PG16 用 PG 作成時に反映/除外):
| パラメータ(旧) | 状態 | 対応 |
|---|---|---|
wal_keep_segments | PG13 で名称変更 | wal_keep_size に(値 = 旧値 × 16MB) |
vacuum_cleanup_index_scale_factor | PG14 で削除 | 新 PG に含めない(設定するとエラー) |
operator_precedence_warning | PG14 で削除 | 新 PG に含めない |
stats_temp_directory | PG15 で削除 | 新 PG に含めない(統計は共有メモリへ移行) |
vacuum_defer_cleanup_age | PG16 で削除 | 新 PG に含めない(hot_standby_feedback を使用) |
promote_trigger_file | PG16 で削除 | 新 PG に含めない(pg_ctl promote を使用) |
force_parallel_mode | PG16 で名称変更 | debug_parallel_query に |
- 引き継ぐ3値
rds.logical_replication/wal_sender_timeout/shared_preload_librariesは PG16 にも存在。 - ⚠️
Source=='user'のエクスポートはshared_preload_librariesを取りこぼすことがある。RDS/Aurora ではshared_preload_librariesの Source がsystemと報告されるため(実機確認済み)、上記 CLI では出てこない。preload 系はSourceに関係なく別途確認し、PG16 ターゲットに必ず引き継ぐこと。 - ⚠️
shared_preload_librariesは上書き型。source の既存値(rdsutils/pgaudit/pg_cron等)を落とさずpg_stat_statementsを追加する(→ Runbook 前提)。 - ⚠️ API 値と実行時 SHOW の値は異なって見えることに注意して確認する:
- RDS API 上の ParameterValue:
pg_stat_statements - 実行時
SHOW shared_preload_libraries:rdsutils,pg_stat_statements(rdsutilsは Aurora 側で付与) - → 確認は API 値だけでなく
SHOW shared_preload_librariesでも行う。PG16 ターゲットには少なくともpg_stat_statementsを含める(既存 preload を落とさない)。
- RDS API 上の ParameterValue:
- eligibility 実機(CPG
eligibility-verification):Source=='user':rds.logical_replication=1/wal_sender_timeout=0の 2件。shared_preload_libraries: API 値pg_stat_statements(Source=system・pending-reboot)/ 実行時 SHOWrdsutils,pg_stat_statements。Source=='user'エクスポートには出ないが設定済み。- 削除・改名パラメータ(wal_keep_segments 等)の user 設定は 無し。
F. ORM / driver 互換(Prisma → PG16)
アプリの ORM / DB ドライバが PG16 に対応していることを確認する。
確認手順(具体・eligibility の実行例)
cd <service>/api # 例: projects/eligibility-verification-service/api
# ① ORM / CLI の実解決バージョン(PG16 サポート版か)
pnpm list @prisma/client prisma --depth 0
# → 例: @prisma/client 6.9.0 / prisma 6.9.0(Prisma v6 は PG16 サポート対象)
# ② schema の provider と特殊指定(preview/binaryTargets/engineType)
grep -nE "previewFeatures|binaryTargets|engineType|provider" prisma/schema.prisma
# → provider="postgresql"・特殊指定なし(既定)であること
# ③ 直接 driver / 生SQL の不在
grep -E '"pg"|"pg-native"|"typeorm"|"kysely"|"knex"|"sequelize"' package.json # 該当なし期待
grep -rniE "from 'pg'|kysely|typeorm|\$queryRaw|\$executeRaw" src --include='*.ts' # 該当なし期待
# → 直接 driver・生SQL があれば PG16 非互換 SQL の確認対象を増やす-- ④ migration ベースライン(Blue/PG13 と Green/PG16 の両方で実行し一致を確認)
SELECT count(*) AS migration_count, max(finished_at) FROM _prisma_migrations;
SELECT count(*) FROM prompt_definitions; SELECT count(*) FROM ocr_results;
-- pnpm prisma migrate status / migrate deploy(pending なし期待・実施タイミングは Runbook 準拠)確認観点:
- ORM が PG16 をサポートするバージョンか(Prisma は v6 公式 Supported databases で PostgreSQL 16 サポート対象)。
- 直接 driver(pg / TypeORM / Kysely / knex / sequelize)や生SQL の利用が無いか(あれば PG16 非互換 SQL の確認対象を増やす)。
- 主な型が基本型(Int / String / DateTime /
jsonb等)中心か。 - ワイヤプロトコルは 3.0 後方互換のため、PG13→16 で接続方式が変わるわけではない。
- ⚠️ query engine バイナリ(
binaryTargets)はランタイム(ECSコンテナ)の OpenSSL/libc 依存=コンテナOSの話で PG13→16 とは無関係。 - ⚠️ SCRAM化 /
rds.accepted_password_auth_method=scram onlyは別タスク(アップグレード当日に同時実施しない)。
eligibility 実機(2026-06・詳細は #13098「ORM/driver 互換」コメント):
- Prisma(
@prisma/client/prismaともに 6.9.0・deploy の migrate は 6.5.0)/ provider=postgresql/ generator 既定。 - 直接 driver・生SQL の利用なし(Prisma 経由に集約)。主操作は
prompt_definitionsSELECT/UPDATE・ocr_resultsINSERT(標準的な SELECT/INSERT/UPDATE/RETURNING・jsonb)。 _prisma_migrationsベースライン count=2(rolled_back なし)。- → PG16 で ORM/driver 起因のブロッカーは見当たらない。
最終確認(Green/PG16 で実施・Runbook 手順8):
- Prisma 経由の OCR read/write が成功(
ocr_resultsに1行 INSERT)。 _prisma_migrationsが Blue/Green で一致(count=2・同名・rolled_back なし)。prisma migrate deployが想定外の migration を実行しない(pending なし)。- アプリログに Prisma 接続/認証/型変換エラーが継続しない。
G. Blue/Green 前提(logical replication)
SELECT current_setting('server_version') AS server_version,
current_setting('rds.logical_replication') AS logical_replication,
current_setting('wal_level') AS wal_level,
current_setting('wal_sender_timeout') AS wal_sender_timeout;
SHOW shared_preload_libraries;2段階で確認する(混乱防止):
G-1. 現状(custom CPG 付け替え前)
- eligibility 実機(2026-06):
server_version=13.20 / logical_replication=off / wal_level=replica / wal_sender_timeout=1min(いずれも既定)。 - custom CPG 未付け替えのため想定どおり=この段階では Blue/Green 前提は未達。
G-2. custom CPG 付け替え+再起動後(Blue/Green 作成前に必須)
- 期待値:
logical_replication=on/wal_level=logical/wal_sender_timeout=0/shared_preload_librariesにpg_stat_statementsを含む。 - 手順は Runbook 手順2(CPG 付け替え+Writer 再起動)。この再確認が取れて初めて Blue/Green 作成へ進める。
G-3. logical replication の容量(slot / sender / worker)
SELECT name, setting FROM pg_settings
WHERE name IN ('max_replication_slots','max_wal_senders','max_logical_replication_workers','max_worker_processes');
-- 対象DB数(BG は「各データベースに logical replication slot 1本」を要する)
SELECT count(*) FROM pg_database WHERE datallowconn AND NOT datistemplate;- 判定: **
max_replication_slots/max_wal_senders≥(対象DB数+既存slot+余裕)**であること。DB数が多い/書込が多いとラグ・失敗の恐れ(AWS 公式の logical replication 注意事項)。既定(RDS/Aurora とも 10〜20 程度)で足りるのが通常だが、DB数が多い場合はインスタンスクラス増強を検討。 - eligibility 実機:
max_replication_slots=20 / max_wal_senders=20 / max_logical_replication_workers=4・対象DB少数 =余裕あり。
H. その他の必須確認
- target engine version: アップグレード先(16.13 採用予定)が
ValidUpgradeTargetに含まれること(本番実施直前に再確認)。 - バックアップ保持:
BackupRetentionPeriod > 0(Blue/Green に必須。eligibility は 7)。 - Aurora Serverless v1 でないこと(Aurora のみ): Blue/Green は Aurora Serverless v1 を非サポート(Aurora 版制限)。
describe-db-clustersのEngineModeがprovisioned(=Serverless v1 でない)ことを確認。Serverless v2 は provisioned 扱いで対象。 - ストレージ空き容量: メジャーアップグレード(pg_upgrade)に空き容量が要る。RDS(非Aurora) は CloudWatch
FreeStorageSpaceに十分な余裕があること(gp3/自動拡張の上限も確認)。Aurora はストレージ自動管理だが、Green クローンで一時的に増える点に留意。bashaws cloudwatch get-metric-statistics --namespace AWS/RDS --metric-name FreeStorageSpace \ --dimensions Name=DBInstanceIdentifier,Value=<id> --start-time <1h前> --end-time <now> \ --period 3600 --statistics Minimum --region $R --profile $P - シーケンス数(Switchover timeout): BG は Switchover 時に green のシーケンス値を同期する。数十万本規模だと switchover が timeout し得る(AWS 記載)。件数を確認し、多ければ switchover timeout を延長。sql
SELECT count(*) FROM pg_class WHERE relkind='S'; -- シーケンス数 - Global Database でないこと(
GlobalWriteForwardingStatus単独では不十分なためdescribe-global-clustersで判定):
aws rds describe-global-clusters --region $R --profile $P \
--query "GlobalClusters[?contains(join(',', GlobalClusterMembers[].DBClusterArn), '<cluster-name>')].[GlobalClusterIdentifier,Status]" --output table- eligibility 実機: **空=非 Global**(確認済み)。所属している場合は Blue/Green の前提・手順が変わるため別途検討。
- クロスリージョン / カスケード read replica を持たないこと(Blue/Green 非対応):
- AWS 公式で Blue/Green は「Cross-Region read replicas」「Cascading read replicas」を非サポートと明記(Limitations and considerations for Amazon RDS blue/green deployments)。ソース DB がクロスリージョン/カスケードの read replica を持つと BG 作成に支障。Aurora も同様(Aurora 版)。
- 確認(read replica の一覧・リージョンを列挙):bash
# RDS: source instance の replica(同一/別リージョン)を列挙 aws rds describe-db-instances --region $R --db-instance-identifier <source-id> \ --query "DBInstances[0].ReadReplicaDBInstanceIdentifiers" # ARN に別リージョンが含まれる replica =クロスリージョン。カスケードは replica 自身がさらに replica を持つ場合 # Aurora: describe-db-clusters の ReadReplicaIdentifiers / cross-region は describe-db-clusters --region <other> - 方針: クロスリージョン replica は BG 前に削除し、Switchover 後は「新 primary と同一リージョン」に replica を新規作成する(クロスリージョンでの再作成はしない)。replica を読むアプリ / ETL(例: online-ops-service・Trocco 等)は、削除前に別 replica へ read 接続先を逃がしておく(レイテンシ・容量・SG を事前確認)。
- 実例(fastdoctor-manager): 大阪(ap-northeast-3) のクロスリージョン replica
db4は排除し、バージニア北部(us-east-1) に新規レプリカ用インスタンスを作成する方針(大阪には再作成しない)。db4 を読む online-ops-service / Trocco は事前に us-east-1 の replica へ逃がす。詳細は mental-online-karte #12732 / #14832。
- pg_stat_statements 有効化+ベースライン:
shared_preload_librariesに含まれているか確認(SELECT * FROM pg_available_extensions WHERE name='pg_stat_statements';でinstalled_versionが NULL なら未作成)。未有効なら CPG にpg_stat_statementsを追加(既存値を保持)→ Writer 再起動 →CREATE EXTENSION pg_stat_statements(手順は Runbook 手順2 / 3-4)。その後 Query ID が変わる前に Top SQL ベースラインを保存(→ Runbook 3-5)。 - DDL / migration 凍結: BG 作成直前〜Switchover 検証 OK まで凍結する運用経路を確認(eligibility は workflow 経路特定済み)。
最終チェックリスト
- [ ] A: PKなし / Unlogged / prepared_xacts / slot / LO / matview = 全0、長時間tx なし / A-7: パーティション・FDW・イベントトリガ確認
- [ ] B: Extension を確認(PostGIS/pg_repack/pg_partman/pg_cron の事前/事後対応を整理)
- [ ] C: PG16 非互換(EXTRACT / 階乗 / public ACL / password_encryption / PL/Python2 / 幾何型@~ / backup関数改名)= ブロッカーなし
- [ ] D: Pooler なし or 対応方針あり、接続元・接続先棚卸し済み
- [ ] E: パラメータ差分を確認、shared_preload_libraries は既存値保持
- [ ] F: ORM/driver の PG16 互換確認
- [ ] G: logical_replication=on / wal_level=logical / wal_sender_timeout=0 / G-3: slot・sender 容量 ≥ DB数
- [ ] H: target version / backup>0 / ベースライン取得 / DDL凍結方針 / Global DBでない / クロスリージョン・カスケードreplicaなし(あれば排除→同一リージョン再作成)/ Serverless v1でない / ストレージ空き / シーケンス数
各チェックの結果は対象サービスの Issue に記録する(eligibility は #13098)。