nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

JDBC XAデータソース

JBoss Advent Calendar 2011の14日目のエントリです。今日はJDBC XAデータソースの説明。

XAってなによ?

ツーフェーズコミット(2相コミット, 2PC)するためのインタフェースです。

XAとか2PCってどんなときに使うの?

一つのトランザクションで2つ以上のトランザクショナルリソース(JDBC JCAリソースアダプタ=データベースとか、JMS JCAリソースアダプタとか)を扱う場合に利用します。

XAとか2PCってなんで必要なの?

例えばデータベースが二つ、DB-AとDB-Bがあって、DB-AからDB-Bにデータを移す処理をするとします。このとき、DB-AからSELECTして削除してDB-Bにインサートする、というようなことをするわけですが、この処理が途中で落ちたら即データ消滅、データ重複などのデータ不整合という結果になってしまいます。XAを使うとこういった複数リソースを利用するトランザクションの不整合の発生を可能な限り抑えることができます。また、アプリケーション側では特に意識せず2つ以上のリソースをごく普通に利用すれば良いだけで、特殊なコーディングをする必要はありません。

XAではトランザクションを制御する側をトランザクションマネージャ(TM)と呼び、データベースなどのリソース側はリソースマネージャ(RM)と呼んだりします。TMはJava EEアプリケーションサーバに含まれているので、基本的にはアプリケーションサーバ=TMという理解で良いでしょう。

XAの2PCでは、全ての更新はprepareフェーズでTMから各RMにprepareを発行することで伝えられ、各RMからok/ngが返ってきたらTMがトランザクションログを記録します。ここまでがprepareフェーズで、ここからcommitフェーズに入り、各RMへcommit/rollbackを発行します。

  • prepareフェーズでTMがダウンした場合、RMにXAResource#recover()を発行してpreparedなトランザクション一覧を取得し、そのTMがダウン前に発行したpreparedなトランザクションをrollbackします。
  • prepareフェーズでRMがダウンして応答しない、エラーになった場合はトランザクションを進めることはできないのでトランザクションをrollbackし、例外を送出します。
  • commitフェーズでTMがダウンした場合、復旧後にトランザクションログがあればcommitフェーズをリプレイします。
  • commitフェーズでRMがダウンした場合、TMはcommit/rollbackを決定して各RMに発行し、例外を送出します。ダウンしたRMについてはRM復旧後にcommit/rollbackをリトライします。

おおざっぱにはこんな感じですが、現実にはもっとバラエティ豊かな分岐をします。例えばRM側ではprepareされた後一定時間commitされないトランザクションは一定時間後にタイムアウトしてrollbackされる(ヒューリスティック決定と呼びます)ので結果不整合になる、であるとか、トランザクションログが失われた、というような要素やシナリオがいっぱいあります。

また、TMの中には上記の説明に合致しない挙動をするTM実装もそれなりにあると思います。

JBoss ASでのJDBC XAデータソース設定

JDBC XAデータソースは設定要素がJDBC ローカル(非XA)データソースとほんの少しだけ異なります。

http://community.jboss.org/wiki/ConfigDataSources

  • <xa-datasource-class>
    • XAデータソースのクラス名を設定します。
  • <xa-datasource-property>
    • XAデータソースにプロパティを設定します。XAデータソースではXADataSourceのインタフェース定義を見るとわかるのですが、プロパティを受け付けるようなインタフェースは存在しておらず、またローカルデータソースのようにDriverインタフェースは使えません。プロパティはXAデータソースクラスのインスタンスに対し、リフレクションで設定されます。
  • <isSameRM-override-value>
    • リソース実装のバグ対応パラメータです。XAResource.isSameRM()の返却値を指定された値でオーバライドします。isSameRM()は2つのXAResourceが同じリソースマネージャを指すかどうかを調べるAPIです。recover()が全く同じものを返却するときにtrueを返却する、というのがセオリーなんですが、これが正しく実装されていないリソースドライバ実装なんかがあったりするのでオーバライドできるようになっています。
  • <track-connection-by-tx>
    • リソース実装のバグ対応パラメータです。JBoss AS 5系からデフォルトで有効になっているのですが、トランザクションインタリーブ(同一コネクション上で複数トランザクションを切り替えて使う)を抑止する設定です。トランザクションインタリーブは仕様上許されているのですがほとんどのリソースドライバ実装で正しく実装されていません。
  • <no-tx-separate-pools>