---
# System prepended metadata

title: 17章-3 ステートフルなアプリケーションの回復
tags: [TransactionalInformationSystems]

---

###### 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で上書きされないようにする。










