JTA/JTS

現実逃避シリーズ第2弾(苦笑).
とめどもさんが日記JTAJTSについて混乱されているようなので,ちょっと書いてみるてすと(またしても弱気,ほとんどうろ覚えなので).
Java以前の分散トランザクション
正確には非Java環境での分散トランザクションですが,重要な仕様が二つあります.

  • X/Open*1 DTP(Distributed Transaction Processing)

この仕様は,80年代後半に作業が始まって,90年前後に最初の仕様がリリースされた(と記憶している)もので,簡単に言ってしまうとTUXEDO*2やEncina*3といったTPモニタのための仕様です(超乱暴).
初期のDTPのモデルはとても簡単(これが大事)で,主な構成要素は次の3つです.

    • Application(AP)
    • Transaction Manager(TM)
    • Resource Manager(RM)

このうちTMがTPモニタで,RMがDBMSとかMQSeries*4のようなMOM(Messaging Oriented Middleware)です.このような構成により,複数のRMに対してTMが2フェーズ・コミット(2PC)を司ってくれることになっています.
そして,上記の要素間で使用する以下のAPIが定められています.

TX
APとTM間のAPIで,トランザクションの開始・終了(commit/rollback)を行います.
XA
TMとRM間のAPIで,2PCを行います.

これらのAPIはいずれもC言語で規定されています.というか,XAに至っては関数ポインタの入った構造体なんていうしろものです.(^^;
DTPは後に拡張されて,cRM(Communication Resource Manager)という要素が追加され,(クライアント)APがcRMを通じて(サーバ)APを呼び出すためのAPI*5が定められたり,例えばTUXEDO上のAPからEncina上のAPを呼び出したい場合に,cRMを通じて複数のTMにまたがって2PCを行うためのXA+というAPI*6が定められたりしました.
さて,このDTPですが,TPモニタがそれほど広く普及したわけではないため,今となってはそれほど重要なものではないと個人的には思います.もっとも,XAだけは今も重要な存在です.

  • OMG CORBA OTS(Object Transaction Service)

X/Open DTPオブジェクト指向以前の分散トランザクション仕様です.それに対して,分散オブジェクト環境においても分散トランザクションが必要だということで,CORBAのCommon Object Serviceの一部として策定された仕様がOTSです.ということで,当然ながらCORBA上に構築された仕様です.
X/Open DTPはよく言えばシンプル,悪く言えば大雑把なモデルをベースにした仕様ですが,OTSオブジェクト指向の専門家とトランザクションの専門化により練り上げられた,緻密なモデルをベースとしています.悪く言えば複雑ってことなんですが.
どれくらい複雑かを味わっていただくために(笑),その構成要素が持つべき主なinterfaceを列挙すると...

    • TransactionFactory
    • Control
    • Terminator
    • Resource
    • SubtransactionAwareResource
    • Synchronization
    • Cordinator
    • RecoveryCodinator

このうち,始めの3つがTXに,その後の2つがXAに相当するって感じでしょうか.
トランザクションを開始するのと,それを終了(commit/rollback)するのに別々のinterfaceが用意されていたりして,結構煩雑です.そのため,高水準のinterfaceとして,

    • Current

が用意されています.これはCORBAでいうところの擬似オブジェクト(Pseudo Object)で,CORBAで定められているもののCORBAオブジェクトではない(ObjectReferenceを持たない),手軽にサービスを使うためのオブジェクトです.
CORBA OTSは,X/Open DTPの経験をベースにしているだけに,よくできた仕様だと思います.しかし,前提となるCORBAが普及しなかったこともあり,メジャーな存在にはなれませんでした.無念だ.
特に,上記interface中のResourceがXAに相当するものなのですが,これを実装しているDBMS等がほとんどありません.私が知る限りでは,O2というフランス製のODBMSくらいです.考えてみれば,OracleやMQなんかがCORBAをサポートして分散トランザクションに参加するというのも,ちょっとなんだかなーって思いますよね.おっと,Oracleは中にVisiBroker持っているんでしたっけ.油断大敵?
そのため,OTSを実装した製品を使う場合でも,たいていはResourceとXAのアダプタを使ってRDBMSなどを使用することになります.


Java環境での分散トランザクション
ここからようやくJavaについて.
Javaにおいても,分散トランザクションが必要ということで,その仕様としてJTSJTAの2つが定められています.

自分の記憶によれば,最初に定められたのはJTSです.これは,CORBA OTSを単純にJavaマッピングしたものです.CORBAというのはプログラミング言語独立の仕様であり,前述のinterfaceはCORBA IDLで記述されています.乱暴に言うと,これをidl2javaしたものがJTSです.よって,膨大なJ2EE関連の仕様の中にありながら,JTS仕様書の本文はわずか10ページ程度しかありません.詳細はCORBA OTSを見ろってことなのです.
なお,とめどもさんが多少混乱されているようですが,JTSは実装ではなくて仕様です.CORBA IDLによるinterfaceをidl2javaして作られるのは,Javainterfaceです*7.CORBAでは実装を規定していませんから,それをJavaマッピングしても実装は得られないのです.
そのJTSですが,当然ながらCORBA OTSと同じ問題を抱えています.それは,CORBAが前提だということです.いかにJ2SEの一部としてJava IDLというCORBA実装が提供されているとはいえ,CORBAが広く使われているわけではありませんし,そもそもJavaとして,CORBAに依存した分散トランザクションしか用意されていないのはいかにもまずいでしょう.

ということでJTAです.JTSがCORBA OTSJavaマッピングなら,JTAはX/Open DTPJavaによる焼き直しという感じで,そっくりのモデルとAPIを持っています.その主なinterface

UserTransaction
TXに相当するinterface
XAResource
XAに相当するinterface

ちなみにXAResourceですが,名前もそこで定義されているメソッドもそのままXAな感じなのですが,これはあくまでJavainterfaceです.その実装がJNIでXAを呼び出しても構いませんが,そうしなければならないわけではありません.Pure JavaなRMはXAをサポートせずにXAResourceを提供するでしょうし,Type4のJDBCドライバが提供するXAResourceにしてもXAを使ったりはしないでしょう.XAはCで定義されているため,対応するCランタイムライブラリのバージョンの組み合わせなどがシビアだったりする場合もあり,XAResourceネイティブなRMが増えることに期待大です.
JTAは,CORBAのようなプラットフォームからは独立した仕様です.そのJTAを,JTS上の薄いラッパーとして実装することもできます.そのような実装の例として,SunのJ2EE Reference Implementationや,IBMのWebSphere Application Serverがあります.これは,たまたまそういう風にJTAを実装したということで,JTAをネイティブに実装しても何の問題もありません.S2のJTAはネイティブな実装ですね.


ということで,JavaにはJTAJTSという二つの分散トランザクション仕様があるわけですが,その主要な違いはCORBAに依存するか,しないかです.まれに,JTSは低水準でJTAは高水準という説明を見かけますが(っていうかJTAの仕様書にそういう図があるし),JTSにもCurrentというUserTransactionと同等の高水準interfaceがあるので,そのような説明は妥当ではないと個人的には思います.現在の位置付けとしては,アプリケーションが使用するのはJTAで,JTSはその水面下で使われることを想定しているっていうことはよく分かるのですけど.
そういえば「Seasarのからさわぎ」で,とめどもさんでしたっけ? JTSを実装をしたい!という方がいらっしゃいましたが,CORBAを前提とすることがほとんど考えられない現状では,その必要性は限りなく0に近いと考えてよいと思います.もちろん,趣味でということなら何の問題もありません.ぜひ実装してください!(^^;


JTAを使おう
JTAを使うと分散トランザクションを実現できるわけですが,分散トランザクションを使わない場合でもJTAを使う価値はあると思います.たいていのJTA実装は,トランザクションに参加するXAResourceが一つだけなら2フェーズコミットを行いません.S2のJTAもそうなっていますね.また,JDBCcommit()/rollback()を直接呼び出すようなJTA実装もありでしょう.
ですから,たとえDB1つしか使わないという場合でも,JTAを使ってもいいのです.重要なのは,トランザクションはリソース(JDBCとかJMSとかJCAとかEJBとか)とは独立したものだということです.アプリケーションでJDBCcommit()/rollback()を直接呼び出すようなことは,もうやめましょう.S2のようなコンテナを使うと,何も気にしなくてもJTAを使うことができます.というかJTAを使っていると意識することすらありませんね.素晴らしい.


2PCは慎重に
逆に,JTAが使えるからといって2PCを気軽に使うのは控えたほうがいいでしょう.2PCを使うと微妙な問題につきまとわれます.

  • パフォーマンス

2PCではコミットの際に,TMはそれぞれのRMと2回ずつやり取り(たいていは通信を伴う)を行います.アプリの処理に比べたら軽いものかもしれませんが,パフォーマンスの検証をしてから慎重に考えた方がいいでしょう.

  • コミットの順序

たまに悩ませてくれるコミット順.例えばOracleとMQで分散トランザクションを行っている場合.両方に対するPrepareを終えると,TMは一つ目のRMをコミットします.それがMQの方だとして,メッセージのPUTがコミットされたとしましょう.その時点で,他のアプリケーションはMQからメッセージをGETできます.そこでメッセージ中の項目をキーにOracleにアクセスすると,悲しいかなOracleはまだ先の2PCのコミットが終わっていなくて,あるべきデータがない,なんていうタイミングが発生する場合があります.どっちを先にコミットすべきかはアプリ次第だったりするので,なかなか悩ましいですー.

  • TMの障害

たいていの場合,DBMSなどのRMはトランザクションを一方的にロールバックできます.セッションが切れたとかタイムアウトしたとかで.2PCを使っている場合でも,Prepareの前までは同じです.しかし,RMがPrepareに対してコミットできるとTMに答えた場合,その後はTMからcommit/rollbackを指示されるまで,RMはそのトランザクションを自分の意志でどうこうすることはできなくなります.当然,そのトランザクションが持っているロックもかけっぱなしです.ところがその時,TMが吹っ飛んでいたりしたら? TMがトランザクションのログを残してくれていて,それにアクセスできる場合などはすみやかにリカバリできますが,TMのトランザクションログにアクセスできない障害の場合,トランザクションをコミットするかロールバックするかを誰かが判断して手作業で実施する必要があったりします.こういう事態に備えた運用をするのは結構たいへんじゃないでしょうか? いやその,2PC使っていてもそういう事態にちゃんと備えた運用手順を用意しているとは限らないのであうあう.心より恥じる.


あー,すっきりした.そろそろ現実に戻らねば... 無念だ.

*1:現在はThe Open Group

*2:UNIX発祥の地であるAT&Tベル研で開発され,現在はBEAが所有.

*3:現在はIBMの一部であるTransarcが開発.

*4:言わずと知れたIBMのMOM.

*5:TUXEDO,Encina,CICSのAPIをそのまま追認という悲しい仕様ですが.

*6:例えに書いておいてなんですが,TUXEDOもEncinaもXA+は対応していないはず.NCRが開発(現在はBEAが所有)したTopEndくらいしか対応していないと思います.

*7:ヘルパクラスも大量に作られますが