ADR-0235: テレメトリー記録処理を独立したワークフローステップに分離し、多数のパラメータバインドをビルダーパターンで管理する (電帳法 KPI の観測性を強化する)
1. Executive Summary
- 文脈: drp のテレメトリー書込 (
saveTelemetryRecord= テレメトリー記録をデータベースに挿入する関数) は Workers Workflows の finalize step 内で inline 呼び出しされている。 - 課題: テレメトリー書込が失敗してもエラーログ出力のみで処理が成功終了し、電帳法 KPI (= 電子帳簿保存法に対応するための観測指標) 「終端 run 数 = テレメトリー行数」が silent に乖離する。
- 対策: テレメトリー書込を独立 Workflows step (
persist_telemetry= テレメトリー永続化専用のステップ) に分離し、retry 3 回・exponential backoff・timeout 10s を載せる。 - 効果: silent drop を alarm 経由で可観測化し電帳法 KPI の分子と分母の乖離を早期に検知でき、transient 失敗 (D1 rate limit / network flap) を retry で吸収し電帳法 KPI 分子の欠落を減らせる。
- 代償: 実装 3 時間・retry backoff 込で p95 latency +200-500ms 悪化し、alarm 集計は初期 console.log 運用で将来 Analytics Engine 移行が別途必要。
2. 何を解決するか
2.1 直面していること
信頼性:
drp/src/audit/persistence.ts:213-219の catch 節が telemetry 書込の SQL 構文エラー・列 drift・SQLITE_MISUSE を吸い込み run 終端は成功として返す。- 失敗した run は telemetry 0 行で残り、観測 SQL 側から見て「run 終端したが未計上」= 電帳法 KPI の分子分母乖離が silent に進行する。
2.2 放置すると起こること
信頼性:
- workflows-e2e (ADR-0197 系) が assert する KPI 「run 終端率 = telemetry 行数 / trigger 総数」は本番 D1 の telemetry を数える設計のため、silent drop が起きると分子が減り率が下振れする。
- monitor は「率下振れ」と読むが真因は「観測欠落」であり、誤診断で修正方向が意図と異なる方向 (run 側の障害調査) に流れる。
2.3 潜在リスク (実害未発生 · 発生確率不明)
信頼性:
- Workers Workflows の他 step (gate node) は Workflows retry 最大 3 回を活かす一方、finalize step 内 inline の
saveTelemetryRecordは step の一部として retry されるが直後 catch で吸われるため実質 no-retry になっている。 - 結果として transient (network flap / D1 rate limit) を吸収できない非対称性が残っている。
3. 採用したい方針
3.1 対策方針
drp/src/workflows/pipeline_run_workflow.ts (= 審査 pipeline の Workflows entry) の finalize 直前に persist_telemetry step を独立 step として挿入し、retry 3 回・exponential backoff・timeout 10s を載せる。最終 retry でも失敗した場合のみ silent-degrade catch を残し、alarm/metric emit した上で workflow 終端は継続する (電帳法 KPI を優先する既存の設計意図を維持しつつ観測欠落を可視化)。
- 信頼性: finalize 直前に
doStep('persist_telemetry', PERSIST_STEP_CONFIG, saveTelemetryRecord)を挿入しPERSIST_STEP_CONFIGは retry 3 回・exponential backoff・timeout 10s とする。 - 信頼性: 最終 retry でも失敗した場合のみ silent-degrade catch を残し alarm/metric emit した上で run 終端は継続する。
3.2 守るべき設計制約
- 信頼性: silent-degrade は最後の retry でも失敗した場合のみ許容し、alarm/metric emit で観測欠落の存在を可視化する (run 終端は継続 · 電帳法 KPI を優先する既存の設計意図を維持)。
- 運用性: ADR-0120 の「1 step.do = 1 Workers request」制約 (= per-step subrequest 上限 1000) を維持し、
persist_telemetrystep は D1 execute 1 回のみで subrequest 数 = 1 に収める。 - 適合性: 98 箇所の位置 bind を builder 化する際、builder の bind 追加順序と SQL 文字列の列順が 1 対 1 で対応することを保証する (順序変更で列値ずれ = 列 drift を新規発生させない)。
3.3 仕様
- 信頼性:
PERSIST_STEP_CONFIGは{ retries: { limit: 3, delay: '2s', backoff: 'exponential' }, timeout: '10s' }とする。 - 信頼性: 最終 retry 失敗時の alarm は既存 log pattern と互換な
console.log(JSON.stringify({ event: 'telemetry_persist_final_fail', session_id, error }))で emit する。 - 信頼性: alarm 発火を D1 専用テーブル
telemetry_persist_failuresにも永続化し console.log 保持期間 (デフォルト 1〜7 日) 消失で電帳法 7 年遡及調査に対応できなくなるリスクを回避する。 - 信頼性: 列 drift (migrate-v22 / v23 パターンの再発) に対する防御として deploy pipeline に「migration smoke test」(= 対象 D1 に対する INSERT dry-run を deploy 直前に実行し列不整合を事前検出する CI step) を追加する。
- 適合性: builder の bind 追加順序は「SQL 文字列の列出現順と同じ順」で追加するルールとし、期待列値を全列アサートする unit test (
drp/test/audit/persistence.builder.test.ts新設) で保証する。 - 運用性: alarm 集計先の Analytics Engine 移行は行き先を §11 に明記した上で別 ADR に送る。
4. 判断基準
4.1 評価軸
| # | 軸 (Q42) | 日本語軸名 | 重要度 (係数) | 案件特有の解釈 |
|---|---|---|---|---|
| 1 | #reliable | 信頼性 | Must (×2.0) | 電帳法 KPI 分子の silent drop を alarm/metric に 100% 出す · retry で transient を吸収 |
| 2 | #operable | 運用性 | Must (×2.0) | ADR-0120 の 1 step.do = 1 Workers request 制約遵守 · alarm 発火時の対応手順が runbook に整備されている |
| 3 | #suitable | 適合性 | Must (×2.0) | 「終端 run 数 = telemetry 行数」invariant の workflows-e2e が全 pass · builder 化で列値ずれを起こさない |
| 4 | #efficient | 効率性 | Medium (×0.5) | p95 latency 悪化幅 · Cloudflare Workflows step +1 の課金増 |
| 5 | #usable | 使いやすさ | Medium (×0.5) | 起案者体験 (run 終端可否) への影響 |
K.O. criterion: Must 軸 score < 3 は不採用。
4.2 評価軸 × 案スコア表
| 軸 | 係数 | 採択案 (独立 step + retry/alarm) | 案 A (fail-closed throw) | 案 B (Queues 非同期) |
|---|---|---|---|---|
| 信頼性 | ×2.0 | 4 | 3 | 4 |
| 運用性 | ×2.0 | 4 | 3 | 2 |
| 適合性 | ×2.0 | 5 | 2 | 4 |
| 効率性 | ×0.5 | 3 | 4 | 2 |
| 使いやすさ | ×0.5 | 4 | 2 | 3 |
| 加重和 (正規化) | 0.827 | 0.560 | 0.633 | |
| K.O. 通過 (Must ≥3) | ✓ | ❌ (適合性 = 2) | ✓ |
5. 検討した代替案
案 A (silent-degrade catch を fail-closed = throw に変える): run 終端が止まると電帳法 KPI 優先の既存設計意図に反する。
- 適合性: 電帳法 KPI 優先で run 終端を止めないという既存設計意図に反するため設計意図の override は別 ADR での合意が必要になる。
- 使いやすさ: run 終端が止まると起案者体験が悪化し LLM 課金コストが loss として残る。
案 B (Cloudflare Queues に telemetry を投げて非同期永続化): consumer 実装・dead-letter 監視まで運用面の増加が広い。
- 運用性: Queue 追加は consumer 実装・dead-letter 監視まで運用面の増加が広く、現行 D1 直書きに retry を足す変更に比べ観測性課題への対処として過剰。
- 信頼性: producer は run 終端のみ確定できる利点はあるが、非同期化に伴う dead-letter 経路の設計が別途必要になる。ただし本案の再検討トリガーとして「Workflows instance expire (18 時間) 以内に D1 が回復する」前提が崩れた場合 (例: D1 障害が 18 時間超継続) は §8 撤退条件で本案への移行 ADR 起票を義務化する。
6. 影響
6.1 正の影響
- 信頼性: 最終 retry 失敗時の alarm 経由で silent drop が可観測になり電帳法 KPI の分子分母乖離を早期に検知できる。
- 信頼性: Workflows retry 3 回で transient (D1 rate limit / network flap) を吸収し電帳法 KPI 分子の欠落を減らせる。
- 適合性: 98 位置 bind の builder 化で列順の SSoT が SQL 文字列側に集約され migrate-v22 / v23 パターンの列 drift 検出が容易になる。
6.2 負の影響 / リスク
- 効率性: workflow step 追加で run 全体の p95 latency が retry backoff 込で +200-500ms 程度悪化する可能性がある。
- 運用性: alarm/metric の集計先は初期 console.log + D1
telemetry_persist_failuresテーブルの二本立てで運用し、Analytics Engine 移行の受け皿 ADR 起票が別途必要になる (§11 に行き先を明記)。 - 信頼性: builder の bind 追加順序変更で列値ずれが発生するリスクは残る (§3.3 の unit test で最終的に検出する設計)。
6.3 中立・トレードオフ
- 信頼性: retry と alarm の導入で「観測性は解決済み」という確証バイアスが生まれ Analytics Engine 移行 ADR が期限を過ぎても放置される可能性がある (§11 で自動 issue 化の受け皿を明記)。
7. コスト試算
7.1 前提条件
| 項目 | 値 | 根拠 |
|---|---|---|
| 実装工数 | 3 時間 | step 分離 2h + config/test 1h |
| 追加 step 数 | +1 (~10 → ~11) | finalize 手前に persist_telemetry を挿入 |
| p95 latency 悪化 | +200-500ms | retry backoff 込 · 稀な transient 時のみ |
| Logpush / D1 失敗テーブル | 実装 1h | Cloudflare Logpush 設定 + telemetry_persist_failures migration |
| migration smoke test | 実装 1h | deploy pipeline に INSERT dry-run step 追加 |
7.2 月コスト試算
- Cloudflare Workflows: step +1 の追加分は定額課金の範疇で $0-2/month 増。
- ランタイム: 通常時は retry を発火しないので overhead は無視可能。
- D1 追加テーブル
telemetry_persist_failures: 想定書込 < 月 100 行で $0/month。 - 実装にかかる一次コスト: 3 + 1 + 1 = 5 時間 (単発)。
8. 撤退条件
- Shipping 後 1 週間で workflow run の p95 latency が fix 前より 1000ms 超悪化した場合、retry 回数を 3 → 1 に減らして retry-storm 疑いを排除する。
- alarm 発火数 (
telemetry_persist_final_fail) が月 5 件超となった場合、D1 rate limit hit の原因調査を行った上で consumer 分散 (Queues 化 等) への移行 ADR を起票する。起票義務: alarm 月 5 件超を観測した時点で ADR-0130 パターンで別 ADR 起票 · 期限: 観測から 2 週間以内 · 撤退トリガー: 起票せず放置した場合は本 ADR の retry 設定を 3 → 1 に戻し観測性優先方針を再合意する。 - D1 rate limit がバースト状に発生し数分間で alarm 5 件を超過した場合、staging 負荷試験で実測した backoff 上限値が回復時間をカバーしていないことが証明されたと見なし、backoff 上限を再設計 (delay 2s → 5s or limit 3 → 5) するか案 B (Queues 非同期) への移行 ADR を起票する。
- Cloudflare D1 障害が 18 時間超継続し Workflows instance expire で retry 機会が消滅する事象が 1 度でも発生した場合、案 B (Queues 非同期) への移行 ADR を 1 週間以内に起票する。放置した場合は本 ADR を supersede 予約とする。
- Analytics Engine 移行 ADR の起票期限 (shipping 後 3 か月) を過ぎても未起票の場合、本 ADR §11 の GitHub Actions scheduled workflow が自動 issue 化する。issue 化後 1 か月未対応で本 ADR の Confirmation 月次レビューを止める。
9. Confirmation
- 電帳法 KPI 分子分母乖離のゼロ化 — 検証手段:
SELECT COUNT(*) FROM telemetry_records WHERE created_at >= '2026-07-15'と workflows-e2e の trigger 総数を月次比較 / 実行頻度: 月次 / 違反時対応: 差分 ≥ 0.5% でtelemetry_persist_final_faillog とtelemetry_persist_failuresテーブルを突合し原因特定 · alarm 未発火なら silent drop 経路 (catch 節の想定外 path) を PR で修正。 - transient 吸収率 — 検証手段:
SELECT COUNT(*) FROM logs WHERE event='telemetry_persist_retry_recovered'を月次計上 · retry で救えた件数を可視化 / 実行頻度: 月次 / 違反時対応: retry 3 回内での recovery 率 < 95% で backoff 設定を再設計。 - 列 drift 検出 — 検証手段: deploy pipeline の migration smoke test (INSERT dry-run) が全 column に対して pass すること · builder unit test (
drp/test/audit/persistence.builder.test.ts) が全列 assert で pass すること / 実行頻度: 全 PR CI / 違反時対応: dry-run fail で deploy を blocking · builder unit test fail で PR merge blocking。 - alarm SLA — 検証手段:
telemetry_persist_alarm_runbook.mdに定義した「alarm 発火後 24 時間以内に対象 run_id を特定し補記 INSERT または再 trigger する」SLA を月次レビュー / 実行頻度: 月次 / 違反時対応: SLA 逸脱 1 件で runbook 手順の見直し PR を起票。 - Analytics Engine 移行期限監視 — 検証手段: GitHub Actions の scheduled workflow (
.github/workflows/adr-followup-issue.yml新設) が shipping 後 3 か月時点で未起票なら自動 issue 化 / 実行頻度: 月次 cron / 違反時対応: issue 発行 1 か月未対応で撤退条件 5 発火。
10. 参照
10.1 関連 ADR
- ADR-0120 (1 step.do = 1 Workers request の制約 ·
persist_telemetrystep 追加時の subrequest 上限との互換確認) - ADR-0197 (auto-consume inter-clone inbox · 電帳法 KPI 系 workflows-e2e の設計)
- ADR-0130 (受け皿未起案の前提を「起票義務 + 期限 + 撤退トリガー」で外へ送るパターン)
10.2 関連 PR/Issue
- 依存 memory:
telemetry-100-column-limit-reached - 過去 shipping: migrate-v22 (citation_details_json) · migrate-v23 (paraphrase_details_json) — silent drop 候補期間の観測対象
10.3 外部資料
- Cloudflare Workflows step retry: https://developers.cloudflare.com/workflows/build/rules-of-workflows/
- Cloudflare Workers Analytics Engine: https://developers.cloudflare.com/analytics/analytics-engine/
- Cloudflare Workers Logpush: https://developers.cloudflare.com/logs/logpush/
- 電子帳簿保存法 第7条・第8条 (記録の完全性義務)
11. 実装配線
- lint 配線:
drp/src/workflows/pipeline_run_workflow.tsにpersist_telemetrystep を追加 ·drp/src/audit/persistence.tsの 98 位置 bind を builder 化 ·drp/test/audit/persistence.builder.test.ts(新設) で全列 assert unit test ·drp/migrations/024_telemetry_persist_failures.sql(新設) で alarm 永続化テーブル追加 ·.github/workflows/deploy-worker.ymlに migration smoke test step 追加 ·.github/workflows/adr-followup-issue.yml(新設) で Analytics Engine 移行 ADR 起票期限の自動 issue 化 · Cloudflare Workers Logpush 設定をwrangler.tomlに追記しtelemetry_persist_final_failのプッシュ通知経路を確保。 - 起案フロー配線: 本 ADR は Pipeline 通常経路で起案 · shipping は
.github/workflows/deploy-worker.yml(= Workers 本番 deploy の GitHub Actions workflow) 経由の即本番 deploy · merge 前に memorydrp-workflow-instances-check-before-mergeに従い実行中審査 run 有無を確認する (deploy は run を kill するため)。 - docs SSoT 同期:
docs/_internal/06_ops/telemetry_persist_alarm_runbook.md(= alarm 発火時の対応手順を書く新設 runbook) を新設し alarm log 形式・対応 flow・エスカレーション先・「alarm 発火後 24 時間以内に対象 run_id を特定し補記 INSERT または再 trigger する」SLA・対象 run_id 特定 SQL (SELECT session_id FROM telemetry_persist_failures WHERE created_at >= ?)・安全な再 insert 手順 (INSERT OR IGNOREパターン) を記載する · Analytics Engine 移行 ADR の受け皿は ADR-0130 パターンで起票 · 起票義務: alarm が定常運用に入った時点 (月 1 件以上が 3 か月連続 or shipping 後 3 か月時点のいずれか早い方) で起票 · 期限: 契機観測から 2 週間以内 · 撤退トリガー: 起票せず放置した場合は本 ADR §8 撤退条件 5 が発火。