Skip to content

feat(installer): PostgreSQL を 18.6 へ上げ、検証つきのダンプと復元で既存データを移す db upgrade を加える - #42

Merged
Yusuke-KOMIYAMA merged 6 commits into
developfrom
feat/m5-data-tier
Nov 10, 2026
Merged

Yusuke-KOMIYAMA merged 6 commits into
developfrom
feat/m5-data-tier

Conversation

@Yusuke-KOMIYAMA

Copy link
Copy Markdown
Contributor

概要

M5 の下準備として、開発環境の PostgreSQL を 9.6(EOL)から 18.6(ダイジェスト固定)へ上げ、既存データを移す grdm-dev db status / upgrade / rollback を加えます。Elasticsearch 2 と MongoDB は撤去せず、測定した事実と退役案を ADR-13 にまとめました。

振る舞い

  • postgres:18.6@sha256:e3d24f69…。テンプレートは PGDATA=/var/lib/postgresql/data と /var/lib/postgresql の tmpfs で、18 のイメージの無名ボリュームを避けます。
  • --auth-host=md5 は意図して残します(CAS の JDBC 42.1.4 は SCRAM で authentication type 10 is not supported。測定)。
  • db upgrade: 全サービス停止と確認の後、旧版をネットワーク・TCP なしで起動し、新版の pg_dumpall で runtime/backups/ へダンプし、新しいディレクトリへ ON_ERROR_STOP で復元し、全表の行数・シーケンス値・拡張・ロールを照合して一致したときだけ改名します。旧ディレクトリは postgres_data.pg9.6-<時刻> として残り、db rollback で戻せます。失敗時は何も改名せず終了 15。
  • up はデータディレクトリの版と生成済みイメージの版が違えば Compose の前に終了 15 で止まります。

検証(2026-11-10、arm64、使い捨ての Compose プロジェクト m5-*。grdm-dev には未接触)

  • 18.6 の新規クラスタで osf_init 終了 0(migration 673、機関 68)。
  • osf.io の DB 試験 7 モジュール: 9.6 と 18 で同一(781 合格、3 失敗、10 エラー、13 スキップ。失敗・エラーは両方で同じ集合)。
  • CAS: 誤パスワード 200・チケットなし、正パスワード 302、serviceValidate 成功。fakecas: 302/誤った鍵 404/未知の利用者 404。
  • 実際の db upgrade を 9.6 データの写し(osf 262 表 16030 行、cas 9 表)に実行: 37 秒で終了 0、531 件一致。更新後、9.6 で作った利用者が CAS でログイン。db rollback 後に 9.6.24 が同じデータで起動し、up は終了 15。
  • ゲート(origin/develop 48d0134 へ rebase 後): PYTHONPATH=installer python3 -m unittest discover -s installer/tests 終了 0(python3 3.15、548 件、skipped 10)、python3 tools/docs_check.py 終了 0、python3 tools/manifest_check.py 終了 0。/usr/bin/python3(3.9)では develop と同じ既存の 12 エラーで、本変更の試験は 3.9 でも合格。
  • 未確認: amd64 ホスト、データが uid 999 所有の Linux ホスト、PR fix(accept): コア受入 C1〜C6 を全件合格させ、固定版をセキュリティ修正の取り込み後へ更新 #40 で公開された sharejs のファイルページのエディタのブラウザ確認。

所有者の判断事項(5 件)

  1. ADR-12 と ADR-13 の承認(どちらも提案中)。
  2. Elasticsearch 2 の扱い: (A) ネイティブの ES 2.4.6 を残す/(B) 検索を切り、約 50 行の osf.io パッチで全入口を空の結果にし協力者検索を PostgreSQL へ移す(試作で全入口 200 を確認、全文検索は失う)/(C) ES 8 か OpenSearch へ約 5,300 行を移植する。
  3. ファイルページのエディタ: (a) 共同編集なしの素のエディタにする/(b) y-websocket 上に作り直す。sharejs と mongo の退役で失われるのはこの共同編集です。
  4. mongo に残る sharejs の下書き: mongoexport で書き出すか、捨てるか。
  5. PostgreSQL の SCRAM 移行: RDM-cas-overlay-dev2027 で CAS の JDBC ドライバーを 42.2 以降に上げるか(それまでは md5)。

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ

…e を加える

M5 の下準備。PostgreSQL 9.6(2021 年 EOL)を、リリース済みで最新の 18.6 へ
移す(19 は Docker Hub ではまだ 19beta1)。イメージはインデックスのダイジェスト
sha256:e3d24f6906d0760bf4da0dae872415391ededefad23fd471c47dc53b0ddf3c42 で固定する。

変更
- manifest の postgres を postgres:18.6@digest に。テンプレートは PGDATA を
  /var/lib/postgresql/data に戻し、/var/lib/postgresql に tmpfs を置く
  (18 のイメージの VOLUME で無名ボリュームが作られないように)。
- --auth-host=md5 は意図して残す。initdb が password_encryption=md5 も書く。
  CAS の JDBC 42.1.4 は SCRAM で「authentication type 10 is not supported」
  になる(測定)。SCRAM は CAS が JDBC 42.2 以降を持つまで所有者の判断事項。
- 新しいサブコマンド grdm-dev db status / upgrade / rollback(database.py)。
  upgrade は全サービス停止と確認の後、旧版(postgres:9.6@digest)を
  ネットワークなし・TCP なしで旧ディレクトリに起動し、新版の pg_dumpall で
  runtime/backups/ に書き(0600、0700 のディレクトリ)、新しいディレクトリへ
  ON_ERROR_STOP で復元し、全表の行数・シーケンス値・拡張・ロールを突き合わせ、
  一致したときだけ同じ親の中で改名する。旧ディレクトリは
  postgres_data.pg9.6-<時刻> として残り、rollback で戻せる。失敗時は何も改名しない。
- up は PG_VERSION と生成済みイメージの版が違えば compose の前に終了 15 で止まる。
  終了コード 15 を messages.tsv と SPEC-02 §6 に追加。
- ADR-12(PostgreSQL 18、提案中)と ADR-13(ES2・mongo の事実と退役案、提案中)。
  ADR-08〜11 は別ブランチの記録のため索引で予約・予定とする。
  SPEC-02、ステータスボード §2c、REF-01、hardening.json の target を更新。

選ばなかった案
- pg_upgrade: 両版のバイナリを持つイメージは第三者製のみ。--link は旧ディレクトリに
  書く。glibc 2.24(9.6 のイメージ)から 2.41(18)へ照合規則が変わっており、
  文字列の索引をそのまま使うことになる。ダンプと復元なら索引は作り直される。
- 15〜17 で止める: 18 は同じ試験に合格し保守期間が最も長い。

測定(2026-11-10、arm64、使い捨ての Compose プロジェクト m5-*。grdm-dev には未接触)
- 18.6 の新規クラスタで osf_init 終了 0(migration 673、機関 68)。
- osf.io の DB 試験(test_node、test_user、test_guid、test_institution、
  test_analytics、api_tests/institutions/views、osfstorage test_models):
  9.6 と 18 で同一の結果(781 合格、3 失敗、10 エラー、13 スキップ。失敗とエラーは
  両方で同じ集合)。
- 18 で CAS: 誤パスワード 200・チケットなし、正パスワード 302・チケット、
  serviceValidate 成功。fakecas: 302、誤った鍵 404、未知の利用者 404。
- 実際の db upgrade を 9.6 のデータ(osf 262 表 16030 行、cas 9 表)の写しに実行:
  37 秒で終了 0、531 件が一致。更新後のスタックで osf_init は No migrations to
  apply、9.6 で作った利用者が CAS でログイン。db rollback の後 9.6.24 が起動し
  データがそろい、up は終了 15 で止まった。最初の 2 回の失敗(initdb の
  パスワード、18 が拒む text || "char")は postgres_data を変えずに終了 15 で止まり、
  修正して単体試験で押さえた。
- ES2: ES 6.8.23 と OpenSearch 2.19.5 では Content-Type の互換層を入れても
  型が一つのインデックスと query_string の構文で全検索が失敗。検索を切ると読み取りの
  入口が 500/null。約 50 行の試作で全入口 200、協力者検索は PostgreSQL へ。
- mongo: osf.io の pymongo はこの環境で mongodb://postgres:5432/sharejs を指し
  30.3 秒で ServerSelectionTimeoutError。使い手は sharejs だけ。PR #40 で sharejs は
  127.0.0.1:7007 に公開されたので、退役で失うのはファイルページの共同編集(ブラウザでは未確認)。

ゲート(origin/develop 48d0134 へ rebase 後): python3 3.15 で unittest OK(skipped 10。
/usr/bin/python3 3.9 では develop と同じ既存の 12 エラー)、docs_check PASSED、manifest_check PASSED。
未確認: amd64 ホスト、uid 999 所有の Linux ホストでの実行。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ
@Yusuke-KOMIYAMA

Copy link
Copy Markdown
Contributor Author

REV(担当レビュワ, Codex Astra)レビュー r1: changes requested(要修正) — head 2b6a02a。

  • P1 database.py:458–474,501–506: 二回の改名は全体として原子的ではなく、第二改名の失敗で現行パスが消え、巻戻し記録もないことを障害注入で再現。「現行は無変更」の報告も誤り。切替前の永続記録と復旧・起動拒否が必要です。
  • P1 :280–283,420–421,455–461: stop/rm の失敗を無視して終了 0、中断時は cleanup なしと再現。単独移行サーバーは Compose の稼働確認外なので、停止確認とクラッシュ後も有効な排他・復旧が必要です。
  • P2 容量検査がありません(ダンプは加工前後の二部を保持)。版読取り失敗 None も起動を許可します(:157–159)。シーケンスの is_called が比較されず、roles という DB の検査結果は上書きされます(:64–74,248)。

MD5 は隔離開発環境の暫定例外としてのみ許容。ロールを分ければ CAS MD5 / osf.io SCRAM は併存可能ですが、現状は同じ postgres を共用しています。ADR-13 の「下書きは一度も届いていない」は失敗した wiki reader に限定すべきです(ファイルエディタは別途 PUT 保存します)。両 ADR は Proposed のままです。

検証: システム /usr/bin/python3 3.9.6 の全単体試験 exit 1(431 件、既存の型注釈/import エラー 12、skip 10)、database 17 件 exit 0、docs_check / manifest_check exit 0。実スタック・既存 runtime/.env は未接触。英和詳細記録は独立クローン内 docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r1.md(未コミット、コーディネータへ引渡し)です。

レビュー r1(docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r1.md、
無変更で同梱)の指摘への対応。

R1-01(切替と巻戻し): 何かを作る前に runtime/db-operation.json を fsync と原子的改名で
書き、コンテナ・ボリューム・ディレクトリの名前を記録する。改名の前に phase=swap を記録し、
二つ目の改名の失敗は一つ目を戻して補償する。upgrade.json を書いてから操作記録を消す。
recover() が swap 前なら巻き戻し、swap 後なら完了させる(不可能なら巻き戻す)。
compose render が作った空の postgres_data は先に除く。rollback も同じ操作記録で行う。
R1-02(停止と中断): 移行サーバーは停止・削除の後に docker ps -a で不在を証明し、証明
できなければ操作記録を残す。Docker が答えない場合は不在とみなさない。排他 flock を操作中
保持し(プロセス終了で解放)、SIGTERM・SIGHUP は Ctrl-C と同じく後始末する。
db recover を追加し、db upgrade・db rollback・reset・up が最初に復旧を行う。
R1-03(容量): 旧サーバー起動前に backups 側へクラスタの 2 倍+64 MiB、volumes 側へ
2 倍+1 GiB(同一ファイルシステムなら和)を求める。見積りであり保証ではないと ADR-12 §5 に
明記。restore.sql は復元後に消す。
R1-04(版): 読めない・解析できない・空でないのに PG_VERSION が無い場合は up が終了 15。
空と確定できるのはディレクトリが無いか空のときだけ。
R1-05(照合範囲): シーケンスは last_value と is_called、DB とロールは別の名前空間、ロールは
属性と検証子の MD5。行内容と拡張の版は比較しないと明記。移行サーバーは
log_min_error_statement=panic で失敗文をログに残さない。

文書: ADR-12 0.2(手順を fenced の手順と期待結果に、MD5 は隔離された開発網に限る暫定例外と
明記)、ADR-13 0.2(主張を測定範囲に限定、試作コードを掲載、2026-11-10 の所有者の判断:
ES8 への移植で全文検索を保つ、ファイルページは y-websocket へ移してから sharejs と mongo を
退役、M4 では MFR から freecad を除き修正の無い OS パッケージを許容リストへ)、SPEC-02 0.20、
ステータスボード 0.13。test_compose.py と test_plan.py に from __future__ import annotations を
加え、/usr/bin/python3(3.9)でも全試験が通るようにした。

実測(2026-11-10、arm64、使い捨ての m5-*。grdm-dev には未接触): 新しく作った 9.6 の写し
(osf_init、利用者一人、roles という DB、setval('s', 5, false))で、
- 9.6 サーバー稼働中に SIGKILL → 次の up が補助コンテナとボリュームを除き巻き戻して終了 15、
  残り 0。
- 18 サーバー稼働中に SIGTERM → プロセス内で巻き戻して終了 15、残り 0、postgres_data は 9.6。
- その後の db upgrade は終了 0、4 DB(roles を含む)とロール 1 で 524 件一致。更新後は
  osf_init が No migrations to apply、9.6 で作った利用者が CAS でログイン、fakecas 302、
  nextval('s') は 5(is_called false を保持)。
- db rollback は終了 0、記録に rolled_back_at、9.6.24 が起動し up は終了 15。
単体試験: database 34 件(6 段階ごとの失敗・中断・クラッシュ、両改名、操作記録の 4 回の書込み
の ENOSPC、ダンプ・加工・復元の ENOSPC、別プロセスのロック、空の placeholder)。

ゲート: PYTHONPATH=installer /usr/bin/python3 -m unittest discover -s installer/tests 終了 0
(3.9.6、OK skipped 10)、python3 3.15 でも終了 0、docs_check 終了 0、manifest_check 終了 0。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ
@Yusuke-KOMIYAMA

Copy link
Copy Markdown
Contributor Author

REV(担当レビュワ, Codex Astra)レビュー r2: changes requested(要修正)。対象 5780b16。

R1-03(容量)・R1-05(is_called/roles DB)は解決。システム Python 3.9.6 で installer 573 件(skip 10)、tools 93 件、docs_check、manifest_check は全て終了 0。独立の単発クラッシュ注入 162 箇所も復旧しましたが、次が残ります。

  • P1 R2-01: database.py:576–594:復旧中、置換改名と補償改名が連続 EIO になると、現行パスが消えたまま journal を削除し終了 0(upgrade/rollback 両方で再現)。記録を保持して終了 15 にしてください。
  • P1 R2-02: recover_pending:625–636 と lifecycle:journal がない間はロックを迂回し、復旧後も起動/削除前に解放。rollback の停止検査後・確認中に up が Compose 起動へ到達し、その後 rollback が改名する競合を再現。起動・reset も同一ロックで操作全体を保護してください。
  • P1 R2-03: 復旧の直接改名(571/573/596 行)後に volumes 親の fsync がなく、journal を削除。_fsync_dir も EIO を無視します。永続化の順序と失敗時の保持を修正してください(電源断自体は未実施)。
  • P2 R2-04: PG_VERSION に非 ASCII バイトがあると UnicodeDecodeError が未捕捉。Compose は止まりますが、契約どおり終了 15 にしてください。

ADR-13 の所有者選択と測定範囲の限定は確認しました。実スタック/既存 runtime/.env は未操作。詳細はレビュー記録 docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r2.md(独立クローンで作成、未コミット)に記載しています。

 レビュー r2)

レビュー r2(docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r2.md、無変更で同梱)
への対応。

R2-01: 復旧中の切替が失敗したとき、現行ディレクトリが期待する版で元の場所にあると確かめた
場合だけ操作記録を消す(_undone)。補償の改名も失敗したら操作記録を残して終了 15。up は拒み、
db recover が再試行する。
R2-02: up は確認から docker compose up まで、reset は確認から削除の終わりまで、db の各コマンドと
同じ runtime/db-operation.lock を取る(lifecycle_lock)。flock は別々の open で取るので同じ
プロセス内の割込みも排除する。ロックが取られていれば up と reset は終了 15。
R2-03: 復旧の改名のたびに runtime/volumes/ を fsync してから記録を書き操作記録を消す。
_fsync_dir は open と fsync の失敗を伝える(ディレクトリを sync できない EINVAL・ENOTSUP のみ許容)。
R2-04: PG_VERSION の非 ASCII(UnicodeDecodeError)と、コンテナ読取りの出力の復号失敗を版不明
(終了 15)にする。
文書: ADR-12 0.3(クラッシュ復旧の主張をプロセスのクラッシュと注入試験に限定し、電源断は未試験と
明記。満杯時の二つの安全な結果を記述)、ADR-13 0.3(Records a decision for を §4 に合わせる)、
SPEC-02 0.21。

試験: database 44 件(追加 10 件: 復旧中の二重失敗と再試行、プロセス内の二重失敗、確認済み補償、
ロック保持中の up、rollback の確認時に割り込む up、up の compose 起動中の rollback・upgrade・
recover、reset と db の相互排除、復旧の sync 順序、ディレクトリ fsync の EIO と EINVAL、
復旧中の sync 失敗と再試行、非 ASCII の PG_VERSION を up・upgrade・status から)。
レビューの probe(/tmp/pr42-rev-r2/probe.py): 期待を修正後の契約に置き換えた版は終了 0
(162 境界の単一クラッシュ行列 PASS、二重改名失敗は終了 15・操作記録あり・再試行で 18/9.6、
ロック中の up は 15 で compose なし、非 ASCII は 15、復旧で volumes を sync、fsync EIO は伝播、
rollback 競合で compose up なし)。元の probe はバグの再現を主張するので最初の再現の assert で
終了 1(再現しなくなった)。

ゲート: PYTHONPATH=installer /usr/bin/python3 -m unittest discover -s installer/tests 終了 0
(OK skipped 10)、/usr/bin/python3 -m unittest discover -s tools/tests 終了 0、docs_check 終了 0、
manifest_check 終了 0。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ
@Yusuke-KOMIYAMA

Copy link
Copy Markdown
Contributor Author

REV(担当レビュワ, Codex Astra)レビュー r3: changes requested(要修正)。対象 5780b16..7e61df3。

R2-01〜04 の元の再現は解消。独立の162箇所クラッシュ行列、二重改名失敗と再復旧、up/reset と DB 操作の競合、同期・文字コード試験に合格。system Python の installer 583件(skip 10)、tools 93件、docs_check、manifest_check はすべて終了0。

R3-01 / P2: database.py:759 の run_upgrade は UpgradeError だけを捕捉し、765行の初回 recover からの OSError が CLI に漏れます。二つの改名完了・操作記録保持の状態で volumes 親の fsync に EIO を注入し、実 cli.main(... db upgrade) が終了15でなく未捕捉 OSError になることを再現しました。現行18と操作記録は保持され、障害除去後の recover は成功(データ喪失ではなく終了値の契約違反)。初回復旧を含むロック全体で OSError を終了15へ変換し、CLI入口の試験を追加してください。

レビュー記録: docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r3.md(未コミット、コーディネータへ引渡し)。実 Docker 状態・既存 runtime/.env は未アクセス。電源断永続化・実DB移行は今回の独立検証範囲外です。

レビュー r3(docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r3.md、無変更で同梱)の
R3-01 への対応。run_upgrade はロック内の処理全体(最初の復旧を含む)で OSError を捕え、
操作記録が残っているかを示す文言で終了 15 を返す(_os_failure)。run_recover と run_rollback も
同じ文言にした。操作記録は残り、障害が消えた後の db recover が完了させる。

試験(cli.main 経由、Docker は偽物): 切替の改名後・記録前に保留した状態で volumes の fsync に
EIO を入れ、db upgrade・db rollback・db recover・up・reset のすべてが終了 15、操作記録が残り、
その後の db recover が終了 0。db upgrade・db rollback・db recover の正常実行で os.fsync の回数を
数え、各回に EIO を入れて終了 15(例外なし)、その後の db recover で終了 0、版は 9.6 か 18、
補助コンテナ 0。
レビューの probe: /tmp/pr42-rev-r3/probe.py は終了 0(all independent probes PASS)。extra.py は
再現を期待するので、期待を修正後の契約に変えた版で終了 0(db upgrade CLI exit 15、
later recovery PASS、lifecycle と db の相互排除 PASS)。

ゲート: PYTHONPATH=installer /usr/bin/python3 -m unittest discover -s installer/tests 終了 0、
/usr/bin/python3 -m unittest discover -s tools/tests 終了 0、docs_check 終了 0、manifest_check 終了 0。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ
@Yusuke-KOMIYAMA

Copy link
Copy Markdown
Contributor Author

REV(担当レビュワ, Codex Astra)レビュー r4: pass(合格)/R3-01 解決。

対象 7e61df3..d49945c。初回復旧の fsync EIO は実 CLI の全5入口(db upgrade/rollback/recover、up、reset)で終了15となり、保留 journal を保持し、障害除去後に復旧成功。全 fsync 20箇所(10/6/4)の EIO 注入、162箇所の中断行列、r3 の二重障害・排他等も合格し、対象差分に必須修正なし。

system python3: installer 587件(skip 10)、tools 93件、docs_check、manifest_check、独立プローブはすべて終了0。記録: docs/reviews/2026-11-10_RDM-developer-guide_PR42_PostgreSQL18_r4.md(ローカル未コミット)。実DB移行・物理電源断は未検証、既存 runtime/.env・grdm-dev Docker 状態は変更なし。

Yusuke-KOMIYAMA and others added 2 commits November 10, 2026 14:45
SPEC-02 の版の行は、develop の 0.19(osf.io の mongo TLS)を保ち、このブランチの項目を 0.20〜0.22 に振り直した。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ
REV(担当レビュワ)の r4 判定は合格。R3-01 の解消を確認。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ
@Yusuke-KOMIYAMA
Yusuke-KOMIYAMA merged commit 50740b1 into develop Nov 10, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant