データベース進化
August 19, 2026 · View on GitHub
TokenHub は明示的かつフォワードオンリーなマイグレーションでデータベースを進化させます。このページでは進化モデル、メンテナンスコマンド、そしてアップグレードとロールバックがデータベースとどう関わるかを説明します。
このページは、リポジトリ内のデータベース進化ライフサイクルと安全契約の規範となる情報源です。backend/internal/dbschema、メンテナンス CLI、管理アップグレード、CI はこの契約に従います。
モデル
- 採用ベースライン:すべてのデータベースはマイグレーション台帳(
schema_migrations)を持ちます。旧リリースが作成したデータベースは次回起動時に採用されます。凍結されたスキーマフローで補完し、参照スナップショットと意味的検証を行い、ベースラインを記録します。新規データベースは凍結されたベースライン SQL から直接作成され、ORM フローは実行しません。 - 拡張マイグレーションは互換性のある構造を追加し、起動時に自動実行されます。収縮マイグレーションは古い構造を削除し、起動時には決して実行されず、前提条件が検証されたメンテナンスコマンドからのみ実行されます。
- チェックサムと dirty 状態:適用済みマイグレーションのチェックサムは毎回の起動で検証されます。非トランザクションマイグレーションの失敗は dirty マーカーを残し、修復されるまで起動を拒否します。トランザクションマイグレーションの失敗は自身のバージョンのみロールバックします。
- データバックフィルは独立した台帳(
data_backfills)に記録されます。ブロッキングバックフィルはインスタンスが準備完了を報告する前に完了する必要があります。オンラインバックフィルはサービスを継続しながら冪等なバッチで実行され、リースによってクラスタ全体で単一の論理タスクとして調整され、実行者の障害時は別のインスタンスが引き継ぎます。 - インスタンスハートビート:実行中の各インスタンスは TTL 付きで自身のリリースを公開します。未期限のハートビートを持つインスタンスが存在する間、収縮メンテナンスは実行を拒否します。
- ロールバック互換性:各リリースは自身が完全に実行できるデータベース状態の範囲を宣言します。管理ロールバックはまず読み取り専用の事前確認を行います。検証済み互換性記録のないリリースは
unknownとして拒否され、データベース状態が対象リリースの範囲外の場合はincompatibleとして拒否されます。管理コンソールはロールバック対象をデータベース互換・非互換・不明で表示します。
/readyz と /healthz は、台帳が dirty または検証不能な場合、ブロッキングバックフィルが未完了の場合に失敗します。保留中のオンラインバックフィルは準備完了に影響しません。
メンテナンスコマンド
メインバイナリは db サブコマンドを提供します。
tokenhub db status # 台帳、バックフィル、稼働中インスタンス
tokenhub db verify # 台帳チェックサム + 意味的スキーマ検証
tokenhub db prepare # 起動互換の採用と expand を実行(サービス提供なし)
tokenhub db migrate # 保留中の拡張マイグレーションを適用
tokenhub db repair --version <n> # dirty マイグレーションのクリア(検証済み修復のみ)
tokenhub db contract --dry-run # 収縮マイグレーションの事前確認
tokenhub db contract --backup-reference <ref> --maintenance
データベース接続は TOKENHUB_DATABASE_URL(またはデフォルトの SQLite パス)から解決されます。
contract は何かを実行する前に、次のすべてを要求します:全データバックフィルの完了、未期限のインスタンスハートビートが不存在、オペレーターが検証済みのバックアップ参照、明示的なメンテナンス宣言。SQLite では事前に内蔵バックアップを作成してください。PostgreSQL では自身で検証した外部バックアップ参照を指定してください。
運用メモ
- 採用ベースラインのないデータベースに対する
tokenhub db migrateは、先に通常のサーバー起動を案内します。採用はサーバーの直列化されたスキーマ処理の中で行われます。 - 拒否された contract はどの前提条件が失敗したかを示します。その時点では何も実行されていません。
- 旧リリースへロールバックした後も、旧リリースは現在のデータベースで動作し続けます。新しいリリースが戻ると台帳を再検証して進化を続けます。
- 管理アップグレードは、まず対象リリース自身のバイナリで
db prepareを実行します。サービス提供中のインスタンスハートビートを公開せずに、直列化された起動互換の採用と expand フローを実行するため、サポート対象の台帳導入前データベースもアクティブ化前に準備できます。その後db verifyが準備済みの台帳とスキーマを意味的に検証します。両方の成功後にのみ対象リリースをアクティブ化します。アクティブ化したリリースの最初の起動が失敗した場合は、前リリースを自動的に一度だけ再アクティブ化します(アップグレード中に contract を実行しておらず、前リリースの互換性記録がデータベース状態をカバーしている場合のみ)。2 回目の失敗ではバージョン切り替えを停止し、運用者の復旧に委ねます。 - 管理コンソールには読み取り専用のデータベース進化セクション(状態バージョン、準備状態、互換範囲、バックフィル、稼働中インスタンス)が表示されます。contract と repair の操作は設計上 CLI のみです。
開発者向け
- マイグレーションランナーは
backend/internal/dbschemaにあります。凍結されたベースライン SQL はbackend/internal/dbschema/migrations/の下に方言ごとに埋め込まれます。 - モデル変更後に SQLite ベースラインを再生成するには
UPDATE_BASELINE=1 go test ./internal/server -run TestSQLiteBaselineSQLIsCurrentを実行します。PostgreSQL ベースラインはTEST_POSTGRES_URLを設定しintegrationビルドタグを付けて同様に再生成します。ベースラインが古いとテストは失敗します。 - CI は PostgreSQL 統合スイートに加え、SQLite と PostgreSQL で v0.4.0 の N-1 双方向契約を実行します。旧リリースがデータベースを作成し、現在のリリースが採用して準備完了を報告した後、旧リリースが再起動して API 契約(認証、プロジェクト、API key、Provider、Model と Route、1 回のゲートウェイリクエスト、監査書き込み)を完了します。現在のリリースは戻った後、永続化された一部のレコードを読み取ります(両方の方言でプロジェクトとモデル、SQLite では Provider も確認)。別の SQLite フローでは実際の v0.5.0 スキーマ形状を固定し、採用後に両方のリリースが起動できることを確認しますが、CRUD 契約や v0.5.0 の PostgreSQL フローは実行しません。
backend/internal/dbschema/fixtures/のコミット済み不変 fixture は、両方の方言の v0.4.0 と SQLite の v0.5.0 を対象とし、CI は採用前にgo run ./cmd/n1checkで各対象データベースを照合します。 - レジストリまたはベースライン変更後に埋め込みマイグレーション manifest を再生成するには
backend/でgo run ./cmd/manifestgenを実行します。埋め込みコピーが古いと CI が失敗します。