JTA/JTS
現実逃避シリーズ第2弾(苦笑).
とめどもさんが日記でJTAとJTSについて混乱されているようなので,ちょっと書いてみるてすと(またしても弱気,ほとんどうろ覚えなので).
Java以前の分散トランザクション
正確には非Java環境での分散トランザクションですが,重要な仕様が二つあります.
この仕様は,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が定められています.
これらの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においても,分散トランザクションが必要ということで,その仕様としてJTSとJTAの2つが定められています.
自分の記憶によれば,最初に定められたのはJTSです.これは,CORBA OTSを単純にJavaにマッピングしたものです.CORBAというのはプログラミング言語独立の仕様であり,前述のinterface
はCORBA IDLで記述されています.乱暴に言うと,これをidl2javaしたものがJTSです.よって,膨大なJ2EE関連の仕様の中にありながら,JTS仕様書の本文はわずか10ページ程度しかありません.詳細はCORBA OTSを見ろってことなのです.
なお,とめどもさんが多少混乱されているようですが,JTSは実装ではなくて仕様です.CORBA IDLによるinterface
をidl2javaして作られるのは,Javaのinterface
です*7.CORBAでは実装を規定していませんから,それをJavaにマッピングしても実装は得られないのです.
そのJTSですが,当然ながらCORBA OTSと同じ問題を抱えています.それは,CORBAが前提だということです.いかにJ2SEの一部としてJava IDLというCORBA実装が提供されているとはいえ,CORBAが広く使われているわけではありませんし,そもそもJavaとして,CORBAに依存した分散トランザクションしか用意されていないのはいかにもまずいでしょう.
ということでJTAです.JTSがCORBA OTSのJavaマッピングなら,JTAはX/Open DTPのJavaによる焼き直しという感じで,そっくりのモデルとAPIを持っています.その主なinterface
.
UserTransaction
- TXに相当する
interface
. XAResource
- XAに相当する
interface
.
ちなみにXAResource
ですが,名前もそこで定義されているメソッドもそのままXAな感じなのですが,これはあくまでJavaのinterface
です.その実装が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にはJTAとJTSという二つの分散トランザクション仕様があるわけですが,その主要な違いはCORBAに依存するか,しないかです.まれに,JTSは低水準でJTAは高水準という説明を見かけますが(っていうかJTAの仕様書にそういう図があるし),JTSにもCurrent
というUserTransaction
と同等の高水準interface
があるので,そのような説明は妥当ではないと個人的には思います.現在の位置付けとしては,アプリケーションが使用するのはJTAで,JTSはその水面下で使われることを想定しているっていうことはよく分かるのですけど.
そういえば「Seasarのからさわぎ」で,とめどもさんでしたっけ? JTSを実装をしたい!という方がいらっしゃいましたが,CORBAを前提とすることがほとんど考えられない現状では,その必要性は限りなく0に近いと考えてよいと思います.もちろん,趣味でということなら何の問題もありません.ぜひ実装してください!(^^;
JTAを使おう
JTAを使うと分散トランザクションを実現できるわけですが,分散トランザクションを使わない場合でもJTAを使う価値はあると思います.たいていのJTA実装は,トランザクションに参加するXAResource
が一つだけなら2フェーズコミットを行いません.S2のJTAもそうなっていますね.また,JDBCのcommit()
/rollback()
を直接呼び出すようなJTA実装もありでしょう.
ですから,たとえDB1つしか使わないという場合でも,JTAを使ってもいいのです.重要なのは,トランザクションはリソース(JDBCとかJMSとかJCAとかEJBとか)とは独立したものだということです.アプリケーションでJDBCのcommit()
/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使っていてもそういう事態にちゃんと備えた運用手順を用意しているとは限らないのであうあう.心より恥じる.
あー,すっきりした.そろそろ現実に戻らねば... 無念だ.