Aurora(PostgreSQL) 13→16 in-place upgrade 所要時間 計測(Clone 方式・非BG)
作成日: 2026-07-22 担当: SRE(yusaku.ishizawa) 関連ドキュメント:
- 索引・進捗: README
- サービス固有値・実施記録: services/online-karte-service.md
📌 本書は Blue/Green ではない「非BG(in-place)方式」の計測手順です。 mental-online-karte #15755(手法選定)で、論理レプリケーションを使う Blue/Green を避け、 fast clone + in-place major upgrade + 書き込み停止カットオーバーを代替主案(案C)として評価している。 本書はその go/no-go を決める数値=「本番相当の in-place upgrade 所要時間(≒書き込み停止時間)」 を、 本番クラスタを無停止のまま clone で計測するための手順。
- 実施タスク: mental-online-karte #15837
- 手法比較(A〜G): mental-online-karte #15755
- BG採用時の下流対応(別方式): mental-online-karte #15448
本書について
- 本番 Aurora クラスタの fast clone(コピーオンライト複製) を作り、その clone を PG16 に in-place major upgrade して 所要時間を計測する。
- clone は本番カタログ(オブジェクト数)を完全継承するため、pg_upgrade 時間は本番相当。本番クラスタには一切変更を加えない。
- staging では本番とデータ量・オブジェクト数が異なり時間が代表値にならないため、本番の clone で測る。
- 続けてアプリ疎通・拡張・実行計画・dbt/BI 検証まで行えば、案C の事前リハーサルを兼ねられる(その場合は後始末を後ろ倒し)。
- ⚠️ 代表値・安全側見積りとして扱う:copy-on-write clone は本番そのものではないため、計測値は完全一致保証ではない。
なぜ BG ではなくこの方式を計測するのか
- BG は内部で論理レプリケーションを使い、PKなし mastra テーブル(
mastra_workflow_snapshot/mastra_evals)の REPLICA IDENTITY 付与と下流 Datastream/dbt の merge化・backfill リスク(#15448)を招く。加えて Clone リハーサル(#13732)で 外部 Datastream slot/publication により BG 作成自体が失敗する事象も確認済み。 - in-place はこれらを回避できるが、switchover <1分にはできず pg_upgrade 分の書き込み停止が発生する。その停止時間が許容メンテ枠に収まるかが採否の分岐点であり、本書で計測する。
影響・コスト
- 計測は 独立した clone に対して行うため、本番・staging 本体への影響は無い。
- clone のストレージはコピーオンライトで安価だが、計算インスタンスは課金対象。計測後は必ず後始末(削除)すること。目安: 本番同一構成(db.r6g.2xlarge × 3)を 1〜2 時間 + 差分ストレージ = 概ね千円台程度。※ pg_upgrade 時間だけ測ればよくコストを抑えたい場合は writer 1台のみでも代表値は得られる(手順2で reader 2台をスキップ)。
前提
- 書き込み権限のある production プロファイル(
production-admin等)。production(read-only) では clone 作成不可。 - clone は同一アカウント・同一リージョン・同一 KMS を継承(copy-on-write)。
- 計測には source 相当の PG16 custom CPG を適用する(→ 手順3)。⚠️ 本番アカウントに
online-karte-service-pg16は未作成で、PG16 CPG の Terraform 化はまだ行わない方針。よって本計測では使い捨ての PG16 CPG(oks-pg16-timing-cpg)を CLI で作成し、source(PG13) の user 設定を写す。恒久運用の CPG は後日 Terraform で別途用意する(名前を分けて衝突回避)。 - clone への接続(precheck SQL / 書き込みプローブ用)は踏み台(bastion)経由 SSM ポートフォワードで行う(接続方式は services/online-karte-service.md §1 参照)。
サービス固有値(online-karte-service / production・ワークド例)
| 項目 | 値 |
|---|---|
| AWS アカウント | aws-fd-aws-production(ID: 967691968827) |
| リージョン | ap-northeast-1 |
| source cluster | online-karte-service(aurora-postgresql 13.20) |
| インスタンス構成 | db.r6g.2xlarge × 3(writer=online-karte-service-2(1c) / reader=-0(1c),-1(1a) / MultiAZ) |
| source custom CPG | online-karte-service(family aurora-postgresql13)— user 設定: shared_preload_libraries=pgaudit,pg_stat_statements / pgaudit.log=all / log_min_duration_statement=1000 / rds.log_retention_period=1440 / max_slot_wal_keep_size=1000 / max_wal_senders=20 / rds.logical_replication=1 / wal_sender_timeout=0 |
| PG16 ターゲット CPG | 計測用に CLI で使い捨て作成(oks-pg16-timing-cpg・手順3)。恒久版の Terraform 化は後日・別名 |
| subnet group | online-karte-service-subnet |
| 本番 VPC SG | sg-0aebb5b56d1268e51(※計測 clone には原則使わない・下記変数参照) |
| KMS | arn:aws:kms:ap-northeast-1:967691968827:key/7f357d07-74b5-4005-a0f3-1a0bcbd21a1b(継承) |
| volume 使用量 | 約 159 GB |
| target version | 16.13(16.8 / 16.9 / 16.10 / 16.11 / 16.13 から選択可・手順0で要確認) |
別サービスで実施する場合は該当する
services/<service>.mdの値へ読み替える。
変数
export AWS_PROFILE=production-admin # 書き込み可能プロファイル(read-only 不可)
export R=ap-northeast-1
export SRC=online-karte-service
export CLONE=oks-pg16-timing-clone # 使い捨てと分かる名前
export CLASS=db.r6g.2xlarge # 本番同等(計測を代表値にするため必須)
export TARGET=16.13 # 手順0で有効な upgrade target か確認
export PG16_CPG=oks-pg16-timing-cpg # 計測用・使い捨て PG16 CPG(手順3で CLI 作成 / Terraform管理外 / 手順7で削除)
# clone は原則「計測用の一時SG」を付ける(踏み台/SSM からのみ接続可)。本番同一SGはアプリ疎通段階に限定
export CLONE_SG=<計測用の一時SG(踏み台/SSM からのみ許可)>
# 本番と同一トポロジ(writer×1 + reader×2 / 全て db.r6g.2xlarge / MultiAZ)
export CLONE_W=${CLONE}-2 # writer 相当(本番: online-karte-service-2 / 1c)
export CLONE_R1=${CLONE}-0 # reader 相当(本番: online-karte-service-0 / 1c)
export CLONE_R2=${CLONE}-1 # reader 相当(本番: online-karte-service-1 / 1a)手順
0. 事前確認(実行前チェック)
# 0-1. 有効な major upgrade target を確認(13.20 が deprecated の可能性 → --include-all 必須)
aws rds describe-db-engine-versions --profile $AWS_PROFILE --region $R \
--engine aurora-postgresql --engine-version 13.20 --include-all \
--query 'DBEngineVersions[].ValidUpgradeTarget[?IsMajorVersionUpgrade==`true`].[EngineVersion,Description,AutoUpgrade]' \
--output table
# → $TARGET(16.13) が含まれることを確認
# 0-2. source cluster / instances の主要設定を控える(clone 手順の入力・本番差分の記録用)
aws rds describe-db-clusters --profile $AWS_PROFILE --region $R --db-cluster-identifier $SRC \
--query 'DBClusters[0].{EngineVersion:EngineVersion,ClusterPG:DBClusterParameterGroup,Subnet:DBSubnetGroup,VpcSG:VpcSecurityGroups[*].VpcSecurityGroupId,Kms:KmsKeyId,StorageEncrypted:StorageEncrypted,DeletionProtection:DeletionProtection,LogExports:EnabledCloudwatchLogsExports}' \
--output json
aws rds describe-db-instances --profile $AWS_PROFILE --region $R \
--query "DBInstances[?DBClusterIdentifier=='${SRC}'].{id:DBInstanceIdentifier,class:DBInstanceClass,az:AvailabilityZone,dbpg:DBParameterGroups[*].DBParameterGroupName,pi:PerformanceInsightsEnabled}" \
--output tableprecheck SQL(clone 作成後、手順4 の前に clone 側で実行) — precheck 失敗だと時間が測れないため最低限確認する。
-- ★最重要: logical replication slot が残っていないか(1本でもあると upgrade 失敗。→ トラブルシュート参照)
-- 本番は Datastream の slot が常時あるため、in-place の最大のブロッカー。
SELECT slot_name, plugin, slot_type, active, database FROM pg_replication_slots;
-- prepared transaction が残っていないか(残っていると upgrade 失敗)
SELECT count(*) AS prepared_xacts FROM pg_catalog.pg_prepared_xacts;
-- user table に unknown 型が残っていないか
SELECT n.nspname, c.relname, a.attname
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
JOIN pg_catalog.pg_attribute a ON c.oid = a.attrelid
WHERE NOT a.attisdropped
AND a.atttypid = 'pg_catalog.unknown'::pg_catalog.regtype
AND c.relkind IN ('r','m','c')
AND n.nspname !~ '^pg_temp_' AND n.nspname !~ '^pg_toast_temp_'
AND n.nspname NOT IN ('pg_catalog', 'information_schema');1. fast clone 作成(copy-on-write)
aws rds restore-db-cluster-to-point-in-time --region $R \
--source-db-cluster-identifier $SRC \
--db-cluster-identifier $CLONE \
--restore-type copy-on-write \
--use-latest-restorable-time \
--vpc-security-group-ids $CLONE_SG \
--db-subnet-group-name online-karte-service-subnet \
--tags Key=purpose,Value=pg16-timing-throwaway
aws rds wait db-cluster-available --region $R --db-cluster-identifier $CLONE⚠️ SG は原則
$CLONE_SG(計測用一時SG)。clone には本番データが入るため、本番同一SG(sg-0aebb5b56d1268e51)を付けるのはアプリ疎通リハーサルを行う段階に限定する。
2. 計測用インスタンスを本番同一構成で追加(writer×1 + reader×2)
本番と全く同じ状況で計測するため、writer 1 + reader 2 の計3台を本番同一クラス・同一AZ配置で作成する。最初に作成したインスタンスが writer になる。
# 2-1. writer 相当(AZ=1c、本番 writer と同AZ)
aws rds create-db-instance --region $R \
--db-cluster-identifier $CLONE --db-instance-identifier $CLONE_W \
--db-instance-class $CLASS --engine aurora-postgresql \
--availability-zone ${R}c --promotion-tier 0 --no-publicly-accessible
aws rds wait db-instance-available --region $R --db-instance-identifier $CLONE_W
# 2-2. reader 2台(1c / 1a)を本番配置に合わせて追加
aws rds create-db-instance --region $R \
--db-cluster-identifier $CLONE --db-instance-identifier $CLONE_R1 \
--db-instance-class $CLASS --engine aurora-postgresql \
--availability-zone ${R}c --promotion-tier 0 --no-publicly-accessible
aws rds create-db-instance --region $R \
--db-cluster-identifier $CLONE --db-instance-identifier $CLONE_R2 \
--db-instance-class $CLASS --engine aurora-postgresql \
--availability-zone ${R}a --promotion-tier 0 --no-publicly-accessible
aws rds wait db-instance-available --region $R --db-instance-identifier $CLONE_R1
aws rds wait db-instance-available --region $R --db-instance-identifier $CLONE_R2
# 構成確認(writer/reader/AZ が本番と一致するか)
aws rds describe-db-clusters --region $R --db-cluster-identifier $CLONE \
--query "DBClusters[0].DBClusterMembers[].{inst:DBInstanceIdentifier,isWriter:IsClusterWriter}" --output table3. PG16 ターゲット CPG を CLI で使い捨て作成し、source 設定を写す
原則:source 相当の PG16 custom CPG を当てて測る(
shared_preload_librariesなどの互換性問題を先に検出し、本番手順との差分を減らすため)。単に pg_upgrade の機械的な所要時間だけを粗く測る場合に限りdefault.aurora-postgresql16を許容する。 PG16 CPG の Terraform 化はまだ行わない方針のため、本計測では CLI で使い捨て CPG を作成する(恒久版は後日 Terraform・別名で用意)。パラメータグループはアタッチするまで無害な非破壊リソース。
# 3-1. PG16 cluster parameter group を作成
aws rds create-db-cluster-parameter-group --region $R \
--db-cluster-parameter-group-name $PG16_CPG \
--db-parameter-group-family aurora-postgresql16 \
--description "throwaway PG16 CPG for in-place timing (oks #15837)" \
--tags Key=purpose,Value=pg16-timing-throwaway
# 3-2. source(PG13 の online-karte-service CPG) の user 設定を写す
# ※ shared_preload_libraries の値中カンマは shorthand ではバックスラッシュでエスケープ
aws rds modify-db-cluster-parameter-group --region $R \
--db-cluster-parameter-group-name $PG16_CPG \
--parameters \
"ParameterName=shared_preload_libraries,ParameterValue=pgaudit\,pg_stat_statements,ApplyMethod=pending-reboot" \
"ParameterName=pgaudit.log,ParameterValue=all,ApplyMethod=immediate" \
"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate" \
"ParameterName=rds.log_retention_period,ParameterValue=1440,ApplyMethod=immediate" \
"ParameterName=max_slot_wal_keep_size,ParameterValue=1000,ApplyMethod=immediate" \
"ParameterName=max_wal_senders,ParameterValue=20,ApplyMethod=pending-reboot" \
"ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot" \
"ParameterName=wal_sender_timeout,ParameterValue=0,ApplyMethod=immediate"
# 3-3. 反映確認
aws rds describe-db-cluster-parameters --region $R \
--db-cluster-parameter-group-name $PG16_CPG --source user \
--query "Parameters[].{name:ParameterName,value:ParameterValue,apply:ApplyMethod}" --output tablerds.logical_replication/max_wal_senders/wal_sender_timeoutは BG・Datastream 用で、Datastream を繋がない使い捨て clone の計測には必須ではないが、本番パリティのため source と同値で写している(不要なら省略可)。shared_preload_libraries=pgaudit,pg_stat_statementsは upgrade 後インスタンスの起動条件に効くため必須(欠けると pgaudit なしで起動=本番と挙動が変わる)。- instance 側 DBPG は cluster-level の
shared_preload_librariesがあれば本計測では既定で可(instance 固有 custom を厳密に写す必要が出たら別途 PG16 DBPG を作成しアタッチ)。 - 手順4 の
modify-db-clusterに--db-cluster-parameter-group-name $PG16_CPGを付与する(下記)。
4. in-place major upgrade を実行し計測
echo "START: $(date -u +%FT%TZ)"
aws rds modify-db-cluster --region $R \
--db-cluster-identifier $CLONE \
--engine-version $TARGET \
--allow-major-version-upgrade \
--db-cluster-parameter-group-name $PG16_CPG \
--apply-immediately
time aws rds wait db-cluster-available --region $R --db-cluster-identifier $CLONE
echo "END: $(date -u +%FT%TZ)"
aws rds describe-db-clusters --region $R --db-cluster-identifier $CLONE \
--query "DBClusters[0].EngineVersion" --output text5. 停止時間の計測(3種類を併用)
“実際に知りたいのは、アプリから見ていつ書けなくなり・いつ書けるようになったか”。以下を併用する。
| 計測値 | 用途 |
|---|---|
time aws rds wait db-cluster-available(手順4) | 全体 wall-clock の上限 |
describe-events | RDS 内部イベントの時系列確認 |
| psql 書き込みプローブ | アプリ視点の実停止時間(失敗開始〜成功復帰) |
RDS イベント
aws rds describe-events --region $R --source-identifier $CLONE --source-type db-cluster \
--duration 240 --query "Events[].[Date,Message]" --output table
for i in $CLONE_W $CLONE_R1 $CLONE_R2; do
echo "----- $i -----"
aws rds describe-events --region $R --source-identifier $i --source-type db-instance \
--duration 240 --query "Events[].[Date,Message]" --output table
donepsql 書き込みプローブ(upgrade 開始前から別ターミナルで回す。$CLONE_DATABASE_URL は clone writer への接続文字列=踏み台経由)
# upgrade 前に clone 側で作成
psql "$CLONE_DATABASE_URL" -c "
CREATE SCHEMA IF NOT EXISTS sre_upgrade_probe;
CREATE TABLE IF NOT EXISTS sre_upgrade_probe.write_probe (
id bigserial primary key, inserted_at timestamptz default now());"
# upgrade 開始前から実行し続ける
while true; do
date -u +%FT%TZ
psql "$CLONE_DATABASE_URL" --set=statement_timeout=3000 \
-c "INSERT INTO sre_upgrade_probe.write_probe DEFAULT VALUES;" \
>/tmp/pg16-probe.out 2>/tmp/pg16-probe.err
echo "exit=$?"
sleep 5
done | tee /tmp/pg16-upgrade-write-probe.log- ログの「INSERT 失敗が始まった時刻」〜「成功に戻った時刻」が アプリ視点の実停止時間。
6. pg_upgrade ログ確認(後片付けの前に・削除すると取れない)
aws rds describe-db-log-files --profile $AWS_PROFILE --region $R \
--db-instance-identifier $CLONE_W \
--query "DescribeDBLogFiles[?contains(LogFileName,'pg_upgrade')].[LogFileName,LastWritten,Size]" --output table
# 必要に応じて取得(pg_upgrade_internal.log / pg_upgrade_server.log)
aws rds download-db-log-file-portion --profile $AWS_PROFILE --region $R \
--db-instance-identifier $CLONE_W --log-file-name "<上記で得た pg_upgrade ログ名>" --output text7. 後片付け(必須・コスト停止)
# 7-1. reader を先に削除 → 最後に writer → クラスタ
for i in $CLONE_R1 $CLONE_R2 $CLONE_W; do
aws rds delete-db-instance --region $R --db-instance-identifier $i --skip-final-snapshot
done
for i in $CLONE_R1 $CLONE_R2 $CLONE_W; do
aws rds wait db-instance-deleted --region $R --db-instance-identifier $i
done
aws rds delete-db-cluster --region $R --db-cluster-identifier $CLONE --skip-final-snapshot
# 7-2. major upgrade 開始時に自動作成される preupgrade snapshot も削除(clone 本体を消しても残る)
aws rds describe-db-cluster-snapshots --profile $AWS_PROFILE --region $R --snapshot-type manual \
--query "DBClusterSnapshots[?contains(DBClusterSnapshotIdentifier,'preupgrade') && contains(DBClusterSnapshotIdentifier,'${CLONE}')].[DBClusterSnapshotIdentifier,Status,SnapshotCreateTime]" \
--output table
SNAPSHOTS=$(aws rds describe-db-cluster-snapshots --profile $AWS_PROFILE --region $R --snapshot-type manual \
--query "DBClusterSnapshots[?contains(DBClusterSnapshotIdentifier,'preupgrade') && contains(DBClusterSnapshotIdentifier,'${CLONE}')].DBClusterSnapshotIdentifier" \
--output text)
for s in $SNAPSHOTS; do
aws rds delete-db-cluster-snapshot --profile $AWS_PROFILE --region $R --db-cluster-snapshot-identifier "$s"
done
# 7-3. 使い捨て PG16 CPG を削除(クラスタ削除後・どのクラスタからも参照されていないこと)
aws rds delete-db-cluster-parameter-group --region $R --db-cluster-parameter-group-name $PG16_CPG計測の代表性・注意
- オブジェクト数が同一(clone は本番カタログを完全継承)= pg_upgrade 時間は本番相当。ただし copy-on-write clone は本番そのものではないため、完全一致保証ではなく代表値・安全側見積りとして扱う。
- clone は copy-on-write のため初回参照が lazy load ぶん I/O が重く、実 upgrade は やや遅め(安全側) に出る可能性。
- インスタンスクラスは本番同等(db.r6g.2xlarge)にする。小さくすると CPU 律速で過大評価になる。
- 台数について: Aurora の major upgrade は pg_upgrade を writer 上で実行し、その後 reader を順次 upgrade/restart する。書き込み停止時間の主要因は writer upgrade だが、全インスタンスが available に戻るまでの wall-clock や reader 復帰 tail は reader 台数の影響を受ける可能性がある。本番同等の復帰挙動まで見たい場合は writer×1+reader×2 で測る。pg_upgrade の粗い所要時間だけを先に把握したい場合は writer 1台のみでもよい。
- ANALYZE: pg_upgrade は optimizer 統計を引き継がない。upgrade 後に
ANALYZE VERBOSE(対象DB)を実施する。非ブロッキングなので書き込み停止には加算されない(実行中はプランが一時非最適なだけ)。- online-karte 実測: DB全体 ANALYZE(online_karte・176テーブル)=約28秒(2026-07-23)。ANALYZE は各テーブル 30000 行サンプルの値を detoast するため、所要は「テーブルGB」ではなく「TOAST/行」で決まる。online-karte の最大は KarteContentYjs 19KB/行・KarteVersion 8.7KB/行 程度(本番確認済み)=サンプル detoast は 1テーブル数百MB程度で軽い。EventLog は 22GB だが TOAST は 528KB(heap 側)で detoast 負荷なし。
- ⚠️ 巨大 TOAST(1行が極端に大きい列)を持つDBでは ANALYZE が桁違いに遅くなる。参考: fastdoctor-manager(RDS・gp3)は mobakar_api_logs 79GB / audits 53GB 等の巨大TOATで
ANALYZE VERBOSEが約91分(services/fastdoctor-manager.md)。online-karte にはそのようなテーブルは無く、かつ Aurora(分散ストレージ)で I/O が速い。対象DBの TOAST 上位テーブルを事前確認して見積もること。
- 得られた実停止時間を #15755 に反映し、許容メンテ枠との突き合わせで案C(非BG)の go/no-go を判断する。
結論
本手順は、本番 Aurora に変更を加えず、production clone 上で PG13→PG16 の in-place major upgrade 所要時間を測るためのもの。計測値は本番相当データ・本番相当カタログを持つ clone に対する dry-run のため、案C(fast clone + in-place major upgrade + 書き込み停止カットオーバー)の go/no-go 判断に使える。ただし copy-on-write clone は本番そのものではないため、完全一致保証ではなく本番停止時間の代表値・安全側見積りとして扱う。本番採用判断に使う計測では、PG16用の本番想定 CPG(+必要なら DBPG)を適用し、RDS event・wall-clock・psql 書き込みプローブの3種で停止時間を記録する。
トラブルシュート:pg_upgrade precheck 失敗と対処
事象(2026-07-22 の実測で発生)
modify-db-cluster 後まもなく、クラスタが upgrade を開始→precheck で中断し 13.20 のまま available に戻る。RDS イベント/precheck ログに次が出る:
Database cluster is in a state that cannot be upgraded: ... one or more databases have
settings or usages that aren't compatible with the target DB engine version.
Examine the precheck log file for more details.precheck ログ(error/pg_upgrade_precheck.log.*)本文:
The cluster could not be upgraded from 13.20 to 16.13 because ...
-- The cluster could not be upgraded because one or more databases have logical replication slots.
Please drop all logical replication slots and try again.原因
- in-place(pg_upgrade)は logical replication slot が1本でも存在すると実行できない。
- 本番 online-karte-service には Datastream 用の logical slot
datastream_replication_slots(pgoutput / logical / online_karte)が常時存在し、clone はそれを継承する。
対処(clone 上で slot を drop → 手順4 を再実行)
clone 上の slot drop は 本番 Datastream には無影響(clone は Datastream の接続先ではない)。踏み台/SSM 経由で clone に接続して実行する。
-- 現状(clone では active=f のはず)
SELECT slot_name, plugin, slot_type, active, database FROM pg_replication_slots;
-- 全 slot を drop
SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots;
-- 0 件を確認
SELECT count(*) AS remaining FROM pg_replication_slots;
WARNING: could not open/remove directory "pg_replslot/..."が出ることがあるが、slot カタログ entry は削除されるため無害(remaining=0を確認できればよい)。active=t(walsender 接続中)の場合は drop 不可。clone では通常 inactive。
診断ログの取得
aws rds describe-db-log-files --profile $AWS_PROFILE --region $R --db-instance-identifier $CLONE_W \
--query "DescribeDBLogFiles[?contains(LogFileName,'precheck') || contains(LogFileName,'upgrade')].[LogFileName,Size]" --output table
aws rds download-db-log-file-portion --profile $AWS_PROFILE --region $R --db-instance-identifier $CLONE_W \
--log-file-name "error/pg_upgrade_precheck.log.<...>" --output text★本番切替への含意(重要)
本番の in-place カットオーバーでは、Datastream 停止 → slot drop → in-place upgrade → slot 再作成 → Datastream 再開(+backfill) の段取りが必須。書き込み停止(≈10分)とは別に、Datastream の再作成後 backfill 時間を見込むこと(#15448 と連動)。
slot は「位置ごとの復活」ができない = backfill は不可避
- drop した logical replication slot は restart_lsn(WAL 上の読み出し位置)ごと消える。同名で作り直せる(
pg_create_logical_replication_slot)が、新 slot は「作成した今」からしか読めない(過去位置を指す slot を後から作る手段はない。pg_replication_slot_advanceも前方のみ)。 - さらに pg_upgrade をまたぐと WAL が論理デコードに使えず、位置の引き継ぎは原理的に不可(precheck が drop を強制する理由)。
- 本家 PostgreSQL 17 の「pg_upgrade で論理 slot を引き継ぐ」機能は 旧クラスタが v17 以上が条件のため、13→16 では対象外。Aurora 固有の slot 復活機能も無い。
- ⇒ 過去分は Datastream の backfill(全テーブル再読込)で埋めるしかない。slot 復活で backfill を回避することはできず、短縮(並列度↑)のみ可能。
Datastream 再開の手順と操作切り分け(TF / コンソール / psql)
online-karte の Datastream は Terraform 管理(gcp/services/fd-datastream/production/datastream.tf の google_datastream_stream.online_karte。replication_slot="datastream_replication_slots" / publication="datastream" / schema public / backfill_all {} / desired_state="NOT_STARTED")。
再開手順
- PG側(psql): slot を再作成(publication は pg_upgrade で消えないので通常は残る。無ければ再作成)。名前は TF と一致必須。sql※
SELECT pubname FROM pg_publication; -- 'datastream' 確認(無ければ CREATE PUBLICATION datastream FOR ALL TABLES;) SELECT pg_create_logical_replication_slot('datastream_replication_slots','pgoutput'); SELECT slot_name, active FROM pg_replication_slots; -- 作成確認rds.logical_replication=1はクラスタパラメータで upgrade をまたいでも残る(変更不要)。 - GCPコンソール:
Datastream > Streams > online-karte(fd-datastream-prd / asia-northeast1)を 開始 / 再開。 backfill_all {}により全 public テーブルを backfill(CDC 位置喪失のため不可避)。監視は GCP コンソール state +#fdt-online-karte-error-prod+ BQ 行数。
操作の切り分け
| 操作 | どこで |
|---|---|
| ストリームの開始 / 一時停止 / 再開(run state)・オブジェクト backfill 実行 | GCP コンソール(Terraform ではない) |
backfill 並列度 max_concurrent_backfill_tasks(backfill 短縮)・対象オブジェクト・data_freshness 等の構成 | Terraform(変数変更→apply) |
| slot / publication の作成・drop | psql(DB操作) |
⚠️
desired_stateにignore_changesが無い(NOT_STARTEDハードコード)。ストリームをコンソールで RUNNING にした後に fd-datastream スタックをterraform applyすると desired_state 差分でストリームを止めにいく恐れがある。切替期間中は不用意に apply しない/apply 時は plan で desired_state 差分が出ないか確認する。backfill 並列度を上げる場合も同様に注意して事前 apply する。
実施記録
| 日付 | 環境 | target | Total time offline(AWS公式) | wall-clock | 気付き |
|---|---|---|---|---|---|
| 2026-07-22 | production(clone・db.r6g.2xlarge×3・約159GB) | 13.20→16.13 | 606.0 秒(約10分6秒) | 約13分 | 初回は Datastream の logical slot datastream_replication_slots で precheck 失敗 → slot drop 後に成功。offline 内訳: precheck約11s→snapshot→volume clone→writer約5分→reader→primary ready。psql プローブは踏み台経由が必要で本実施では未取得(offline 値=AWS公式イベントで代替) |
| 2026-07-23 | production(clone・db.r6g.2xlarge×3) | 13.20→16.13 | 639.0 秒(約10分39秒) | 約15分(全復帰まで) | 再現確認(前回606sと整合)。upgrade 後 DB全体 ANALYZE VERBOSE(online_karte・176テーブル)=28秒。合計(upgrade→全復帰→ANALYZE)≈ 約15.5分。ANALYZE は非ブロッキングで書込停止に非加算 |
実施で判明した重要事項
⚠️ in-place upgrade は logical replication slot が1本でもあると precheck で失敗する(
... one or more databases have logical replication slots. Please drop all logical replication slots and try again)。本番は Datastream の slot(datastream_replication_slots/ pgoutput / logical / online_karte)が常時ある。⇒ 非BG in-place でも 本番切替時は「Datastream 停止 → slot drop → in-place upgrade → slot 再作成 → Datastream 再開(+backfill)」の段取りが必須。BG より論点は少ないが Datastream 連携はゼロにならない。
⚠️ メジャーVUP はクラスタ全体オフライン(ローリング/フェイルオーバーではない)。upgrade 中は writer も reader も接続/クエリ不可(reader へ寄せて読み取りを継続する手は使えない)。よって
Total time offline(606秒)は読み書き両方の全断時間。ただし
Total time offline(606秒)は primary ready までであり、reader の安定復帰は含まない。本実測のサービス影響は3区間:区間 時間 状態 ① 全断(読み書き不可) 08:07:53→08:17:59 = 606秒(約10分) writer/reader とも不可(= Total time offline)② reader 復帰遅れ 08:17:59→08:20:14 = 約2分15秒 primary 稼働。reader は Read replica has fallen behind the master too much. Restarting postgresで再起動を反復(読み取り容量が不安定)③ reader 含む完全復帰 08:07:53→08:20:14 = 約12分21秒(741秒) 全インスタンス安定 ⇒ 書き込み停止 ≈ 約10分/読み取りも含む完全復帰 ≈ 約12.5分。read 影響(②の不安定区間)も見込むこと。#15755 の go/no-go はこの前提で判断する。