📘

第9章 RML-3 事件簿 — インシデント対応フローの世界観を揃える

に公開

RML-3 事件簿 — インシデント対応フローの世界観を揃える

『The Worlds of Distributed Systems』第9章


「インシデントが起きたとき、
画面の向こうで何が起きていることになっているのか?」

第8章では、RML-3(History World)を

  • 法務
  • ビジネス
  • SRE / エンジニア

の三角形で運営する「自治領」として見直しました

この第9章では、もう一段だけ現場寄りに寄せて、

RML-3 インシデントが発生したとき、
実際のフローをどう設計するか?

を扱います

ここでのゴールは、「理想のインシデント運用」ではなく、

  • RML-1/2/3 の世界観
  • インシデント対応フロー

を接続する、**現実的な「事件簿フォーマット」**を作ることです


1. インシデント vs アラート vs バグ — 世界ごとに区別する

まず、呼び方の整理から

現場ではよく、

  • 「アラートが鳴った」
  • 「インシデントが起きた」
  • 「バグを踏んだ」

などの言葉が混ざりますが、RML の世界観で分けるとこうなります

1.1 アラート(Alert)

  • 監視の閾値を超えた
  • エラー率が上がっている
  • レイテンシが悪化している

など、「RML-1/2 の世界で何か怪しい」ときに鳴るもの

  • 多くは一過性
  • ユーザー影響はあるが、回復すれば跡が残らない

→ RML-1/2 フローで完結することが多い

1.2 バグ(Defect)

  • 仕様と違う動作
  • 特定の条件でエラーになる
  • 挙動はおかしいが、History World までは届いていない

→ RML-1/2 の世界の問題
 JIRA のチケットで管理する、あの世界線

1.3 インシデント(Incident)

この本の文脈では、インシデントをこう定義します

History World(RML-3)に到達した出来事のうち、
意味のある責任を伴うもの

具体的には

  • 金銭が動いた(or 動くべきだったのに動いていない)
  • 顧客/第三者が現実世界で影響を受けた
  • 規制・契約・社会的責任に絡む可能性がある

ような事案です

RML でいうと

  • world = "RML3" / action = "escalate-history"
  • Effect Ledger に記録すべき外部効果
  • インシデントレポートの対象

これらは「事件簿」に載る世界の話になります


2. RML-3 インシデントのフロー — 6ステップの事件簿

RML-3 インシデントのフローを、現場でそのままテンプレにできるくらいラフにすると、こんな 6 ステップです

  1. Detect(発見)
  2. Contain(封じ込め)
  3. Understand(理解)
  4. Decide(方針決定)
  5. Act(対処・コミュニケーション)
  6. Learn(学習・再発防止)

それぞれに「どの世界の話か」を貼っておきます

2.1 Detect — 発見(RML-2 → RML-3 の境界)

トリガはだいたいこのあたり

  • RML-2 のコードが world = "RML3" で例外を投げる
  • Gateway / SLO アラートが「RML3 エラー率」を検知する
  • CS(サポート)や営業経由で「ユーザーからの苦情」が上がる

ここで大事なのは、

「RML-3 かもしれない」と気づいた瞬間に、
RML-3 用のフローに載せること

です

チェックポイント例:

  • インシデント管理ツールに「RML3」ラベル付きで起票されたか?
  • Effect Ledger に「暫定レコード」を立てたか?
  • RML-2 レベルのリトライ / 補償だけで握っていないか?

2.2 Contain — 封じ込め(主に RML-2 の世界)

封じ込めは、

「これ以上、History World に変な記録を積み増さないための処置」

です

  • 該当機能の停止(feature flag off)
  • 特定のテナントやリージョンのみブロック
  • RML-2 レベルでの暫定補償(新規実行を即時キャンセルする など)

このフェーズは SRE / エンジニアが中心ですが、ビジネスへの影響が大きい場合は PO もすぐ巻き込みましょう

2.3 Understand — 理解(RML-1/2 の世界の解析)

ここでは、

  • 何が起きたのか(事実)
  • どの世界で起きたのか(RML-1/2/3)
  • どこまで履歴に残っているのか(History World)

を整理します

基本的には

  • アプリのログ(RML-1/2)
  • Effect Ledger(RML-3)
  • メトリクスやトレース

を突き合わせて「単一のタイムライン」を作る作業です

2.4 Decide — 方針決定(三角形の会議)

第8章の三角形が本格的に登場するフェーズです

  • 法務:規制・契約・プライバシー的に、最低限やるべきこと
  • ビジネス:顧客・ブランドとして、どこまでやるか
  • SRE / エンジニア:技術的に可能な手段とリスク

をテーブルに載せて、

「どの世界の履歴を、どう上書きしていくか?」

を決めます

典型的には

  • 返金/再実行/訂正通知/機能停止/ロールフォワード
  • 顧客への個別連絡 or プレスリリース or 利用規約上の告知

などを組み合わせることになります

2.5 Act — 対処・コミュニケーション(History World の更新)

ここでようやく、

  • 実際の返金処理
  • データ訂正
  • 顧客へのメール・電話
  • 社内向けと社外向けの説明

が走ります

Point:
ここでの全てのアクションは、**History World に対する「新しいイベント」**です

  • インシデントの原因となった過去の記録は消せない
  • 代わりに、返金や訂正などの「修正イベント」を足す

Effect Ledger 的には、「誤ったイベント + 修正イベント」のセットとして履歴を持ちます

2.5.1 社外向けコミュニケーションのテンプレート

RML の世界観を踏まえると、社外向けの説明文にも一貫性を持たせられます

ざっくりとした構成は、例えば次のようになります

  1. What happened?(何が起きたか)

    • RML-3 の観点で、影響範囲・対象を正直に書く
  2. When / Who was affected?(いつ・誰に影響したか)

    • Effect Ledger から引ける範囲で具体的に
  3. What is the current status?(今はどうなっているか)

    • 封じ込めが完了しているかどうか(RML-2 の現状)
  4. What have we done and will do?(何をしたか/これから何をするか)

    • 返金・訂正・再発防止策の概要
  5. How to contact us?(問い合わせ窓口)

例(かなり簡略化した文案):

このたび、一部のお客様に対して決済が重複して行われる事象が発生しました
2025年6月1日 10:00〜11:00 の間に当サービスで決済を行った 37 名のお客様が対象です
現在は当該機能を停止しており、新たな重複決済は発生していないことを確認しています
対象となる全てのお客様に対して、重複分の全額返金と、お詫びとしてのクーポンを順次お送りしております
本件に関するお問い合わせは、◯◯窓口(メールアドレス/電話番号)までご連絡ください

ここで重要なのは

  • 影響範囲(RML-3)はごまかさない
  • 技術的な詳細説明は RML-2 レベルまでに留める(内部向けポストモーテムで深掘り)
  • 「何をやった/やるか」を、History World の修正イベントとして説明する

というバランスです

2.6 Learn — 学習・再発防止(RML-1/2 の設計/組織ガバナンス)

最後に、ポストモーテム(事後検証)です

扱う対象は、

  • 技術設計(RML-1/2 のコードやアーキテクチャ)
  • 運用・プロセス(検知・封じ込め・判断の流れ)
  • ガバナンス(権限・ToS・インシデントの定義)

など

ここで、History World で発生した出来事を、
RML-1/2 の世界の改善に変換する
ことがゴールになります


3. Runbook と Playbook — RML視点のドキュメント整理

インシデント対応の話になると、

  • Runbook(ランブック)
  • Playbook(プレイブック)

という言葉がよく出てきます

一般的な使い分けは

  • Runbook: 具体的な操作手順(How)
    例)「このアラートが出たら、このコマンドを実行する」
  • Playbook: シナリオ/判断フロー(What / Who)
    例)「この種のインシデントが起きたら、誰に連絡して、どの順番で判断するか」

RML の世界観にマッピングすると

  • Runbook = 主に RML-2 の世界
    (技術的な封じ込め・切り戻し・一時対応)
  • Playbook = 主に RML-3 の世界
    (法務・ビジネスを巻き込んだ組織的な判断)

と捉えると整理しやすくなります

3.1 RML-2 Runbook の例

  • 「決済ゲートウェイのエラー率が閾値を超えたときの手順」

    • 対象サービスの Feature Flag を Off にするコマンド
    • 依存サービスの状態確認
    • ログの取り方・メトリクスの確認方法

これは、基本的に エンジニアだけで完結する世界です

3.2 RML-3 Playbook の例

  • 「金銭的な誤請求が判明したときのシナリオ」

    • 閾値(件数・金額)に応じて SEV を決める
    • 誰に連絡するか(法務・経営・CS・PR)
    • 返金 or 相殺 or クーポン のどれを選べるか
    • プレスリリース or 個別メール の条件

こちらは、技術だけでは決められない領域です

3.3 実務的な落としどころ

RML-3 事件簿を運用していくなら、

  • Runbook(RML-2):

    • 各種アラートやエラーコードごとに「まず何を叩くか」
    • オンコール担当や SRE が即座に動けるようなレベルの手順
  • Playbook(RML-3):

    • History World に到達したインシデント種別ごとに「誰を巻き込んで、どう決めるか」
    • 第8章の RACI と紐づいたシナリオ

という 二段構えのドキュメントとして整備しておくと、

  • RML-2 レベルの小さな障害は Runbook だけで完結
  • RML-3 に上がった瞬間に Playbook にバトンタッチ

という運用がしやすくなります


4. RML-3 事件簿テンプレート — 1枚で全体を見える化する

フローを頭に入れたうえで、実際の「事件簿テンプレート」に落とし込んでみます

YAMLっぽく書くと、だいたいこんな形です

incident_id: INC-2025-0001
title: "二重請求が発生した可能性"
rml_world: RML3
status: resolved # or ongoing, monitoring

detected_at: 2025-06-01T10:23:45Z
detected_by:
  - type: alert
    source: "payment_service.rml3_error_rate"
  - type: cs_ticket
    id: "CS-1234"

classification:
  severity: SEV-1 # 会社ごとの定義に合わせる
  asset:
    - money
  scope:
    affected_users_estimate: 37
    affected_transactions_estimate: 41

timeline:
  - at: 2025-06-01T10:23:45Z
    world: RML2
    type: detection
    detail: "RML3エラー率が閾値を超えた"
  - at: 2025-06-01T10:25:00Z
    world: RML2
    type: containment
    detail: "決済APIの新規受付をFeatureFlagで停止"
  - at: 2025-06-01T11:10:00Z
    world: RML3
    type: impact_assessed
    detail: "37ユーザー / 41トランザクションに二重請求が発生"

decisions:
  summary: >
    全対象ユーザーへの全額返金 + お詫びクーポンを実施
    プレスリリース等の外部公表は不要と判断
  decided_by:
    - role: legal
      name: "..."
    - role: business
      name: "..."
    - role: sre
      name: "..."

actions:
  technical:
    - type: refund
      world: RML3
      count: 41
    - type: feature_fix
      world: RML2
      detail: "Idempotency Key欠如を修正し、サーガ設計を見直し"
  communication:
    - type: user_email
      world: RML3
      template_id: "refund_apology_v2"
    - type: internal_post
      world: RML2
      channel: "#incident"

lessons_learned:
  technical:
    - "決済サーガにIdempotency Keyを必須化"
  process:
    - "RML3アラートを受けたときのCS連携手順をRunbook/Playbookに反映"
  governance:
    - "ToSの障害時補償条項と実態運用のギャップを見直し"

ポイントは:

  • timeline[*].world に RML を明示する
  • actions.* でも「どの世界を更新しているのか」を書いておく
  • decisions誰がどんな判断をしたか を残す

このフォーマットで事件簿を積んでいくと、

  • 「どの世界で詰まりがちか」(検知?封じ込め?決定?)
  • 「技術 vs 組織 vs 契約のどこにボトルネックがあるか」

が、後からレビューしやすくなります


5. 具体例:決済サーガの RML-3 事件を追う

少しだけストーリー仕立てで、RML-3 事件を追ってみます

5.1 起点:RML-2 の retry-without-idempotency

決済サーガで、こんな実装があったとします

// アンチパターン例:idempotencyKey を渡していない
async function charge(paymentId: string, amount: number) {
  try {
    await paymentGateway.charge({ paymentId, amount });
  } catch (e) {
    throw new StructuredError({
      world: "RML2",
      severity: "error",
      action: "retry-with-backoff",
      code: "PAYMENT_GATEWAY_TEMPORARY_ERROR",
      message: "決済ゲートウェイで一時的なエラーが発生しました",
      cause: e,
    });
  }
}

クライアント側は素直に retry-with-backoff に従い、
数回リトライします

でも Idempotency Key がない ので、
決済ゲートウェイ側では同じ請求が複数回走り、
実際に二重請求が発生します

ここまでが RML-2 の世界の失敗

5.2 RML-3 の検知

数時間後、CS からこんな声が上がります

「同じ決済が二重に引き落とされている、という問い合わせが何件か来ています」

同時に、Effect Ledger を見ていた SRE が、

  • 同じ userId / orderId で複数の決済記録がある

ことに気づきます

ここでインシデントが起票され、

  • rml_world = RML3
  • classification.severity = SEV-1
  • asset = money

として扱われることになります

5.3 封じ込め・理解・決定

このあと発生することは、だいたい想像がつくはずです

  • 封じ込め:

    • 新規決済を一時停止
    • 決済サーガの該当経路を止める
  • 理解:

    • Effect Ledger + 決済ログから、影響取引を抽出
    • コードを読んで、原因が「idempotencyKey 設計漏れ」と判明
  • 決定:

    • 全対象ユーザーに全額返金
    • お詫びクーポンを付けるかどうかはビジネス判断
    • プレスリリースの要否は法務+経営判断

この一連の流れを、9章の事件簿テンプレ に沿って記録していきます

5.4 学習:RML-2 の世界での修正

最後のフェーズでようやく、

  • 決済サーガに Idempotency Key を必須化
  • retry-with-backoff を返すコードはすべて洗い出し
  • SDK 側で「idempotencyKey がないとコンパイルエラー/Lint エラー」くらいにする

といった RML-1/2 レベルの修正 が入ります

RML 的に見ると

  • RML-2 の設計ミスが
  • RML-3 のインシデントとして噴出し
  • 事件簿を通じて
  • また RML-2 の設計改善に戻ってくる

という循環になっています


6. アンチパターン — 事件簿が機能しないパターン

6.1 「事後レポートのためだけに書いている」

  • フォーマットはある
  • でも、書かれるのは「後から見栄えのするストーリー」だけ
  • 実際の意思決定やタイムラインとズレている

問題

  • 次回以降の参考にならない
  • 法務・経営にとっても「本当に信用していいのか?」となる

対策

  • タイムラインだけは「リアルタイムで」埋めていく運用にする
  • 決定事項は、その場で Decision Log に書き込む

6.2 RML-3 事案を「一段軽く」扱おうとする

  • 本当は RML-3 なのに、「RML-2 バグ扱い」にしてしまう
  • PL や顧客信頼へのインパクトを、組織として認めたくない

結果

  • History World を正直に見ない文化ができる
  • 後から「なぜ報告されなかったのか?」問題になる

対策

  • RML-3 の定義を先に決めておく(第8章のルール)
  • 迷ったら RML-3 扱い & 軽いインシデントとして握る

6.3 「技術的な原因」だけに寄せてしまう

  • ポストモーテムで、技術的な root cause だけ詳しく書き、
  • 組織/ガバナンス/ToS の観点が抜け落ちる

結果

  • 同じクラスの問題が、形を変えて何度も発生する
  • 法務やビジネス側は「またあれか」としか認識できない

対策

  • 事件簿テンプレに、必ず

    • lessons_learned.technical
    • lessons_learned.process
    • lessons_learned.governance
      の 3 つを持たせる

7. チェックリスト — あなたの組織の「RML-3 事件簿」はどこまでできているか

最後に、この章で扱った内容をベースにしたチェックリストです

7.1 定義まわり

  • 「インシデント」「アラート」「バグ」の違いを、RML の観点で説明できるか?
  • RML-3 インシデントの定義(どんな事案を RML3 とするか)が、文書化されているか?
  • 迷ったときの原則(例:グレーは RML-3 に寄せる)が決まっているか?

7.2 フローまわり

  • Detect / Contain / Understand / Decide / Act / Learn の 6 フェーズに相当するフローがあるか?
  • RML-3 エラー(world = "RML3")をトリガに、インシデントが自動起票されるか?
  • 封じ込めのための Feature Flag や Kill Switch が用意されているか?

7.3 事件簿テンプレまわり

  • インシデントレポート(事件簿)のテンプレートはあるか?
  • タイムラインに world(RML1/2/3)を明示できているか?
  • decisions セクションに「誰が何を決めたか」を書く欄があるか?
  • lessons_learned が「技術」「プロセス」「ガバナンス」に分かれているか?

7.4 ドキュメント(Runbook / Playbook)まわり

  • RML-2 レベルの障害に対応する Runbook は整備されているか?
  • RML-3 レベルのインシデントに対応する Playbook(誰に連絡して、どう判断するか)はあるか?
  • 事件簿で得た学びを、Runbook / Playbook に反映するサイクルが回っているか?

7.5 組織・文化まわり

  • ポストモーテムに、法務 / ビジネス / SRE の三者が参加しているか?
  • RML-3 インシデントのコスト(返金・クーポン・工数など)をざっくり把握しているか?
  • 「RML-3 インシデントがあること」自体を、恥ではなく学習機会として共有できているか?

8. おわりに — 事件簿は「世界観のログ」である

この章でやってきたことを、一文でまとめるとこうなります

RML-3 事件簿とは、
「どの世界で何が起き、その上で誰がどう振る舞ったか」のログである

  • RML-1/2 の世界でシステムがどう振る舞い
  • RML-3 の世界で歴史がどう刻まれ
  • 組織としてどんな判断をし
  • それを踏まえて、また RML-1/2 の世界を書き換えていくのか

その全体が、一つの「世界観のログ」として残っている状態を目指します

次の第10章では、この事件簿と History World を、

「プロダクト戦略」と「長期的な信頼」の文脈でどう位置づけるか

という観点から、もう少し引きで眺めてみます

Discussion