###### tags: `TransactionalInformationSystems` # 17章-3 ステートフルなアプリケーションの回復 ## より一般のステートフルなアプリケーション ※ この節はふんわり書かれているのでこのノートもふんわりしている   余裕があれば見直したい ここまで考えてきたアプリケーションは、メッセージキューに状態に関する情報を全て格納するという点で、実質的にはステートレス、もしくは準ステートフルとでもいうべきものだった。 ここでは、さらにステートフルな一般のアプリケーションに対するアプローチを考えていく。ただし、クライアントアプリケーションが直接データサーバにアクセスする2層アーキテクチャに限定する。 目標は、システムの障害による失敗をユーザに見えないように回復を行い、ほぼ全体のプロセスをただ一度だけ実行すること。カバーできない例外は、出力の重複表示と、失われた直前の入力の再要求のみにしたい。 メッセージの管理は、必ずしもキューでは行わない。代わりにメッセージログをとることで、回復時にもとのメッセージを復元してアプリケーションをリプレイする。 ### サーバリプライロギング メッセージログはサーバとクライアントのどちらで取ってもよいし、両方で取ってもよい。ただ、以下の理由からサーバでとるのが丸い。 - サーバは多くのクライアントと並行にやり取りするので、ロギングのためのディスクI/Oを効率よく使える。 - 複数のリクエストを柔軟な順番で実行できる(これは通常のメッセージングフレームワークでは不可能)。 - サーバはクライアントよりも信頼性が高い。 なお、ユーザの入出力に関するものはクライアント側がログを取る。 ### データ構造 通常の回復機構に加え、サーバで以下のデータ構造を保持する。 - アクティブアプリケーションテーブル(AT):実行途中(失敗/再起動中のものも含む)のアプリケーションの状態に関する情報を格納する - メッセージルックアップテーブル(MT):アクティブなアプリケーションのメッセージ、特にリプライメッセージをランダムアクセス可能な形で保存する これらはメモリ上に置いて高速なアクセスを可能にし、ストレージには永続ログを書き出す。 各メッセージは以下のタグで修飾する。 - アプリケーション識別子(AppID):ホストクライアントを識別する値で、クライアントの中で一意に定まる - message sequence number(MSN):各クライアントアプリケーションの中で単調増加かつ一意に定まる ```verilog AT: array[AppID] of record LastMSN: integer; // アプリケーションの最後のメッセージのMSN StableMSN: integer; // これ以前のメッセージが全部クライアントの永続ログに記録されているMSN RedoMSN: integer; // 不要になっていない最古のメッセージのMSN RedoLSN: integer; // RedoMSNに対応するログエントリのLSN MT: array[AppID,MSN] of record MsgType: (request, reply, input, output); MsgContents: array of char; LF: persistent array[LSN] of record LogRecType: (write, read, undo, request, reply, input, IP, start-IP, term-IP, CP); LogRecContents: array of char; AppID: integer; MSN: integer; ``` アプリケーションの状態は定期的にクライアント側でも記録される。この点をインストレーションポイント(IP)と呼ぶ。IPもMSNを持つメッセージの形で記録し、開始と終了をそれぞれstart-IP、term-IPとする。 ### 通常動作中のサーバロギング サーバはデータベースへの書き込み、リクエスト/リプライのそれぞれについてログエントリを生成する。そのうちメッセージに関するものはMTにもランダムアクセスできる形で格納される。リプライログエントリは、クライアントに送り返されるまでにforceされる。このforceが完了したら、対応するリクエストに関するMTのエントリは削除してもよい。 リクエストの処理中にサーバが落ちた場合に特別な考慮が必要で、2つの選択肢がある。 - ロールバックリクエストと再実行: 終了していなかったリクエストを全てundoして実行しなおす。複数のリクエスト間の分離性が維持されたままundoされる必要がある。 - リクエスト処理の再開: データベースからの読み込みも全てログを取る。再開時にはログをもとに状態を復元し、そのままリプライを完成させる。再開の間はデータベースの読み書きを遮断する。書き込みは恒等性のためにLSN検証を行う。読み込みに関して2つの方法がある。 1. 読み込んだデータを物理ログにとる。ログだけからデータを復元できるが、ログサイズが大きくなる。 2. 読み込み操作を論理ログに記録する。ログに基づいてデータベースから読み込んで復元する。flush順依存性を管理し、必要とされているバージョンがflushで上書きされないようにする。