Skip to content

[CCIP] LanceDBからPostgresへvectorを移行して安全に切り替える #616

Description

@hmjn023

Parent: #612

Depends on: #615

背景

既存のDrizzle migrationはLanceDBを読めず、extract-ccip-batch.ts は全件再推論であってデータ移行scriptではない。既存embeddingを再計算せず、欠損・重複・検索結果の変化を検証しながらPostgreSQLへ移す専用backfill/cutoverが必要。

スコープ

  • LanceDB media_ccip をpage/batch単位で読むapplication migration script
  • 768次元、UUID、media FK、model/version、timestampの検証
  • --dry-run、batch size、source filter、再開、冪等upsert
  • 件数・media ID差分・model/version内訳・orphanの検証report
  • サンプルanchorのcosine値と最終top-K/reranking比較
  • 最終差分同期、read backend切替、rollback手順
  • 検証期間中はLanceDBをread-only backupとして保持する

非スコープ

  • source dump/export用LanceDB cacheの撤去
  • CCIP再推論
  • crop embeddingの生成

受け入れ条件

  • dry-runではDB/LanceDBを変更しない
  • 中断後に重複なく再開できる
  • 移行前後のID集合・件数・metadata差分を機械的に検証できる
  • 検証対象anchorで検索結果の許容差を確認できる
  • cutover後に新規抽出・更新・削除がPostgresだけで完結する
  • 問題時にLanceDB backendへ戻せる
  • CCIP用とsource dump用のLanceDB設定・削除範囲を混同しない

主な参照

  • apps/server/src/infrastructure/ai/lancedb-ccip-vector-store.ts:11
  • apps/server/scripts/extract-ccip-batch.ts:103
  • apps/server/scripts/optimize-ccip-lancedb.ts:5
  • packages/application/src/services/lancedb-dump-service.ts:351
  • packages/core/src/domain/config/config-schema.ts:154

評価反映(2026-07-18)

以下を移行・cutoverの追加必須条件とする。

事前データ監査

2026-07-18時点のCCIP用LanceDBには次の状態が確認された。

  • raw rows: 62
  • unique media IDs: 46
  • duplicate groups: 16

現時点の重複はvector、model、version、source、media revisionが一致し、主にextracted_atだけが異なる。実行時にも再集計し、reportへrawRowsuniqueLogicalRowscollapsedDuplicatesを必ず記録する。

重複keyは(mediaId, model, embeddingVersion)とする。同一keyの内容が一致する場合だけ最新のextracted_atを採用してcollapseし、vectorまたは入力metadataが競合する場合は自動選択せず移行を失敗させる。orphan、不正UUID、不正次元も黙ってskipしない。

再開可能なbackfill

LanceDBの現在のlist()順序には依存しない。移行元snapshotのfingerprintと、決定的にsortされたkeyに基づくcheckpointを保存し、入力snapshotが変わった場合は古いcheckpointで再開しない。

backfillは次の順で冪等に実行する。

  1. 対象mediaの唯一のkind=full regionを確保する
  2. (region_id, model, embedding_version)でembeddingをupsertする
  3. checkpointと件数・差分reportを更新する

dry-run

--dry-runはPostgresだけでなくLanceDBも一切変更しない。openOrCreateTable、index作成、schema更新など副作用を伴う経路を使用せず、真にread-onlyなreaderまたは検証用snapshotを使用する。実行前後のLanceDB file manifestを比較し、変更がないことを自動確認する。

Cutoverとrollback

「cutover後はPostgresだけへwriteする」と「静的なLanceDBへrollbackできる」は両立しないため、本Issueでは次のrollback windowを採用する。

  1. LanceDBを正本としたままinitial backfillを行う
  2. new/update/deleteを単一service経由でPostgresとCCIP用LanceDBへdual-writeする
  3. final deltaを適用してparityを確認する
  4. read backendをPostgresへ切り替える
  5. rollback観測期間中はdual-writeを継続する
  6. 観測期間終了後にLanceDB writeを停止し、以後はLanceDB rollbackを保証しない

rollback期間中は両方への永続化が確認できないwriteを正常完了扱いにせず、失敗または再調整対象として記録する。LanceDBへreadを戻せるのはdual-write completenessが成立している期間だけとする。dual-writeを採用できない場合は、代替としてCCIP writeをfreezeしたcutoverを明示的に選び、rollback可能期間中はfreezeを解除しない。

Parity判定

現在の論理46件については全件をanchorにできるため、代表サンプルだけでなく全論理recordを検証する。

  • 重複collapse後のID/key集合が完全一致する
  • model/version/input revisionが完全一致する
  • cosine値の絶対差を1e-6以下とする
  • top-Kのmedia集合と順序を一致させる。ただし差が1e-6以内の同点候補は同一tie groupとして扱う
  • Rust再ranking後のmedia集合・順序・distanceが同じ規則で一致する
  • 不一致、orphan、collapse、skip件数を機械可読reportへ出力する

追加受け入れ条件

  • 実行時のraw/unique/duplicate/conflict件数がreportされる
  • 内容が異なる重複やorphanを黙って移行しない
  • snapshot fingerprint付きcheckpointから決定的に再開できる
  • dry-run前後でLanceDB file manifestが変化しない
  • 上記の数値基準で全論理recordのparityを検証できる
  • read切替後のrollback windowと、その終了条件が設定・運用手順に記載される
  • source dump/export用LanceDBには設定・script・削除処理のいずれも影響しない

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions