Skip to content

PG16 アップグレード preflight・リファレンス:eligibility-verification(EVS)

📖 本書はリファレンス(なぜ・調査結果・固有値・設計判断・ゲート根拠)。 手順(どうやる)は → clone-rehearsal.md / staging.md / production.md。 汎用手順の背景は ../../procedure-clone-rehearsal.md / ../../procedure-production.md

対象サービス: eligibility-verification(オンライン資格確認・電子処方箋・保険証 OCR の MS) 担当: SRE(yusaku.ishizawa) 最終更新: 2026-08-19(★実機再確認: production / staging 両アカウント)


0. 現状サマリ(★2026-08-19 実機確認)

環境エンジンCluster PG状態
staging16.13eligibility-verification-pg16移行完了(2026-06-25〜26 の staging-direct で Switchover 済・#14063)
production13.23eligibility-verification(custom PG13・in-sync未実施=残タスク

⚠️ 旧 services/eligibility-verification.md からの重要な訂正(実機確認で判明)

旧記載実際
現行エンジン 13.2013.23(2026-08-19 実機・Terraform main.tf13.23
「本番は CPG 未作成両 CPG とも作成済eligibility-verification(PG13 custom)/ eligibility-verification-pg16(PG16 target)。cluster/instance PG の両方が存在(2026-08-19 実機)
「フェーズB(CPG 付替+再起動)が必要」2026-06-30 に実施済み#14065 に全コマンド・出力の実行ログあり)。SHOW rds.logical_replication=on / wal_level=logical / wal_sender_timeout=0CREATE EXTENSION pg_stat_statements も完了。再起動後にアプリが自動再接続し ocr_results が 735,918→735,919 と増加=write 健全も確認済み

★★ 残っているのは「フェーズC(BG 作成)→ フェーズD(Switchover)」だけ

フェーズB が済んでいるため、本番は Switchover の 1 枠のみで完了する(当初計画の 2 枠から 1 枠に削減できる)。 瞬断を伴う事前メンテ枠は不要

⚠️ ただし BG 作成の直前に §3-2 の SHOW を1回だけ再確認すること。フェーズB から2ヶ月弱経っており、 その間の別作業で CPG が戻される可能性はゼロではない(コストは数十秒・production.md §2)。

🔴 ただし BG 作成前に解消すべきブロッカーが 1 件ある

本番 EVS DB に未適用の migration が 1 本ある_prisma_migrations = 2 に対し app repo は 3 本)。 本番デプロイのたびに prisma migrate deploy が走るため、BG 同期中にデプロイされると CREATE TABLE が Blue にだけ流れ、Switchover 後の新 primary に存在せずアプリが落ちる。 → 詳細と対処は §5-2。BG 作成前に必ず解消/合意すること。


0-1. アーキテクチャ(誰が Aurora を触るか・2026-08 時点)

図から読み取るべき3点

  1. Aurora を触るのは OCR 経路だけ — 「オン資確認実行」(/v1/insurance-card)と電子処方箋は外部 API を呼ぶだけで Aurora に接続すらしない(#13098 でコード確定)。疎通確認の経路を間違えやすい最大のポイント(§6-1)。
  2. アプリは cluster endpoint に接続 — Switchover でエンドポイント名は据え置かれるため Secret / DB_HOST / ECS の再設定は不要。必要なのは接続の張り直しだけ(§6)。
  3. 踏み台は DB には届くが ALB には届かない — DB SG は fd-office 踏み台を許可するが、 ALB SG は prefix list のみ許可。だから HTTP 経由の疎通確認は成立せず、 判定は DB 側(pg_stat_activity)から行う(§6-2 / §11)。

1. 環境・接続(実機確認 2026-08-19)

項目stagingproduction
AWS アカウント301608970378967691968827
AWS プロファイルstaging-admin参照=production(read-only) / 変更系=production-admin(⚠️ read-only では BG 作成・Switchover 不可。#12905)
リージョン(Rap-northeast-1ap-northeast-1 ★本番も東京(us-east-1 の dr-work-record / mental-appointment とは異なる)
Secrets Manager Secret IDeligibility-verificationeligibility-verification
DB 名(psql dbnameeligibility_verificationeligibility_verification
クラスタ endpointeligibility-verification.cluster-ctb7v7xkimcr.ap-northeast-1.rds.amazonaws.comeligibility-verification.cluster-cvwvyuc2cloj.ap-northeast-1.rds.amazonaws.com
DB Security Groupsg-0ab38c77bb380fda7sg-01a53660a7ef4826d
踏み台(★fd-office 系のみ)i-0cefcd4a25e8703e5fd-office-connection・SG sg-0cf2a96d7429ac09ei-03b8c9b9fb3c4fe9afd-office-connection-bastion・SG sg-0c5ef6b0d1cf98489
接続方式Prisma が DATABASE_URL 単一 Secret で接続(schema.prisma: url = env("DATABASE_URL"))。DB_HOST(ECS env)はアプリ未使用=変更不要。Secret 値・DB_HOST は Terraform 管理(jsonencode)のため手動変更は次の apply で上書きされる同左

⚠️ 踏み台は fd-office 系でないと届かない

EVS の DB SG の 5432 inbound は 2つの SG のみ(実機確認):

  • production sg-01a53660a7ef4826dsg-03ef3659aca4c210a(EVS ECS)/ sg-0c5ef6b0d1cf98489(fd-office-connection-bastion)
  • staging sg-0ab38c77bb380fda7sg-0a4c49925d69da1b1(EVS ECS)/ sg-0cf2a96d7429ac09e(fd-office-connection)

他サービスで常用する fd-platform 踏み台(i-09384db1d690bcfd0 等)は inbound に含まれず到達不可。 誤った踏み台を指定すると port-forward が張れても 5432 に届かず、原因究明で時間を溶かす。

代替踏み台(production): i-0a96515fe2956f506fd-office-connection)も同じ SG を持ち到達可(#14064)。 ⚠️ i-0cefcd4a25e8703e5 は staging 専用。旧手順書はこれを本番用として記載していたが、 本番アカウント(967691968827)には存在しない(#14064 で判明・実機で InvalidInstanceID.NotFound を確認)。

⚠️ 参照専用ロールでは DB に接続できない(#13926 / #14064)

production の read-only ロールは secretsmanager:GetSecretValuessm:StartSession を持たない。 つまり --profile production では DB パスワードの取得もポートフォワードもできず、DB 内部の確認が一切できない

  • describe-*(RDS/EC2/ECS/CloudWatch)= production(read-only)で可
  • DB 内部の確認(psql)= production-admin が必要(verify script も production-admin を使う)

この制約により、2026-06-23 時点では本番の \dx / pg_stat_activity が確認できず preflight が止まっていた。 権限手当て後の 2026-06-24 にまとめて実施している(§4-1)。


2. クラスタ構成(アップグレード対象=production)

項目値(2026-08-19 実機確認)
クラスタ識別子(SOURCE_CLeligibility-verification
インスタンス構成Writer 1台(eligibility-verification-0)/ Reader 無し
インスタンスクラスdb.t3.medium(★バースタブル・2 vCPU)
Performance Insights無効(→ DBLoad メトリクスは emit されない)
DB Subnet Groupeligibility-verification-subnet
Global Database非所属GlobalClusterIdentifier=null)→ Global 削除の手順は不要(dr-work-record / mental-appointment との最大の違い)
バックアップ保持7 日
現行エンジン13.23
target エンジン16.13(staging の実績と揃える。16.14ValidUpgradeTarget にあるが staging 未検証のため採らない)

ValidUpgradeTarget(13.23 から・2026-08-19 実測): 14.20/14.22/14.23 / 15.15/15.17/15.18 / **16.11/16.13/16.14** / 17.7/17.9/17.10 / 18.3/18.413.23 → 16.13 は直接メジャーアップ可能(中間バージョンを挟む必要なし)。

  • Reader 無し=再起動が要る場合も Writer 1台のみ(Reader→Writer の順序考慮は不要)。
  • 非-Global = 標準 BG をそのまま作れる(Global 削除→standalone 化の前段が不要)。

★2-1. db.t3.medium(バースタブル)であることの含意

BG の Green 構築(初期コピー)+論理レプリケーション適用は CPU を継続的に使う。t3 は CPU クレジットを 使い切るとベースライン性能(20%)へスロットリングされるため、

  • Green 構築が想定より大幅に長引く
  • Switchover 前に レプリラグが 0 へ収束しない(= GO 条件を満たせず枠を空振り)

という失敗が起こりうる。BG 実行中は CPUCreditBalance を最重要監視項目として見る(§10 のダッシュボード)。 db.t3.medium のクレジット上限は 576、ベースラインは 20%/vCPU。


3. パラメータグループ

役割名前family存在(2026-08-19 production)
ソース側 custom CPG(PG13・cluster)eligibility-verificationaurora-postgresql13✅ あり・クラスタに付与済(in-sync)
ソース側 instance PG(PG13)eligibility-verificationaurora-postgresql13✅ あり(user param なし)
ターゲット側 cluster PG(PG16)eligibility-verification-pg16aurora-postgresql16✅ あり
ターゲット側 instance PG(PG16)eligibility-verification-pg16aurora-postgresql16✅ あり(user param なし)

user param(両 cluster PG 共通・実測)

パラメータApplyMethod
rds.logical_replication1pending-rebootstatic(反映に再起動が必要)
wal_sender_timeout0pending-rebootdynamic

⚠️ ApplyMethod=pending-reboot は「この値をどう反映させるか」の記録であって「未反映」を意味しない。 2026-06-30 の再起動で反映済み(#14065)。実際の適用状態は DB 直の SHOW が正(§3-2)。

shared_preload_librariespg_stat_statements(Source=system = Aurora PG13 の既定値と一致するため user param として現れない(#14065 の実行ログでは付替前の CPG 参照時に user param として表示されている)。Terraform(main.tf)でも同値を宣言しておりドリフトではない。 pgaudit を preload する dr-work-record と違い、EVS は pgaudit 不使用=target CPG も同値で整合が取れている。

  • Terraform: source 側 custom CPG は microservice-ecscluster_parameter_group_custom_enable=truemain.tf)、 target 側は pg16_param_groups.tftemplate_modules/options/aurora-bluegreen-param-groups)。
  • 関連 PR: staging #2164 / production #2253(いずれも MERGED・実機に反映済み)。

★3-2. BG 作成直前の再確認(済んでいる前提だが、数十秒なので必ず1回やる)

フェーズB は 2026-06-30 に完了済み(#14065)。describe-db-clusters の Writer メンバー DBClusterParameterGroupStatusin-sync(2026-08-19 実機)。それでも確証は DB 直の SHOW でしか取れないため、 BG 作成前に踏み台経由 psql で以下を確認する(read-only・production.md §2):

sql
SHOW rds.logical_replication;  -- 期待: on
SHOW wal_level;                -- 期待: logical
SHOW wal_sender_timeout;       -- 期待: 0
SHOW shared_preload_libraries; -- 期待: pg_stat_statements を含む
\dx pg_stat_statements         -- 拡張が作成済みか(未作成なら BG 作成前に CREATE EXTENSION)
  • 3つとも期待値(想定どおり) → そのまま BG 作成へ。メンテ枠は Switchover の1枠のみ。
  • 万一 off / replica が返った場合のみ(=フェーズB 以降に誰かが CPG を戻した)、 Writer 1台の reboot が必要=瞬断なので Switchover とは別の事前枠を取り直す(production.md §2-B)。

4. 使用拡張・後処理の該当有無(実機確認 2026-06)

項目該当備考
インストール拡張(\dxplpgsql のみpg_stat_statements は別途 CREATE EXTENSION
pg_stat_statementspreload 済み・CREATE EXTENSION → Switchover 後に ALTER EXTENSION ... UPDATE(1.10 へ)
pgaudit未使用dr-work-record と違い CPG に含めない=target CPG との整合ズレの心配なし
pg_cron / pg_partman(bgw)未使用該当なし
pg_repack / postgis未使用該当なし
Auto Scaling / ReaderなしWriter 1台
外部 CDC(Datastream 等 publication/slot)なしonline-karte のような外部論理レプリ無し=BG 作成が external replication で失敗するリスクなし(BG 作成直前に再確認)

→ 後処理で実際にやるのは ①拡張更新(pg_stat_statements)②(任意)統計リセット ③アプリエラーログ監視 の3つのみ。

4-1. ★preflight 実測結果 全節(横断チェックリスト A〜G 準拠)

確認方法(SQL・コマンド・期待値)は横断チェックリスト postgresql-pg16-upgrade-preflight-checklist.md を正とし、 本節は EVS の実測結果のみを記録する(#13802 で全 SQL を staging 実機検証済み・#14064 で本番実施)。

実施環境Issue
2026-06-17staging#13802(チェックリストの全 SQL を実機検証)
2026-06-24production#14064(A〜C / D / G-1 を全節実施)

A. DB 内部チェック(Blue/Green 阻害要因)

項目stagingproduction判定
A-1 PK 無しテーブル0行0行
A-2 unlogged / prepared_xacts / replication slot / large object / matviewすべて 0すべて 0
A-3 長時間 tx・進行中 DDLなしなし(自セッションの SELECT のみ)
A-4 サポートされない reg* 型カラム00
A-5 無効な database(datconnlimit=-20行0行
A-6 template0 / template1両方 datistemplate=t

B. Extension

項目stagingproduction判定
pg_extensionplpgsql 1.0 のみplpgsql のみ
pg_available_extensions(全件・drift)plpgsql 1.0/1.0(drift なし)同左
pg_stat_statementspreload 済み・未作成preload 済み・未作成installed_version=NULL / default 1.8)✅ 作成はフェーズB
postgis / pg_repack / pg_cron / pg_partman / cube / seg0件0件✅ 事前対応不要

preload レベルでも事前対応は不要: 現行 CPG の shared_preload_librariesrdsutils,pg_stat_statements のみで、pgaudit / pg_cron / pg_partman を含まない(#14064)。

C. PG16 非互換

項目stagingproduction判定
C-2 DB 内関数/ビューの EXTRACT・階乗 ! !!0件 / 0件0行 / 0行✅ 該当なし
C-3 public スキーマ ACLPUBLIC=UC(PG13 既定維持)PUBLIC=UC(owner YvkE3F7S=UCブロッカーではない
C-4 password_encryptionmd5md5✅ ブロッカーでない(既存 md5 資格で接続成立)
C-5 包含演算子 @ ~ 削除(幾何型・cube/seg)0行 / 0件0行 / 0件✅ 該当なし
C-6 廃止言語(plpython2u / plpythonu0件0件✅ 該当なし
C-7 バックアップ関数旧名(pg_start_backup 等)関数/ビュー 0行✅ 該当なし
C-8 挙動変更(hash_mem_multiplier 1.0→2.0 等)現状 1⚠️ 移行後の監視対象(下記)

C-3 PUBLIC=UC について: PG15 以降 public スキーマの既定 ACL は縮小されたが、 既存 DB をアップグレードする場合は既存 ACL がそのまま維持されるため移行ブロッカーではない。 セキュリティ観点の見直し候補ではあるが、本移行では変更しない(変更すると影響範囲が読めない)。

C-4 md5 について: SCRAM 化は PG16 移行とは独立した別タスク。md5 のままでも接続は成立する(実機確認済み)。

⚠️ C-8 は「該当なし」ではなく「移行後に見る」項目: PG16 では hash_mem_multiplier の既定が 1.0 → 2.0 に変わる(現状 1)。ハッシュ処理のメモリ配分が変わるため、 Switchover 後は DBLoad / FreeableMemory / 一時ファイル / slow query を監視する(§10 のダッシュボード+§12 の Top SQL 突合)。

D. 接続元・接続先の棚卸し

項目結果(production・2026-06-24)判定
Connection Pooler(RDS Proxy / PgBouncer)なし(staging も出力空)
pg_stat_activity の接続元アプリ(ECS)由来のみYvkE3F7S の2ホスト(10.21.1.67 / 10.21.3.28)・application_name 空・長寿命プール接続
DB SG inbound 5432eligibility-verification-sg(アプリ) + fd-office-connection(踏み台) の2 SG のみ✅ 外部DBクライアント用の許可なし
外部 DB クライアント(Trocco / ReDash / Retool / BI / Airflow)なし
pg_replication_slots / pg_publication / pg_publication_tables / pg_stat_replicationすべて 0 行BG 阻害なし(外部 CDC 非運用)

最重要の確認: 外部 logical slot / publication があると BG の Read Replica 作成が Replica creation is canceled due to external replication. で失敗する(online-karte の実例 #13732)。 EVS は 0 行なのでこのリスクはない。BG 作成直前にもう一度確認するproduction.md §3)。

E / F / G. パラメータ差分・ORM 互換・Blue/Green 前提

項目結果判定
F ORM/driver 互換Prisma 6.x(5.0+ で PG16 正式サポート)。pg / pg-native / Kysely / TypeORM の直接利用はなし=Prisma に集約。$queryRaw 等の生SQL も無し(#13098 でコード確認)
F アプリ SQLAurora へ出る SQL は prompt_definitionsmode(UNIQUE) SELECT / ocr_results INSERT / 管理用 prompt_definitions UPDATE のみ。標準的な SELECT/INSERT/UPDATE/RETURNING で、PG16 非互換項目(EXTRACT 戻り値型・階乗・public CREATE 依存)に該当なしjsonb も通常サポート範囲
G-1 BG 前提(付替前の現状)rds.logical_replication=off / wal_level=replica / wal_sender_timeout=1min / shared_preload_libraries=rdsutils,pg_stat_statements⚠️ 想定どおり→フェーズBで on/logical/0
G-2 付替+再起動後2026-06-30 に on / logical / 0 を確認(#14065)✅ 完了
ValidUpgradeTarget13.20 時点: 16.8 / 16.9 / 16.10 / 16.11 / 16.13(#14064)/ 13.23 時点(2026-08-19 再確認): 16.11 / 16.13 / 16.14✅ 16.13 を採用
バックアップ保持7 日

結論(#14064)

本番 eligibility-verification は Blue/Green 可能な状態。BG を阻害する外部レプリケーション・ 外部 DB クライアントは存在しない。read-only で取得可能な DB 側 preflight は全節クリア。

残課題(preflight の枠外・別作業)

項目状態
C-1 / F: アプリ repo の grep(生SQL・EXTRACT・階乗・Prisma 互換)完了(#13098 comment・上表 F に反映)
E / H: AWS describe 系(パラメータ差分・DNS TTL)⚠️ 一部未実施(psql 不要で取得可)。DNS TTL は当日 §0 で確認
C-2 関数定義スキャンの完全版pg_get_functiondef が集約関数で array_agg is an aggregate function を出すため、prokind NOT IN ('a','w') + CTE フェンス版が未実施。拡張=plpgsql のみ・ビュー 0 行のため 0 行見込みで判定影響なし

5. ★スキーマ実態と「このサービス最大のリスク=シーケンス継続性」

EVS の DB は極小(app repo eligibility-verification-service/api/prisma/ で確定)。 ⚠️ 環境でテーブル数が違う(production 3 / staging 5・2026-08-20 実測)。理由は §5-2 の未適用 migration

テーブル役割PK書込prodstg
ocr_resultsOCR 結果の追記専用ログ(アプリは読み返さない)SERIAL(int・autoincrement)★アクティブ(OCR 実行ごとに 1 INSERT)
prompt_definitionsOCR 指示文マスタSERIAL(int・autoincrement)管理時のみ
_prisma_migrationsPrisma migration 履歴textmigration 時のみ✅ (2)✅ (3)
myna_mock_consented_patientsマイナ資格確認モック(#16756)SERIALQA 時のみ未適用
myna_mock_consent_query_settings同上SERIALQA 時のみ未適用

5 テーブルすべて SERIAL PK_prisma_migrations を除く)= シーケンス継続性の検証対象。 staging の実測で 4 シーケンスが検出されるのはこのため。

なぜシーケンスが最大リスクなのか

Blue/Green の論理レプリケーションは「行」は複製するが「シーケンスの現在値」は複製しない。ocr_results / prompt_definitionsSERIAL PKnextval 依存)なので、Switchover 後の Green で シーケンスが max(id) より遅れていると、次の OCR 実行がその場で PK 衝突して落ちる

  • read だけの確認では絶対に検出できない(件数は一致して見える)。
  • UUID PK の mental-appointment ではこのリスクが無い(preflight で sequence=0 と判定される)が、 EVS は真逆=ここが移行検証の主眼

→ 対策は §11 の verify script(4) シーケンス継続性)を Switchover 直後・アプリ再開前に必ず実行すること。 DRIFT が出たら setval で是正してからアプリを流す。

5-2. 🔴 未適用 migration = BG 中に走ると Switchover 後に壊れる(★最優先で解消)

本番 EVS DB には未適用の migration が 1 本ある。BG 同期中にこれが走ると Switchover 後にアプリが落ちる。

確定している事実

事実根拠
origin/main に migration が 3 本ある(3本目 = 20260803000000_add_myna_mock_consent_tablesapp repo・2026-08-04 マージ(#16756 / #16760)
その内容は CREATE TABLE ×2 + CREATE INDEX ×2myna_mock_*migration.sql
本番の _prisma_migrations = 2(staging = 3)2026-08-20 実測(verify script)
本番の稼働イメージは v1.7.1(リリース 2026-07-13)task definition eligibility-verification-task:38 実測
本番デプロイのたびに prisma migrate deploy が走る.github/workflows/eligibility-verification-service@deploy-prod.yml の「Database migration」ステップ

migration マージ(08-04) 以降に本番デプロイが無く未適用のまま(上記の整合からの推定)。  つまり 次回の本番デプロイでこの DDL が本番 DB に流れる

なぜ危険か

Aurora Blue/Green は内部で論理レプリケーションを使うが、DDL は Green に複製されない(AWS も BG 中の DDL を避けるよう明記)。 BG 同期中にこの migration が走ると:

  • テーブルは Blue にだけ作られ、Switchover 後の新 primary(旧 Green)には存在しない
  • アプリはその頃テーブルがある前提で動くため relation does not exist で落ちる
  • 最悪、同期自体が壊れて BG をやり直しになる

対処(BG 作成前に必ずどちらかを確定させる)

方針補足
A(推奨)BG 作成の前に本番デプロイを1回通し、migration を適用しておくGreen は Blue のコピーとして作られるため、先に適用すれば Green もテーブルを継承する。以後は DDL 凍結
BBG 期間中に EVS の本番デプロイが発生しないことを関係者と合意リリース凍結の徹底が前提。緊急修正が入ると破綻するため A より弱い

⚠️ Prisma には環境別に migration を出し分ける仕組みが無く、prisma migrate deploy保留中の migration をすべて適用する。myna_mock_* は staging の QA 用モックだが、 本番にもテーブルだけは作られる(未使用でも作られる)点に注意。

当日の確認方法

sql
-- BG 作成前と Switchover 後の両方で一致すること
SELECT count(*) FROM _prisma_migrations;
SELECT migration_name, finished_at FROM _prisma_migrations ORDER BY finished_at;

verify script の 2b/2c(全テーブル件数の pre/post diff)でも、_prisma_migrations の件数差が出たら DDL が流れた証拠として検知できる。

⚠️ 2b/2c の diff は同一環境内(pre ↔ post)の比較専用。テーブル数が違うため staging と production の出力を突き合わせても意味がない


5-1. 移行前ベースライン(件数)

既知の実測値:

  • 2026-06-24(#14064・preflight 時点): ocr_results = 729,194 行 / max(id) = 729,194 / _prisma_migrations = 2 / prompt_definitions = 2
  • 2026-06-30(#14065・フェーズB 時点): ocr_results = 735,918
  • 2026-08-20(verify script --label pre・read-only 実行): ocr_results = 808,995 行 / max(id) = 809,010 / prompt_definitions = 2 / _prisma_migrations = 2 / 全 3 テーブル

★約 50 日で +73,077 行(1 日あたり約 1,460 件)本番は現役で書き込みが走っている(実行の 20 秒前にも INSERT があった)。 Switchover の書込断が実トラフィックに当たることの裏付けであり、メンテ枠の周知が必要。 ★件数(808,995) と max(id)(809,010) の 15 のギャップは、削除ではなくロールバックされた INSERT(シーケンスのみ消費)による正常な差。 --write-probe を使うと同じ理由でこのギャップが 1 増える。

当日は改めて取得し §14 へ追記する。整合確認クエリ:

sql
SELECT COUNT(*) FROM _prisma_migrations;
SELECT COUNT(*) FROM ocr_results;
SELECT COUNT(*) FROM prompt_definitions;

6. consumer 棚卸し(誰が EVS Aurora を触るか)

DB SG 5432 inbound(実機・§1 の枠内)= EVS の ECS + fd-office 踏み台のみ。SaaS(Retool 等)・ CIDR 直許可・ETL(Trocco 等)・外部 logical レプリは 無し=consumer は事実上 1 系統。

consumer経路read/write
eligibility-verification-service(ECS Fargate・desired 2)Prisma(DATABASE_URLcluster endpoint)→ Auroraprompt_definitions read / ocr_results write
SRE(調査)fd-office 踏み台 → SSM port-forward → psqlread-only
  • ECS: cluster eligibility-verification-cluster / service eligibility-verification-service(2026-08-19 実測 running=2・タスク IP 例 10.21.3.33 / 10.21.1.153)。
  • cluster endpoint 接続=Switchover でエンドポイント名が据え置かれる(identifier は不変)ため、 Secret / DB_HOST / ECS の再設定は不要。必要なのは接続の張り直し(再接続)だけ。
  • ⚠️ ECS タスク IP は再デプロイで変わるため、IP を手順書に固定で書かない(verify script は実行時に aws ecs から動的解決する)。

6-1. ★どの画面操作が EVS Aurora を触るか(重要・誤解しやすい)

画面操作経路EVS Aurora
「オン資確認実行」ボタン/admin/kartes/:id/eligibility_verificationEVS /v1/insurance-card外部 OQS 照会のみ。結果は fastdoctor-manager 自身の DB に保存触らない(接続も書込も発生しない)
保険証/医療証の画像アップロード(OCR)Image 保存 → ExecuteOcrJob(非同期)→ OcrPreviewService → EVS POST /v1/ocr/previewocr_results に +1(接続も発生)★これで確認する

EVS Aurora の疎通確認は「保険証画像の OCR」で行う。「オン資確認実行」では EVS Aurora は動かない。 Flipper ocr_from_eligibility_verification が有効であること(OFF だと legacy Ocr::Service 経由で EVS を通らない)。

6-2. API プローブの限界(EVS 固有)

  • GET /health-checkDB を触らない liveness のみ(controller が固定文字列 'OK' を返すだけ)=DB 疎通の証明にならない
  • アプリ→Aurora の疎通は「verify script の 接続元→ECS 属性化(pg_stat_activity)」+「画面からの OCR E2E」で担保する(§7)。

HTTP 経路から DB まで叩く案は採らない(2026-08-20 判断)。 ALB → ECS → Aurora を一気通貫で確認する案(踏み台→ALB の SG を開けて read クエリを投げる)を検討したが、 production で GraphQL read が利用できないため見送った(mental-online-karte#17740 / terraform_for_aws#2659 はクローズ)。 将来 production で利用可能になった場合に再開できるよう、候補だった経路を記録として残す:


7. アプリ経由のライブ検証(staging で実証済み・2026-06)

⚠️ staging 限定(本番 acct 967691968827 / .fstdr.jp では実施しない)。実 PII 不可。 Textract+Bedrock の少額課金+ocr_results 1行が発生するので後始末する。 ocr_results は本番 OCR(個人情報を含み得る)も入るため、調査時は SELECT * を避け id/件数/時刻のみ参照。

7-1. GraphQL 経由(fdc api graphql

経路(#13098 でコード確定): EVS の OCR は入口が2系統あるが、どちらも同じ OcrPreviewUsecase.call() に合流する。

REST   : POST /v1/ocr/preview          (AuthGuard = x-api-key・サーバー間・mode は呼び出し側指定)─┐
GraphQL: previewInsuranceCardOcr        (CatAuthGuard + @requiresScopes・ユーザー単位・mode 固定)  ├─→ OcrPreviewUsecase.call()
         previewMedicalCertificateOcr                                                              ─┘   ├ prompt_definitions READ(find by mode)
                                                                                                        └ ocr_results WRITE(create=1行 INSERT)
  • GraphQL は chronic-api フェデレーション Gateway 経由(認証=CAT + @requiresScopes([is:Operator]/[is:ServiceAccount]))。 fdc api graphql はこの Gateway を叩く(トークンは自動リフレッシュ)。
  • 書込トリガー: GraphQL mutation previewInsuranceCardOcr(または previewMedicalCertificateOcr)。 1リクエスト=prompt_definitions READ + ocr_results1行 INSERT(jsonb)
  • 認証/実行: pnpm fdc auth loginis:Operator)→ fdc api graphql

⚠️ REST の Swagger 記述に誤りがある(#13098 で指摘): POST /v1/ocr/preview の説明に 「このAPIは、MS上にレコードを生成する副作用は発生させません」とあるが、実装は ocr_results に INSERT する。 副作用なしと誤解して本番で叩かないこと。

⚠️ read-only の GraphQL クエリ(promptDefinitions)は production では利用できない(2026-08-20 判断)。 OcrResolver.promptDefinitionsprisma.promptDefinition.findMany()prompt_definitions を read-only に SELECT できる経路はコード上存在するが、production では使えないため 疎通確認には採用していない(§6-2 / #17740)。

  • 手順(詳細・実証ログは #13098 コメント(2026-06-10)):
    1. ダミー保険証画像(「TEST/DUMMY」明記)を生成 → S3 s3://eligibility-verification-staging/evs-ocr-test/ に put → aws s3 presign(1h)。
    2. 署名付き URL を &/= でシェルが壊さないようファイル経由で GraphQL に埋め込む:
      bash
      # /tmp/evs_presigned_url.txt に presign URL を保存後
      python3 - <<'PY'
      url=open('/tmp/evs_presigned_url.txt').read().strip()
      open('/tmp/evs_ocr_query.gql','w').write(
        'mutation { previewInsuranceCardOcr(input:{ imageUrl:"%s" }) '
        '{ result { ocrResult { insuranceNumber } alert } userErrors { message code } } }'%url)
      PY
      cat /tmp/evs_ocr_query.gql | pnpm --silent fdc api graphql   # userErrors:[] = 成功(ocr_results に1行 INSERT)
    3. 後始末: S3 のダミー画像と一時ファイル削除(テスト行は無害=アプリは読み返さない。消すなら staging で DELETE FROM ocr_results WHERE id=<id>)。

7-2. 画面(fastdoctor-manager)経由 — 実証手順(2026-06-22 実施)

  1. ダミー保険証画像を用意(実 PII 不可・「TEST/DUMMY・テスト タロウ」等を明記)。健康保険証 のレイアウトに寄せると OCR 分類が通りやすい。
  2. EVS Aurora を read-only 監視(fd-office 踏み台 i-0cefcd4a25e8703e5 経由 SSM):
    sql
    SELECT count(*) AS cnt, max(id) AS max_id, max(created_at) AS latest FROM ocr_results;   -- ベースライン
    SELECT pid, client_addr, state, state_change FROM pg_stat_activity
      WHERE datname='eligibility_verification' AND pid<>pg_backend_pid();                    -- 接続状況
  3. 画面で保険証画像をアップロード(staging・2026-06 実施の遷移):
    1. オンライン診療 依頼フォーム https://contact-test.fastdoctor.jp/online-consultation/?scene=complete(Basic 認証 fast/fast1)で患者情報を登録し案件を作成。
    2. オンライン医師差配 https://backend-stg.fstdr.jp/online_coordinator/ で該当患者の詳細情報モーダルを開く。
    3. モーダルの 「保険証[未登録]」をクリックhttps://backend-stg.fstdr.jp/admin/medical_examinations/<id>/receipt
    4. その画面で保険証の写真をアップロード(=OCR トリガー)。
    • 非同期ジョブExecuteOcrJob)のため反映は数十秒〜数分遅れることがある。
  4. EVS Aurora を再確認ocr_results の max_id/件数が +1、新規 ECS 接続が active→idle になれば成立。
    sql
    SELECT id, created_at,
           (result::text ILIKE '%TEST%' OR raw_result::text ILIKE '%TARO%') AS dummy,  -- ダミー判定(PII 非表示)
           (coalesce(alert,'')='') AS classified_ok                                     -- alert 空=健康保険証として分類成功
    FROM ocr_results WHERE id > <baseline max_id> ORDER BY id;

実測例(2026-06-22 staging): 画像アップロード → ocr_results4293→4294→4295 と +1 ずつ増加、dummy=t / classified_ok=t。 「オン資確認実行」では同 DB は 無変化(接続も idle のまま)であることも確認=§6-1 の経路差を実証。

注意・落とし穴

  • 非同期遅延: ExecuteOcrJob はジョブのため即時反映でないことがある(数分待って再確認)。
  • 冪等スキップ: ExecuteOcrJobreturn if image.ocr_result.present?同じ画像の再アップロードは EVS を呼ばない(増えないのが正常)。確認は毎回別の新規画像で。
  • 分類失敗: OCR が「健康保険証/医療証」と分類できないと alert に「…どちらでもありませんでした」が入る(行は作られるが processed_result={})。ダミーは保険証レイアウトに寄せる。

8. Blue/Green 名・スナップショット名

項目
Blue/Green 名(BG_NAME・本番)eligibility-verification-bg
Blue/Green 名(リハーサル)eligibility-verification-bgtest
Clone 名(リハーサル CLeligibility-verification-bgtest-blue
Switchover 前スナップショットeligibility-verification-pre-pg16-<YYYYMMDDHHMM>
Switchover 後の旧 Blueeligibility-verification-old1(自動リネーム)

9. ★Switchover 前 GO/NO-GO ゲート(EVS 版)

production.md §5 で機械的に実行する。1つでも欠けたら NO-GO=枠を閉じて延期

#条件確認方法
BG Status=AVAILABLE / StatusDetails=null / SwitchoverDetailsAVAILABLEdescribe-blue-green-deployments
green メンバーの CPG が in-sync・engine=16.13describe-db-clusters(green)
レプリラグ ≒ 0OldestReplicationSlotLag / slot の confirmed_flush_lsn 差が 0 bytes)CloudWatch + blue で pg_replication_slots
blue の長時間 tx = 0blue で pg_stat_activity
全テーブル件数が blue == greenverify script 2b(blue/green それぞれ)を diff
green で ANALYZE 実行済(read_only を外したセッションで・全テーブル対象)切替直後の性能事故防止
CPUCreditBalance に余裕がある(枯渇していない)§10 のダッシュボード(EVS 固有・t3.medium)
保険スナップショットが available・engine=13.23describe-db-cluster-snapshots

⑤⑥ は全テーブルが 3 つしかないため所要は僅少(mental-appointment の clone 実測で ANALYZE VERBOSE 27.2 秒 → EVS はさらに短い見込み)。


10. 監視(CloudWatch ダッシュボード)

Terraform: fastdoctor-template/eligibility-verification/production/pg16_dashboard.tfダッシュボード名: eligibility-verification-aurora-pg16-bluegreen(ap-northeast-1) 共通モジュール: template_modules/options/aurora-bluegreen-dashboard

green(動的名 eligibility-verification-green-xxxxx)は name_prefix への SEARCH で自動表示される。

★EVS 固有のウィジェット選択(実測に基づく)

設定理由(実測)
show_burstable_cpu_credittruedb.t3.medium(バースタブル)。BG 中の最重要監視項目(§2-1)
show_db_loadfalsePerformance Insights 無効DBLoad は emit されない(list-metrics=0
show_volume_bytes_leftfalseAuroraVolumeBytesLeftTotal を emit しない(list-metrics=0

show_db_loadshow_burstable_cpu_credit は同じスロット(x=8,y=7)を使うため排他(同時 true は module の precondition で plan 失敗)。 ※撤去可: BG 完了・旧 Blue(-old1) 削除・様子見後、module ブロックを削除して apply(destroy される)。 ※staging は移行済みのため本番のみに置く。


11. 移行前後の検証スクリプト(prove)

preflight(pg16-preflight-aurora.sh・読み取り専用)とは別に、Switchover の前後で DB 直の read / 接続 / シーケンス継続性を機械的に確認するスクリプト。

  • 場所: docs/sre/scripts/pg16-verify-eligibility-verification.shGitHub
  • 使い方(切替の前後で実行して比較):
    bash
    bash docs/sre/scripts/pg16-verify-eligibility-verification.sh --env production --label pre
    # …Switchover…
    bash docs/sre/scripts/pg16-verify-eligibility-verification.sh --env production --label post
    --env で profile / 踏み台を自動切替(staging=staging-admin/i-0cefcd4a25e8703e5、 production=production-admin/i-03b8c9b9fb3c4fe9a)。両 env とも ap-northeast-1
#検証何を守るか
1server_version13 系 / 16 系の確認
2read プローブ(3テーブルの件数・max(id)・最新時刻)データ欠落
2b/2c全テーブル exact 件数 → --label post で pre と自動 diff構造的な欠落
3接続元→ECS 属性化pg_stat_activityclient_addraws ecs で逆引き)アプリ→Aurora 疎通(§6-2 の代替)
4シーケンス継続性(read-only) pg_sequences.last_value vs 対応表の max(id)§5 の最大リスク。書込ゼロで DRIFT を検出
5★write/nextval 実検証(--write-probe 指定時のみ・BEGIN; INSERT; ROLLBACKnextval の確定検証
6writability(pg_is_in_recovery() / default_transaction_read_only昇格後に書けるか
7API プローブ(--api-probeGET /health-checkECS/ALB 生存のみ(DB 疎通ではない・§6-2)

合格基準(post 実行時): server_version が 16 系 / 2c の diff が空(または ocr_results の in-flight 数件差のみ) / 4 の verdict が全て OK/unused3 で ECS 接続が復帰(★本番は 2 行・各 1 本以上) / 6 で in_recovery=false

11-1. ★3) の期待値(本番は 2 行)

ECS の desiredCountstaging=1 / production=2(タスクサイズは両環境 1024 CPU / 2048 MB・実測 2026-08-20)。 したがって 3) の出力に現れる eligibility-verification-service の行数は staging 1 行 / production 2 行が期待値で、 本番で 1 行しか出なければ片方のタスクが再接続できていないことを意味する。

⚠️ 接続本数の絶対値は合否条件ではない(判定は「タスク IP からの接続が 1 本以上」)。 Prisma はプールを遅延して開き、アイドル接続は回収されるため本数は実行時のトラフィックで変動する。 実測でも staging 3 本 / production 2 本×2 と差が出たが、タスクサイズは同一でどちらも健全

見るべきは ①0 本でないか ②本番は 2 行そろっているか ③backend_start が Switchover 時刻より後か。 ※ connsstate 別の集計なので、同一タスクで idleactive が混在すると行が分かれる(合計は足し算)。 ※ local / rdsadmin の行は Aurora 内部の管理プロセスでアプリとは無関係(判定対象外)。

★Switchover 直後は「0 本」が正常 → post は --wait-ecs 必須

Switchover は Blue を read-only 化 → エンドポイント DNS を Green に付け替え → Blue を -old1 にリネームする。 つまり切替後は **物理的に別のインスタンス(旧 Green)**に繋がり、新 primary の pg_stat_activity は そもそも旧接続を持たない(旧 Blue の接続は切断される=瞬断)。加えて Prisma は接続を遅延して張る (次のクエリが来るまで再接続しない)ため、切替直後は **「少ない」ではなく「0」**になる。

タイミング3) の見え方
pre(Blue)2 行 / 各 2 本 = 計 4 本(2026-08-20 実測)
post 直後(新 primary)0 行 ★これは正常
トラフィック到来後2 行 / 各 1〜2 本に回復

--wait-ecs を付けずに post を実行すると、正常なのに FAIL と誤検知する。 本番の OCR 書込は約 1,460 件/日 ≒ 1 分あたり約 1 件(§5-1 実測)なので、--wait-ecs 180 なら 期待値で約 3 リクエストが到来し Prisma が張り直す。

なお client_addr(ECS タスク IP)は切替では変わらない。変わるのは行数・本数のみ。 ただし復旧のため ECS を強制再デプロイすると IP が変わるため、スクリプトは IP を直書きせず 実行時に aws ecs から再解決している。

⚠️ --write-probeROLLBACK するがシーケンスは戻らない(ocr_results の id を1消費する)。 ocr_results はアプリが読み返さない追記専用ログのため実害はないが、既定 OFF4(read-only)で DRIFT なしを確認できていれば 5 は省略してよい。


12. Terraform 整合(Switchover 後・旧 Blue 削除より前)

  • パス: fastdoctor-template/eligibility-verification/production/
  • main.tf の DB モジュール呼び出しを更新:
    • rds_engine_version"13.23""16.13"
    • rds_family"aurora-postgresql13""aurora-postgresql16"
    • source 側 custom CPG(cluster_parameter_group_custom_enable=truecluster_parameter_group_params)を PG16 用 CPG 参照へ切り替え-pg16 を継続利用する方針= #14418
  • terraform plandowngrade / replace(ForceNew)が出ないことを確認してから apply。
  • apply は -target でドリフト巻き込みを回避(staging 実績 PR: #2263)。
  • eligibility-verification-old1(PG13)は数日安定確認後に削除。source 側 PG13 CPG は孤児化してから削除。

13. 関連 Issue / PR

種別参照
親チケットmental-online-karte #12725「[DB] eligibility-verification PostgreSQL 16系アップグレード」
本番手順書mental-online-karte #13721(CLOSED・本書群で置換)
staging 調査・検証mental-online-karte #13098
staging-direct 実施mental-online-karte #14063
Clone リハーサルmental-online-karte #13719(CLOSED)
本番 preflightmental-online-karte #13926(CLOSED)
本番 CPG 追加(TF)mental-online-karte #13927(CLOSED)/ terraform_for_aws #2253
本番 フェーズA-1/B(CPG 付替+再起動)mental-online-karte #14065(CLOSED・2026-06-30 実施・全コマンド実行ログあり)
staging CPG 追加(TF)terraform_for_aws #2164
実装メモ(custom PG / logical / PG16 target)mental-online-karte #12905
昇格後の PG 整合(-pg16 継続利用)mental-online-karte #14418
本番 preflight 全節実施(A〜G)mental-online-karte #14064(OPEN・2026-06-23/24 の実測ログあり)
プレフライト チェックリスト(横断)の整備mental-online-karte #13802(OPEN)/ doc: postgresql-pg16-upgrade-preflight-checklist.md
踏み台→ALB フルパス疎通(★見送り)mental-online-karte #17740(CLOSED・not planned)/ terraform_for_aws #2659(CLOSED)
prove スクリプトmental-online-karte #17737 / terraform_for_aws #2656
CloudWatch ダッシュボードmental-online-karte #17738 / terraform_for_aws #2657
手順書再編(本書)mental-online-karte #17739 / terraform_for_aws #2658
対象 DB の棚卸し・方針mental-online-karte #13044

14. 実施記録

日付環境フェーズ結果・所要・気付き
2026-06-16〜17stagingClone リハーサルGreen 作成 約33分(Read Replica 約16分+PG16 化 約14分)。Green offline 約12分(Blue 無影響)。Switchover 書込断 約2秒
2026-06-17stagingpreflight(チェックリスト全 SQL)全項目 ✅(#13802)。plpgsql のみ / PK なし 0 / 外部 slot 0 / RDS Proxy なし / md5 / PUBLIC=UClogical=off は CPG 未付替のため想定どおり。
2026-06-23productionpreflight 着手・権限ブロッカーread-only ロールに GetSecretValue / ssm:StartSession が無く DB 内部確認が不能と判明(#14064)。踏み台を i-03b8c9b9fb3c4fe9a と特定(旧記載 i-0cefcd4a25e8703e5 は本番に存在せず誤り)。CPG レベルでは preload 要対応なしを確認。
2026-06-24productionpreflight 全節実施(A〜C/D/G-1)ブロッカー無し(#14064)。外部 slot/publication/レプリ すべて 0 行=BG 阻害なし。接続元はアプリ ECS の2ホストのみ=外部DBクライアントなし。ベースライン ocr_results=729,194。C-8 hash_mem_multiplier は移行後の監視対象として認識。
2026-06-22staging画面 E2E 実証保険証 OCR で ocr_results が +1(4293→4295)。「オン資確認実行」では EVS Aurora 無変化=経路差を実証。
2026-06-25〜26stagingstaging-direct(実DB直接)B→C→D 完走(#14063)。Switchover 書込断 約2秒(events 実測)。Green=16.13 / Blue==Green 整合 / ライブ複製OK / 拡張 1.10 / slot 0 / A-0事後 0。アプリ(画面・GraphQL 両経路)→新 PG16 書込 OK。
2026-06-30productionフェーズB(CPG 付替+Writer reboot=瞬断)成功(#14065)。付替前 logical=off/wal_level=replica/wal_sender_timeout=1min → 付替+reboot 後 on/logical/0CREATE EXTENSION pg_stat_statements 完了。外部CDC(slot/publication/replication)0行を確認。再起動後にアプリが設定変更なしで自動再接続ocr_results 735,918→735,919=write 健全。
2026-08-19実機再確認staging=16.13 移行完了。production=13.23・CPG 付与済(in-sync)・db.t3.medium/PI 無効・非-Global・Writer1台。旧 doc の「13.20 / 本番 CPG 未作成」を訂正。
2026-08-20verify script 実走(--label pre・read-only)✅ 両環境で全チェック PASS(exit=0)。staging=16.13 / production=13.23。3) 接続元→ECS 属性化は 本番 2 タスク・計 4 本を検出(staging 1 タスク 3 本)。4) シーケンスは両環境 DRIFT なし。移行前ベースラインを取得/tmp/pg16-verify-evs-rowcounts-production-pre.txt)。
未実施productionフェーズC/D(BG 作成→Switchover)残タスクはここだけ。フェーズB 済みのため Switchover の1枠のみで完了する。snapshot 名・所要・結果は実施時に追記。