全体像(俯瞰)
[ユーザー/Dev-Portal]
│ (自然文 or SQL)
▼
API /nlq/plan ──→ SQL候補 + ガード適用 (P5)
│
│(検証済SQL)
▼
API /nlq/execute ──→ DB実行 → stats構築
│ │
│ ├─ guard/sql の時間計測 → stats.timeline
│ ├─ (既存) Metabase Card 作成/再利用 → stats.metabase / stats.metabase_items
│ └─ (任意) AI quick分析 → stats.analysis
│
└─(結果・stats返却)→ Dev-Portal
│
│(履歴保存要求)
▼
API /nlq/history(POST) ─→ stats.* を Artifacts[] に正規化
│
└─ nlq_history に保存(artifacts, stats, created_at)
│
├─ GET /nlq/history で参照(一覧/検索)
└─ [常駐] metabase-gc サービス
│
├─ Metabase から NLQカード一覧取得
├─ nlq_history.artifacts の id/hash と突合
├─ Dashboard配下は保護(KEEP_DASHBOARDED)
└─ TTL超過かつ参照なしのみ削除(DRY_RUN可)
ステップ別フロー
A. 計画 → 実行 → 履歴保存(Artifacts化)
- /nlq/plan
- 入力(自然文/SQL)から 候補SQL を返し、P5ガードを適用した validated_sql も提示。
- ここではまだ DB や Metabase には触れません。
- /nlq/execute
- ガード適用 → 実行。結果行(サンプル or フル)と共に
statsを構築。 stats.timelineにguard/sql/metabase/analysisの各フェーズ時間を記録。- (既存ロジック)Metabase Card 作成/再利用があれば
stats.metabase(単体)またはstats.metabase_items(複数)に、mode, resource, id, hash, url or iframe_src, reused等を格納。 - (任意)AI quick 分析の要約等を
stats.analysisに格納。
- ガード適用 → 実行。結果行(サンプル or フル)と共に
- /nlq/history(POST)
- 受け取った
stats.metabase/stats.metabase_items[]を Artifacts配列へ変換(kind:"metabase",mode,resource,id,hash,url,iframe_src,created_at)。 - 既存の
artifacts[]があれば追記し、重複除去してnlq_historyに保存。 - DBには
artifacts jsonb/stats jsonb/created_atを保持(DDL は 060_* にて追加済み)。
- 受け取った
- /nlq/history(GET)
session_idやcursorで検索・ページング。- 各履歴行に
artifacts[]が載っているため、Dev-Portal から Metabase の可視化へ遷移できる(public/signed どちらでも)。
ポイント:重いデータ本体は保存しない。参照に必要な **可視化リンク(Artifacts)**だけを履歴へ。
B. Metabase 側のクリーンアップ(TTL清掃)
- metabase-gc サービス(docker-compose.gc.yml)
- 既定では DRY_RUN=true で起動し、一定間隔(
GC_INTERVAL_SECONDS)ごとに実行。 - Metabase に対し認証(API Key / Session / Username+Password)でアクセス。
- 既定では DRY_RUN=true で起動し、一定間隔(
- 候補収集
/api/cardで全 Card(Question)を列挙。- NLQカード判定(いずれか該当):
nameがNLQ_CARD_PREFIX(例:NLQ:)で始まるvisualization_settings.owner="nlq"descriptionにowner=nlqorhash=...を含む
- 保護ルール
KEEP_DASHBOARDED=trueの場合、Dashboard に配置されている Card は保護(削除対象外)。
- 履歴参照との突合
- DB の
nlq_history.artifactsを走査し、参照中のid/hashをメモリ集合に取り込み。 - カードの
idが一致、またはhashが一致する場合は 削除対象外。
- DB の
- TTL 判定
created_at(なければupdated_at)からGC_TTL_DAYS超過のみ 候補。- DRY_RUN=false の時だけ 実削除。実行結果は JSON1行でログ出力(収集しやすい)。
これにより、孤立して古くなった一時Cardだけが減っていき、Metabase 側が散らかりません。
主要な“受け渡しデータ”と責務
| 接点 | 送受信データ(主要) | 責務 |
|---|---|---|
/nlq/plan → クライアント | candidate_sql, validated_sql, display_hint | ガード済みの候補提示 |
/nlq/execute → クライアント | rows, row_count, stats(timeline / metabase* / analysis) | 実行結果と付帯メタ |
/nlq/history(POST) → DB | artifacts[], stats(軽量サマリ), created_at | Artifacts化・保存 |
metabase-gc → Metabase | DELETE /api/card/{id} | NLQ 管理カードのTTL清掃 |
metabase-gc → DB | SELECT artifacts | 参照中(id/hash)の抽出 |
代表的な決定ロジック
- Card再利用:正規化後 SQL の ハッシュ(
MetabaseLink.hash)で既存Cardを探索し、見つかればreused:true。 - Artifacts化:
stats.metabase単体も、stats.metabase_items配列も 両方artifacts[]に取り込む。 - 清掃判定:
- NLQカードか? → Yes
- Dashboard配下か? → Yesなら保護
- 履歴参照(id/hash)があるか? → Yesなら保護
- TTL超過か? → Yesなら削除候補(DRY_RUNで確認→本削除)
可搬性と運用ポイント
- ENV だけで切替:public(dev)⇄ signed(prod/K8s)を
METABASE_MODEで切替。 - タイムゾーン固定:
TZ=Asia/Tokyo(時刻整合/TTL判定の安定化)。 - 索引:
(session_id, turn_no)/(created_at)/GIN(artifacts)により検索/GCが軽量。 - オブザーバビリティ:
stats.timeline(フェーズ別の処理時間)- GC の JSON サマリ(
checked,candidates,deletedなど)
失敗時のふるまい(要約)
- /nlq/execute の quick 分析失敗:HTTP 200は維持し、
analysis=None・タイムラインnote:"error"。 - Artifacts 生成失敗:
/nlq/history(POST)側で artifacts を空配列にして保存(致命的にしない方針が安全)。 - Metabase 認証不可:GCは
errorsに記録し exit code 0(ループ停止を防止)。本番は監視で検知。
これが今回の 全データフローです。
要は「/nlq/execute が stats を返す → /nlq/history がそれを Artifacts に正規化して保存 → GC が Artifacts と TTL を手がかりに Metabase を綺麗にする」という三位一体の動きになっています。
コメントを残す