###### tags: `TransactionalInformationSystems` # 17章-2 準ステートフルなアプリケーションの回復 ## 対話的なアプリケーション ここではユーザとの対話的なトランザクションを取り扱う。一回の入力-出力プロセスで終了せず、対話の中で行われる一連の操作を全て単一のトランザクションを実行したい。ACID特性は保証したいし、もっといえばただ一度の実行も保証したい。 出力は基本的にundoできないものなので、全て単一のトランザクションにまとめてしまうことは難しい。ので、実践的な解法としては、トランザクションを出力フェーズを境界として分割し、複数のトランザクションを順次実行することになる。これらが一連の操作であることは、キューマネージャが認識する。 - クライアント側では、リプライのdequeue→出力→さらなる入力の受付→リクエストのenqueueまでを1つのトランザクションで実行する。 - サーバ側では、リクエストのdequeue→実行→リプライのenqueueまでを1つのトランザクションで実行する。 これを連鎖して対話的なプログラムを実現する。これの著しい特性は、最初のリクエストが一度enqueueされたら、それ以降の処理は全てただ一度実行されるのが保証されること。 ![](https://i.imgur.com/bHyaPXY.png) --- ### 定理17.2 キューを利用した対話的なトランザクションチェインにおいて、以下が保証される。 1. いちど対話の開始となるユーザ入力トランザクションがcommitされたら、トランザクションの連鎖全体がサーバでただ一度実行される。 2. いちど対話の開始となるユーザ入力トランザクションがcommitされたら、各ユーザ出力は全て少なくとも一度ずつ出力される。 3. ユーザ出力が検証可能なら、2の条件は「ただ一度ずつ出力される」に改善する。 #### 証明 定理17.1の拡張になる。 新しく考える必要があるのは、クライアント側で、ユーザに出力した後もしくは新しいユーザ入力を受け取った直後、リクエストのenqueue前にクラッシュしたパターン。このようなケースでは、出力は検証なしでは複数回ユーザに提示されることになり、ユーザに次の入力を再び与えてもらう必要がある。入力を再び与えてもらいさえすればあとはただ一度だけ実行される。 このような振る舞いはあまり嬉しくはないが、少なくとも与えてもらう必要のある入力がクラッシュ直前のやり取りに関するものだけであることは救いである。$\square$ --- ### 注意点 この手法では、対話を複数のトランザクションに分割してしまうため、トランザクションの分離性はもはや保証されない。なので、アプリケーション側がそれを許容する設計になる必要がある。例えば座席予約で、最初の空席確認の時点で仮予約を取るとか、そういうの。 また、アプリケーションの回復には、最後のメッセージだけでは不十分であり、**クラッシュ時点のローカル変数の情報**も必要になる。この情報のサイズがそれほど大きくない場合には、enqueue/dequeueされるリクエスト/リプライにこれらの情報を全て付属してしまう方法がある。これなら、途中でクライアントがクラッシュして回復した場合にもサーバから受け取ったリプライに必要な情報が付属している。 > 著者曰く、近年のeコマースなどで広く用いられるクッキー/URLを用いたセッション管理は「**対話的なトランザクションを再発明したものであるが、悲しいことにトランザクション的でない=ACID特性を満たせないクッキー/URLに頼ってしまっているのが皮肉だ**」とのこと。次世代のeコマースシステムはトランザクション的な高信頼手法を採用すると信じている、と書いているが、現在でもステートレスなHTTPとクッキー/URLを用いたアーキテクチャが一般的なことを見ると著者の予測は外れているだろうか。 > 確かにステートフルなプロトコルが開発されたりもしたが、HTTPの単純にして強力、かつWebの特性を最も上手く活かしたコンセプトには太刀打ちできず普及していない。メッセージキューによる高信頼なセッション管理も、オーバヘッドがデカいしなあ。そういうものを使えるフレームワークがあるか調べるのも面白そう。 ## ワークフロー管理 複数の対話的な操作や自動化された手順などの集合からなるワークフローについても、ここまで考えてきた手法をベースに実現されうる。 ワークフローエンジンがワークフローサーバで走り、ワークフローの状態・文脈を管理する責任を負う。 - 状態:ワークフロー全体の制御チャートのうち、どのアプリケーション(アクティビティ)を実行しているか。 - 文脈:アクティビティの入出力や、制御フローに関係するデータ等の情報。 ワークフローの内部で走る各アクティビティは別個のアプリケーションサーバで実行される。 ### ワークフロー状態/文脈の耐障害性 状態や文脈の回復を行うには、永続・高信頼性なストレージにこれらを保存するのがナイーブな方法である。ただし、アクティビティの終了に伴う変数の変化と状態の移行がアトミックに実行される必要がある。 したがって、以下の情報をトランザクションで管理する。 - 現在のアクティビティの実行結果に基づくワークフロー変数への変更。 - 状態の移行。 - 状態の移行にともなうワークフロー変数への変更。 これによって状態と文脈のトラッキングが実現すれば、あとはいちどワークフローの開始がサーバに記録されれば全過程のただ一度の実行が保証される。 ### 分散ワークフロー ワークフローが、複数のワークフローサーバに分けて分散実行される場合、状態間移動を単一のトランザクションで行った場合、複数のサーバをまたがることになり、あるサーバがダウンしている場合にパフォーマンスに問題をもたらす。 代わりに、これを状態移行に関わるサーバの数だけのトランザクションに分ける。アクティビティの終了を通知するトランザクションは、状態の移行に関する情報を次にアクティビティを開始するサーバの数だけenqueueする。それぞれのサーバは個別にトランザクションの中でdequeueして情報を受け取り、それぞれのアクティビティを開始する。