# ストリーム処理の耐障害性
## 定義
バッチ処理は失敗したタスクを単純に再起動でき、失敗タスクの部分出力を破棄できる(入力が不変で各タスクが別ファイルへ出力し、成功時のみ可視化されるため)。この結果、バッチジョブの出力は「何も失敗しなかった場合と同じ」になる——これを*厳密に一度(exactly-once)*と呼ぶが、実際には失敗時に再試行でレコードが複数回処理されうるため*effectively-once*(実効的に一度)の方が正確な呼び方である。ストリーム処理では同じ問題がより難しくなる: ストリームは無限であり、タスクが「完了」してから出力を可視化するという選択肢が取れないためである。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Fault Tolerance")
## マイクロバッチとチェックポイント
*マイクロバッチ(microbatching)*はストリームを小さなブロックへ分割し、各ブロックをミニチュアのバッチ処理として扱う手法で、Spark Streaming が採用する。バッチサイズは典型的に約1秒で、小さすぎるとスケジューリング・調整のオーバーヘッドが増し、大きすぎると結果が可視になるまでの遅延が伸びるというトレードオフの妥協点である。マイクロバッチは処理時間による暗黙のタンブリングウィンドウ(→ [[イベント時間とウィンドウ処理]])を提供するため、より大きなウィンドウが必要なジョブはマイクロバッチをまたいで状態を明示的に持ち越す必要がある。
Apache Flink が採る変種は、状態のローリングチェックポイントを定期的に生成し耐久ストレージへ書き込む方式である。ストリームオペレータがクラッシュすると直近のチェックポイントから再開し、最後のチェックポイントとクラッシュの間に生成された出力は破棄する。チェックポイントはメッセージストリーム中のバリア(barrier)によってトリガーされ、マイクロバッチの境界と似た役割を果たすが特定のウィンドウサイズを強制しない。
どちらの手法もストリーム処理フレームワークの内部に限れば、バッチ処理と同じ厳密に一度のセマンティクスを提供する。しかし出力がストリームプロセッサの外(データベース書き込み・外部メッセージブローカへの発行・メール送信等)へ出た瞬間、フレームワークは失敗したマイクロバッチの出力を取り消せなくなる——失敗タスクの再起動は外部への副作用を二重に発生させ、マイクロバッチやチェックポイントだけではこの問題を防げない。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Microbatching and checkpointing")
## アトミックコミットとべき等性
厳密に一度の見かけを保つには、イベント処理に伴う全ての出力・副作用(下流オペレータや外部メッセージングシステムへのメッセージ・データベース書き込み・オペレータ状態の変更・入力メッセージの確認応答=ログベースブローカのコンシューマオフセット前進を含む)が、処理成功の場合*かつその場合に限り*永続化されるようにする必要がある。これは分散トランザクションとアトミックコミット(2PC)の問題そのものである。Google Cloud Dataflow・VoltDB・Apache Kafka は、XA のような異種技術間のトランザクションではなく、状態変更とメッセージングをストリーム処理フレームワーク内部に限定して管理することで、このアトミックコミットを効率的に実装している。複数の入力メッセージを1トランザクションにまとめることでトランザクションプロトコルのオーバーヘッドを償却できる。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Atomic commit revisited")
もう一つの手段が*べき等性(idempotence)*——同じ操作を複数回行っても一度だけ行ったのと同じ効果になる性質——に頼る方法である(→ [[べき等性]])。自然にべき等でない操作(カウンタのインクリメント等)も、追加のメタデータを使えばべき等にできる。例えば Kafka の各メッセージは永続的で単調増加するオフセットを持つため、外部データベースへ値を書き込む際にそのオフセットを添えておけば、既に適用済みの更新かどうかを判別でき同じ更新の二重適用を避けられる(Storm の Trident の状態管理も同様の発想)。べき等性への依存は、失敗タスクの再起動が同じメッセージを同じ順序で再生する(ログベースメッセージブローカはこれを満たす)・処理が決定的である・他ノードが同じ値を並行更新しないという前提を要求する。処理ノードの切り替え時にはフェンシング(→ [[分散ロックとリース]])が必要になる場合もある。これらの制約はあるものの、べき等な操作はわずかなオーバーヘッドで厳密に一度のセマンティクスを達成する有効な手段である。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Idempotence")
## 障害後の状態再構築
ウィンドウ集計(カウンタ・平均・ヒストグラム)やジョイン用のテーブル・インデックスなど、状態を要するストリーム処理は、障害後にその状態を復旧できる必要がある。選択肢は3つある: (1) 状態をリモートデータストアに保持し複製する(個々のメッセージごとにリモートクエリするとレイテンシが問題になりうる)、(2) 状態をストリームプロセッサにローカルに保持しつつ定期的に複製する(Flink は耐久ストレージへのスナップショット、Kafka Streams はログコンパクション付きの専用 Kafka トピックへの変更送信という、CDC に似た方式を採る。VoltDB は各入力メッセージを複数ノードで冗長に処理して状態を複製する)、(3) 入力ストリームから状態を再構築する(短いウィンドウであれば該当期間のイベントを再生するだけで十分高速な場合があり、CDC で保守されたローカルレプリカならログコンパクション済みの変更ストリームから再構築できる)。どれが最適かはネットワーク遅延とディスクアクセスレイテンシの相対的な特性に依存し、普遍的に優れた解はない。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Rebuilding state after a failure")
## 横断的知見
- (この節は今後、複数ソースの突き合わせで得られた知見を蓄積する。現時点では単一ソースからの知見のみ。)
## 未解決の問い
- マイクロバッチ/チェックポイントによる内部的な厳密に一度と、外部副作用に対するアトミックコミット/べき等性は独立に選べるが、両者を組み合わせた際の運用複雑度・障害モードの相互作用はどこまで文書化されているか。
- べき等性によるデデュプ(Kafka のオフセットを外部書き込みに添える方式)は、複数のストリームプロセッサインスタンスが同じ外部システムへ並行書き込みする場合にどのような追加の調整が必要になるか。
- 状態再構築の3手法(リモート複製・ローカル複製・入力からの再構築)のコスト比較は、ネットワークとディスクの相対性能に依存すると本文は述べるが、具体的な閾値やベンチマークは示されていない。実務でどう判断すべきか。
## 関連
- ソース: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]]("Fault Tolerance")
- 概念: [[べき等性]] / [[分散トランザクション]] / [[フォールトトレランス]] / [[イベント時間とウィンドウ処理]]
## 出典
- [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]]("Fault Tolerance" 節)