Repository navigation
feat(installer): PostgreSQL を 18.6 へ上げ、検証つきのダンプと復元で既存データを移す db upgrade を加える - #42
Conversation
…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
|
REV(担当レビュワ, Codex Astra)レビュー r1: changes requested(要修正) — head
MD5 は隔離開発環境の暫定例外としてのみ許容。ロールを分ければ CAS MD5 / osf.io SCRAM は併存可能ですが、現状は同じ 検証: システム |
レビュー 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
|
REV(担当レビュワ, Codex Astra)レビュー r2: changes requested(要修正)。対象 R1-03(容量)・R1-05(is_called/roles DB)は解決。システム Python 3.9.6 で installer 573 件(skip 10)、tools 93 件、docs_check、manifest_check は全て終了 0。独立の単発クラッシュ注入 162 箇所も復旧しましたが、次が残ります。
ADR-13 の所有者選択と測定範囲の限定は確認しました。実スタック/既存 runtime/.env は未操作。詳細はレビュー記録 |
レビュー 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
|
REV(担当レビュワ, Codex Astra)レビュー r3: changes requested(要修正)。対象 R2-01〜04 の元の再現は解消。独立の162箇所クラッシュ行列、二重改名失敗と再復旧、up/reset と DB 操作の競合、同期・文字コード試験に合格。system Python の installer 583件(skip 10)、tools 93件、docs_check、manifest_check はすべて終了0。 R3-01 / P2: レビュー記録: |
レビュー 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
|
REV(担当レビュワ, Codex Astra)レビュー r4: pass(合格)/R3-01 解決。 対象 system python3: installer 587件(skip 10)、tools 93件、docs_check、manifest_check、独立プローブはすべて終了0。記録: |
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
概要
M5 の下準備として、開発環境の PostgreSQL を 9.6(EOL)から 18.6(ダイジェスト固定)へ上げ、既存データを移す
grdm-dev db status / upgrade / rollbackを加えます。Elasticsearch 2 と MongoDB は撤去せず、測定した事実と退役案を ADR-13 にまとめました。dbサブコマンド、upの拒否)・§6(終了コード 15)、ステータスボード §2c(M5 の残件)振る舞い
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 には未接触)
osf_init終了 0(migration 673、機関 68)。db upgradeを 9.6 データの写し(osf 262 表 16030 行、cas 9 表)に実行: 37 秒で終了 0、531 件一致。更新後、9.6 で作った利用者が CAS でログイン。db rollback後に 9.6.24 が同じデータで起動し、upは終了 15。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 でも合格。所有者の判断事項(5 件)
mongoexportで書き出すか、捨てるか。🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01BW1YQk71jLXY2FaSzMnnYJ