「クラッシュ・オンリー」設計を読んだ

The Twelve-Factor App (日本語訳)のIX. 廃棄容易性に出てきたcrash-only software(クラッシュ・オンリー・ソフトウェア)を聞いたことがありませんでした。

The Twelve-Factor Appに記載されていたリンク先(Crash-only software: More than meets the eye [LWN.net])をざっくり読んだので、以下にメモを残しておきます。

メモ

イントロダクション

George CandeaとArmando Foxは、ソフトウェアはシャットダウン・再起動よりもクラッシュ・回復のほうが高速であることを見つけ、2003年にHot Topics in Operating Systems IXに「Crash-only Software」として報告した。

実験では重要なデータは失われなかったことがわかっている。現代においてクラッシュしたときにデータを失うようなソフトウェアは人気がなく、利用されないため、このような結果になったと考えられる。

一般的なシステムにはクリーン(グレースフル)にシャットダウンする経路とクラッシュによってシャットダウンする経路の2種類があり、両者をサポートするためにわざわざ余分にソースコードを書いている。ソースコードを書くということはバグのリスクを抱えるということである。 一般的なシステムにおいて、明示的な(グレースフルな)シャットダウン・再起動は、定常状態のハイパフォーマンス、再起動時のパフォーマンス、許容できるデータ喪失のトレードオフを考慮して実装されている。

クラッシュ・オンリー・ソフトウェアとは、安全にクラッシュし、素早く回復するソフトウェアのことである。シャットダウンする唯一の方法はクラッシュさせることであり、起動する唯一の方法は回復させることである。クラッシュ・オンリー・システムは、再試行可能なリクエストで通信をおこなうクラッシュ・オンリー・コンポーネントから構成される。障害を処理するには、クラッシュして障害のあるコンポーネントを再起動し、タイムアウトしたリクエストを再試行する。クラッシュからの回復は後付けではなく、開発プロセスの最優先であり、グレースフルシャットダウンのための余分なコードは不要となる。

クラッシュ・オンリー・ソフトウェアというミームがあまりに流行したため、誤解が生まれている。以下ではその誤解を紹介している。

誤解1 クラッシュ・オンリー・ソフトウェアの名前に引きづられた誤解

クラッシュ・オンリー・ソフトウェアは、シャットダウン・コードを書かず、エラー処理もせず、状態も保存しない、というものではない。

実際には、より厳格な規律とより慎重な設計が必要である。クラッシュ・オンリー設計は、より堅牢で信頼性の高いソフトウェアを作るのに役立つが、そもそも堅牢で信頼性の高いソフトウェアを書くことを免除するものではない。

誤解2 クラッシュ・オンリー設計の実装に関する誤解

クラッシュ・再起動を使いすぎたり、システム全体をクラッシュして再起動させたりしてはならない。

クラッシュ・オンリーソフトウェアの考え方の1つは、コンポーネントの動作がおかしかったりバグがあったりした場合、クラッシュさせて再起動させれば、再び機能し始める可能性が高いというものである。何か問題が発生するたびにプログラムをクラッシュさせるコードを意図的に書くというのは間違いである。正しい解決策は、考えられるエラーはすべて正しく処理し、不測のエラー状態に対してはクラッシュ・再起動に頼る。

物事がうまくいかなくなったら、クラッシュしてシステム全体を再起動というのも間違いである。クラッシュ・オンリーのシステム設計の信条の1つは、クラッシュ・再起動は安上がりだという考えである。なぜなら、クラッシュ・再起動するのはシステムの小さな自己完結した部分だけだからである。

クラッシュメカニズムは、クラッシュ・オンリー・システムの完全に外部で独立したものとして実装しなければならない。そうでなければ、クラッシュすることができなくなる。

パフォーマンス、リカバリ時間、信頼性のトレードオフを無視してクラッシュ・オンリー設計を実装することは間違い。主要なトレードオフは、アプリケーションの状態を保存するチェックポイントを設けることで、リカバリ時間と信頼性は向上するが、定常状態のパフォーマンスは低下する。つまり、クラッシュしたときにチェックポイントから回復できるが、チェックポイントでアプリケーションの状態を保存する分だけクラッシュしていない定常状態のパフォーマンスは低下する。ちょうどよいバランスを見つけ出さなければならない。

誤解3 クラッシュ・オンリー設計に適したシステムについての誤解

どのようなシステムがクラッシュ・オンリー設計に適しているか、について誤解がある。

クラッシュ・オンリー・ソフトウェアはステートレスでなければならない、ということはない。クラッシュ・オンリー・ソフトウェアは、システムが必要とする不揮発性の状態をデータベースやセッションステートストアなどに保存しなければならないだけである。ただし、クラッシュ・セーフで迅速な回復が可能なデータストアを作成するのは非常に困難であるため、専門家に任せるべきである。

ハイパフォーマンスが要求されるシステムにもクラッシュ・オンリー設計は適用できる。システムは頻繁にバグやクラッシュが発生するため、システムの定常状態の性能だけでなく、クラッシュからのリカバリを考慮するとクラッシュ・オンリー設計の方が有利である。

ロックを使ったマルチスレッド・システムにも適用できる。ロックはクラッシュ・オンリー・コンポーネント内で使用され、コンポーネント間では予期しないクラッシュを考慮する必要がある。 コンポーネント間のインターフェイスは障害の境界として考慮が必要になる。例えば、すべてのリクエストにタイムアウトを設定し、失われる可能性のあるコミットされていない状態に依存しないようにリクエストを作成する必要がある。

ミッション・クリティカル なアプリケーションにも適用できる。クラッシュ・オンリー・ソフトウェアは、予期せぬクラッシュというコンピューティングの避けられない事実を最初から考慮しているため、実際には信頼性が高い。

障害をクラッシュによって対処するソフトウェアは、バグを隠したり助長したりするという批判がある。クラッシュ・オンリー・ソフトウェアは、リカバリーコードを明示的にテストすることで以前は隠されていたバグを見つけだすことができる。バグの回避策としてコンポーネントを明示的にクラッシュさせて再起動させることは、クラッシュ時のダンプを取ったり、バグを解決するために使用できるデータを記録したりすることを妨げるわけではない。

結論

クラッシュ・オンリー・ソフトウェアを書けば、コードを書いたり設計したりする作業を省けわけではない。クラッシュ・オンリー設計の原則を厳密に適用し、正しく実装することができれば、より高品質で信頼性の高いコードが生み出され、より信頼性が高く、デバッグしやすいシステムが手に入る。

おまけ

内容はそこまで重要ではないですが、かっこいい言い回しだと思ったので、紹介しておきます。

Probably the most common misconception is the idea that writing crash-only software is that it allows you to take shortcuts when writing and designing your code. Wake up, Sleeping Beauty, there ain't no such thing as a free lunch.

おそらく最も一般的な誤解は、クラッシュ・オンリー・ソフトウェアを書けば、コードを書いたり設計したりするときにショートカットできるという考えである。目を覚ませ、眠れる森の美女よ、タダで食べられるものなどないのだ。

おわりに

何か発生したらクラッシュさせてしまえ!とかエラーハンドリングのコードを減らしてクラッシュ・オンリー設計なんです!というのは正しくないというのは全くそのとおりだと思いました。 結局できるエラーハンドリングはすべておこない、それでも発生した予期せぬエラーにはクラッシュで対応するということであり、さらにそのクラッシュが正しくおこなわれることやクラッシュしたときの処理が再開できるような設計にすることを考えると、実装量や設計はむしろ大きくなりそうです。

また、予期せぬエラーが発生した場合にクラッシュしてコンポーネントを再起動、入れ替えをおこなうことができる設計、というのはまさにThe Twelve-Factor AppのIX. 廃棄容易性につながるものだとよくわかりました。

たとえば、AWS SQSを使って処理をキューイングしておき、Lambda関数がそのキューからメッセージを取り出して処理をおこなうような構成がこれに当たると思います。予期せぬ障害が発生した場合、その障害がおきたLambda関数のインスタンスだけをクラッシュして別のインスタンスを起動し、メッセージはタイムアウトによってキューに戻されます。そして、戻されたメッセージは再び別のLambda関数インスタンスに取得されて処理がおこなわれます。

参考