SOAと「つなぐ」技術 第6回:SOAをとりまく「つなぐ技術」 - トランザクション

Avatar
dstn

マーケティング企画部の渡辺です。

今回は、SOAやEAIなどの「つなぐ」技術において重要な要素であることがある、「トランザクション」についての説明をします。 

トランザクションとは

トランザクションとは、関連する「複数の処理」を一つにまとめたもののことです。 

複数の処理が一体となって処理され、必ず「すべて処理完了」ないしは「すべて処理されない」のいずれかになってほしい場合に利用します。 

なぜそんなことが必要なのかを、簡単な例を使って説明しましょう。銀行の「送金処理」を例に考えてみましょう。口座Aから口座Bへ一万円の送金をする場合には以下の処理が行われます。 

  • 処理-1:口座Bに一万円を増やす 
  • 処理-2:口座Aから一万円を減らす 

単にこのままでも良いようにも思えます。しかし、「処理の失敗」や「並行処理」を考えると、このままでは様々な問題が起こります。 

まず、「処理の失敗」について考えてみましょう。実際のシステム運用では様々な理由で処理が失敗してしまうことがあります(例えば、ハードウェアの故障で処理が出来ないなど)。その結果、以下のようになってしまう可能性があります。 

振り込まれたのにお金が減っていない: 

  • 処理-1(処理成功):口座Bに一万円を増やす->○ 
  • 処理-2(処理失敗):口座Aから一万円を減らす->×

振り込まれずにお金が消えてしまう:

  • 処理-1(処理失敗):口座Bに一万円を増やす->× 
  • 処理-2(処理成功):口座Aから一万円を減らす->○

どちらも銀行にとっては致命的です。業務システムではこのように、複数の処理がすべて実行されるか、あるいはすべて実行されないか、必ずそのどちらかにならないと困る場合があります。

処理の失敗がなくても、並行処理により複数の処理のタイミングが重なってしまうと、予想しない結果になることがあります。

一万円しかないのに二万円送金できてしまう:

  • 処理a-1(処理成功):口座Bに一万円を増やす 
  • 処理b-1(処理成功):口座Bに一万円を増やす→二万円が振り込まれる 
  • 処理b-2(処理成功):口座Aから一万円を減らす→残高ゼロに 
  • 処理a-2(異常状態):口座Aから一万円を減らす→残高が「マイナス一万円」に

こちらでは、処理1と処理2の間に別の処理が入ってしまうことで問題が発生しています。もしこんなことが可能だったら、複数のATMからタイミングを合わせてお金を引き出すことで、計画的に悪いことが出来てしまいます。

トランザクション処理に求められること「ACID性」

このように業務システムでは、関連する複数の処理(トランザクション)を、問題が起こらないように実行することが求められます。では、「問題が起こらないように」とは具体的にどういうことなのでしょうか。 

銀行の例でもわかる通り、発生する問題は考えてもすぐに解らない難しい現象です。しかし幸いにして、有名なデータベース技術者のジム・グレイ氏が、信頼性のあるトランザクション処理に求められる性質を「ACID性」としてまとめています。 

・原子性/不可分性(Atomicity) 

複数の処理が全て実行されるか、あるいはすべて実行されないかのどちらかになることを言います。銀行の例での最初の例が、原子性を欠く場合に該当します。 

・一貫性/整合性(Consistency) 

処理の結果、データがあってはならない状態にならないことを言います。「口座残高は0以上である」というルールがあるなら、処理の前後において残高がマイナスにならないことを言います。 

・独立性(Isolation) 

トランザクション処理中の中間状態が外から見えないようにすることを言います。一万円しかないのに二万円送金できてしまう例は、独立性を欠く場合に該当します。 

・永続性(Durability) 

トランザクション完了通知をした後、その状態が保たれることを言います。例えば「送金を完了しました」のお知らせが来た後にシステム障害が起こり、システム復旧後に送金する前の状態に戻ってしまった場合は、永続性を欠く場合に該当します。 

このようにトランザクション処理では様々な処理上の配慮が必要になることがあります。これらに毎回個別に対処することは現実的ではないため、トランザクション処理実現のための方式や、トランザクション処理を実現するソフトウェアが用意されています。 

「つなぐ」技術と分散トランザクション

トランザクションと聞くと、データベースを連想する人も多いと思います。データベースエンジン(RDBMS)には大抵、トランザクションに関連した機能が備わっており、トランザクション処理の問題はデータベースに任せて解決できることがあるためです。 

同一システム内での処理なら、データベース内の処理に任せて済むかもしれません。しかし、「つなぐ」技術の世界、例えばSOAやEAIでは、トランザクション処理がさらに難しくなります。処理1と処理2が全くの別システム上での処理、ということが多くなるためです。 

  • 処理1(業務システムA上のパッケージソフトX内のDB書換処理):口座Bに一万円を増やす 
  • 処理2(業務システムB上のCSVファイルの書き換え処理):口座Aから一万円を減らす 

それぞれ別々に動作している異なるシステムを、何らかの手段で、それぞれの処理に対して、外部から処理間の関係を調整してトランザクション処理を実現しなければならなくなります。 

「つなぐ」技術の世界では、あらゆる種類のリソースに対する処理が求められます。CSVファイル やExcelファイルに対する処理や、クラウドのデータ、データベース、様々なパッケージアプリケーションなど、分散したさまざまなリソースを組み合わせて利用できることが求められます。 

したがって、「つなぐ」技術を実現するためには、このような様々なものを組み合わせての分散トランザクションが実現出来る必要があります。つまり、「つなぐ」技術においては分散トランザクションの実現が難しいポイントであるとも言えます。 

二相コミット(Two-Phase Commit)

トランザクション処理は、処理結果を確定せず外部から見えないようにして処理を進め、全ての処理に成功したら処理結果を確定(コミット)し、処理に失敗したら既に実行した処理を取り消す(ロールバック)ことで実現できます。例えば、以下のような方法です。 

  • トランザクション開始 
  • 処理A:処理成功だが未反映 
  • 処理B:処理成功だが未反映 
  • 全ての処理が成功したのでコミット開始 
  • 処理B:処理結果が他から見えるようにする 
  • 処理A:処理結果が他から見えるようにする 

しかしながら、トランザクション処理においても例外的な事態によるトラブルが起こり得ます。例えば、上記の手順では以下のような問題が起こり得ます。

  • トランザクション開始 
  • 処理A:処理成功 
  • 処理B:処理に成功するが、処理完了までに長時間かかる 
  • 全ての処理が成功したのでコミット開始 
  • 処理B:処理結果が他から見えるようにする 
  • 処理A:処理完了から時間が経ちすぎているため、トランザクションタイムアウトが発生し、処理結果が確定されない

処理Bは処理されたのに、処理Aは処理されないままとなり、望ましくない状態になります。

このような問題に対処するために、さまざまなトランザクション処理方式が考案されています。残念ながらあらゆる事態に対応できる処理方式は理論上存在しないことが証明されており、複雑な手順にすると処理が遅くなることが多いため、現実的な落としどころが求められます。

信頼性が求められる場合に良く利用されているトランザクション処理方式として、二相コミット(Two-Phase Commit)というものがあります。

  • 調整者:処理の準備を依頼する 
  • 処理A:処理をコミットが行える状態まで進める 
  • 処理B:処理をコミットが行える状態まで進める 
  • 調整者:全ての処理者から準備OKが帰ってきたら、処理実行(コミット)を依頼する 
  • 処理A:準備状態から処理を完了させ、完了メッセージを返す 
  • 処理B:準備状態から処理を完了させ、完了メッセージを返す 
  • 調整者:全ての処理者から完了が帰ってきたら、トランザクションが完了したとして終了する

処理にいきなり取り掛かるのではなく、まず最初に、トランザクション処理のための準備フェーズを設け、全ての処理者が準備に成功したという応答を返したら処理を実際に完了させてトランザクションを完了させる判断を下し、その次に処理を確定するフェーズを実施する方法です(このため「二相=ツーフェーズ」と呼ばれる)。

この仕組みにより、例えば処理完了までに時間がかかり過ぎるような場合にも、先ほどと同様の問題は起こらなくなります。 

ロングラニングトランザクション(Long running transaction)

トランザクション処理には、処理の完了までに長期間を要する場合があります。例えば、注文によって開始され、荷物が発送され、荷物が相手に届き、相手が受け取り確認をすることで終わるトランザクションなどです。長時間かかるトランザクションであることから、ロングラニングトランザクションなどの呼ばれ方をします。 

通常のトランザクション処理では、他が間違って使わないように処理に係るデータをロックし、トランザクションが完了するまで未確定の処理結果など状態を保持して待つ必要があります。これはシステムに負荷をかけますし、後続の処理をブロックしてしまうこともあります。また、システム内部で長時間にわたって状態を保持しつづけなければならないので、システムをメンテナンスのために停止することも難しくなります。 

そのため、処理に長時間を要するトランザクションに、通常のトランザクションの実現方法を用いることは適切ではないことがあります。例えば二相コミットはせいぜい分単位の待ち時間に使うものだとされ、少なくとも日や月の単位で待つような場合には適さないとされます。そのため処理完了までに長期間を要するトランザクションには、別の考え方、ないしは別の方式で取り組む必要があります。

そこで、補償トランザクションをつかったトランザクションの実現方法が考えられています。

トランザクション完了まで確定待ち状態にせず、処理を完了させてとにかく進めてしまいます。途中で実行できない処理があった場合は、新たに取り消し処理を行って、実行した処理の実行結果を帳消しにします。このような補正処理のことを「補償トランザクション」と呼びます。

海外旅行準備の例:

  • 飛行機を確保する→処理成功 
  • 旅行先でホテルを予約する→処理成功 
  • 有休を取る→取れなかった 
  • 補償トランザクション:ホテルをキャンセルする処理実行→処理成功 
  • 補償トランザクション:飛行機をキャンセルする処理実行→処理成功

なお、通常のトランザクションを全く用いないわけではなく、小さい単位に分けて通常のトランザクションを用いるような作り方もします。

トランザクションの途中で処理結果を反映させてしまいますので、処理中の中間状態が他の処理から参照(や書き換え)可能になります。また、自身が処理をしている間に他の処理が思わぬことをして処理の前提が変わってしまうようなことも考慮しないといけなくなります。

そのため、最初の銀行の例で示したようなトラブルが起こったり、思わぬことが起こってしまいがちで、補償トランザクションをどのような処理にするか良く考えなければならず、設計や実装が難しい側面もある方法です。 

おわりに

今回は、トランザクション処理とは何かについて、そして「つなぐ」技術においてトランザクション処理の実現がポイントであることがあることについて説明をしました。 

「つなぐ」ことは、簡単に思えることもあると思います。読み書きできたら良いだけでしょ、と思えるかもしれません。読み書きできるツールならいろいろあるぞ、とも思えるかもしれません。しかし、実際に取り組むといろいろ難しいことが出てきます。分散トランザクションの実現はそのようなポイントの一つだと思います。 

「つなぐ」ためのソフトウェア、例えばSOAやEAIのツールは、システム同士を「つなぐ」ための面倒なことを片付けてくれるソフトウェアであるとも言えます。 

次回は、連携の手段としてのHUBとBUSについて説明をします。

コメント

ログインしてコメントを残してください。

Powered by Zendesk