AsakusaとTsurugiとバッチ処理(昨年に引き続き)

[昨年に引き続き]

 

Tsurugiについて

詳細は以下

okachimachiorz.hatenablog.com

 

・これは前回の繰り言

要するにRDBを作りましょう、という話。いろいろインメモリーでメニーコアとか、ECC(Epoch-based Concurrency Control)とか、いろいろ特長はあるけど、目標としている機能の一つに「writeバッチ処理に強い」というものがある。

 

バッチ処理の困難さ

このあたりも繰り言にもなる。基本的にまず、そもそも論としてwrite処理は既存RDBとは相性が良くない。これは単純に整合性を持たせる(serializable)ためのコストが大きいことによる。そもそも相性の良くないwriteをさらに大量に、かつ一度に書き込むバッチはさらに重ねて相性が良くない。

 

以下、今年一年の進捗的な話

 

■Tsurugiの現状として

現在、Tsurugiでは本格的にバッチ処理対応に入っている。上記の通りの困難さが常につきまとうので、今後の具体的な方策は常に変更されるというか、試行錯誤しながらになる。

 

現在の見通しでは、個人的には

1.そもそもバッチ処理の整理

2.ベンチマークの作成

3.処理方式の検討

4.実装と実行結果の検証・ベンチマーク

5. 3-4の周回

6.目処がついた段階でその他ツールの整備

7.時間切れタイムアウト

の順で開発をしていくものになると思う。現状では、ベンチマークの作成が「大体こんな感じかな」で終了し、処理方式の具体的な検討に入ってる段階になる。

 

バッチ処理の整理

 去年の段階ではまだまだ整理はついていないよーん、という話だったけど、現時点は徐々に整理がつきつつある。まず前提としてのバッチ処理とは何者か、という話になるが、端的に言えば「いわゆる業務系の複雑な処理で、特に大量のread/writeを含むtransaction群をまとめて実行する処理」ということでいい。これも前のエントリーの繰り言になるが、Tx業界用語ではlong transactionという言い方が一般的で、いわゆる“バッチ処理”という言い方は日本固有だ。「まとめて処理する」ということでは、普通はbulkという表現の方がよい。

bulk処理の場合は複数transactionをまとめて、というニュアンスがあり、long transactionでは単一のtxで処理が長い、という意味の方が強い。日本的な「バッチ処理」という意味ではbulkの方が本来的な意味に近い。

 

その「バッチ処理」だが、今のところは、具体的には以下の二つのカテゴリーが検討されている。それぞれプロトタイプのベンチマークもできつつある。

 

1.Read&Write back

処理すべきデータの塊をまず一斉に抜きだしておいて、しかるべき処理を施したのちに、一斉に書き戻す。大きなIPO(input-process-output)の流れで処理を行うスタイル。旧来型の汎用機バッチからの、特にファイルベースでの処理からの、伝統的なバッチ処理。事前にある程度設計をしておいて開発されることが多い。バッチ処理対象と対象以外のデータの分離がしやすいのと、処理失敗時のデータのリカバリーや障害対策時の切り分けがしやすい。

 

2.Bulk long transaction

一つ一つの長い処理を、そのまままとめて固まりとして流す。そこそこの規模の(しかし、大抵は謎に巨大な)SQLを準備して、でかいLoopで全部一斉に(かつ、順に)処理をするスタイル。どちらかというとRDBでのSQLベースでなんとかします、というオープン系のバッチ処理によく見られるタイプ。バッチ処理対象と対象外のデータの分離がしづらい、オンラインバッチ処理で使われることが多い。個々のレコードをどかどか更新・参照をしながら、同時にある程度規模で定期的に集計・締め処理をリアルタイムで行い、速やかに上書き更新の必要がある時の処理。処理失敗時・障害発生時の対応手段がDBの機能に強く依存するため、小規模なものであれば、お手軽に開発できる一方で、巨大化したときのコスト(処理時間・開発メンテ工数)が(大抵のユーザが事前に見積もったコストより)大きく跳ね上がることが多い。

 

■現在のエンジンとバッチ処理

現在、Tsurugiの処理エンジンについては、2つを検討している。あくまで検討で本当にどうなるかわからない。・・・本来ならあまり公にすべきものではなく、もっと鉄板になってから書いた方がいいのだけど、NEDOプロなんで多少ふわふわでも開示すべき、という話。

 

Sirakami(白神)

SILOベース。ある程度の実装も行いつつある。こちらは多分ファーストチョイスとして提供されると思う。

OCCベースになるので、普通にwriteは不利になる。read heavyで writeは少々、というワークロードでは現時点では一貫性を担保(serializable)した世界最強プロトコルの一つ。シンプルでかつ強靱。ただしwriteについては、激しく不利である。

 

OCCの性格上、Bulk long transactionは向いていない。なにも考えずに細かい短い処理とwrite heavyなbatch処理を混在させた場合、場合によっては(back-offどれだけやろうが)終了しない処理が発生する可能性がある。よって、バッチ処理を実装するのであれば、普通にRead and write-backスタイルになり、OCCになんらかの手当をし、かつそれをtransparentになるような形にする必要がある。

 

Oze(尾瀬

MVSRベース。こちらはまだ設計の試行錯誤段階。SirakamiがTSベースのOCCであるのに対して、こちらは今現在はTSベースでもlockベースでもない特殊なプロトコル。おそらく単独のエンジンとしてはTsurigiの最初のリリースでは製品化は時間的に無理だと思う。プロトタイプとして提供される可能性がある、というレベル。

 

MVSRベースなので当然serialization空間は広い(non-deterministicであれば理論上もっとも広い)。その分だけwriteには有利にはなる。ただし、メカニズムが複雑になるので、OCCに比べて、特に単純なワークロードが頻発するような場合は不利になる。

 

バッチ処理について言えば、Read&Write back / Bulk long transactionのどちらも可能になる。別段特別な追加的なアーキテクチャは必要ない。

 

■SirakamiとOzeの比較について

Ozeはまだ、検討段階でしかないので、比較云々は当然時期尚早ではあるけれど、デザイン的には別ものであることは明らか。Read主体であれば、ほぼぶっちぎりにSirakamiになるし、大量のWrite主体であれば、Ozeの圧勝だと思う。・・・まぁ要するに真ん中がベストって話になるが、そんな最強なものがあっさりできれば世話はない、という話でしかない。Write heavy batchに関して言えば、当然Ozeに軍配があがるはず。Sirakamiでも当然対応はするが、制約がつくと思う。

 

あとは第三案として、Sirakamiがベースで、一部Ozeのコンセプトを限定的に入れたものという技巧的な手がある。が、複数のCCを混在させたRDBは普通はない。とはいえ、バッチ処理を(あるいはタイムアウトしたwrite系の処理とかretryさせるときに)一種のプロテクトモードで実行すりゃいいだけだと個人的には思っているので、これだけリソースがある時代なら普通にアリだとは思っている。そもそも相性が壮絶に悪いなら適当にIsolateしてやればいいでしょう?という話は、理屈だけなら通るわけで・・・。この手の仕組みはRDBのど真ん中から作らないと絶対に無理なので、今回はいい機会なので、検討ぐらいはやってみるつもり。そもそも僕の担当でもあるし。普通にダメ元ではある。

 

以上はTsurugiの話。んで以下からAsakusa。

 

■Asakusaの処理

前回のエントリーでAsakusa=BatchTxの設計そのもの的な側面があるよ、という話だけど、これは整理をすると、Asakusaの処理が総じて、Read&Write back型に属するというところに落ち着く。Asakusaはもともと旧来型の大規模バッチ処理の分散処理での高速化というのが目的であるし、現在の使われた方もいわゆる基幹系のバッチに使われているのが大半。なので、どちらかといえば伝統的なバッチ処理への対応のためのスタイルになっている。また、加えて、処理自体をRDBから分離して分散処理し、RDBに書き戻す、というアーキテクチャをもっているため、形としてはRead&Write backを採用せざるを得ない側面もある。

 

SirakamiはRead&Write backでの対応になると思うので、Asakusa的な発想・ノウハウはそのまま利用できる。逆にOzeベースのものはAsakusa的ではないので、どうなのよ?というところもある。

 

ではAsakusaとTsurugiをどーするのか?という話にはなるが・・・

 

■Tsurugiのバッチ処理DSLとしてのAsakusaについて

 これも前回のエントリーと同様。まず、前提として、Nautilusとしては、当然AsakusaはTsurugiの対象として考慮されるべきものだし、会社のポリシーとして顧客サポートをきっちり行うことが企業活動のベースになっているので、検討はする。

 他方、Tsurugiは公金プロジェクトであり、Nautilusの私物ではない。よって、ユーザにAsakusaを強制することはそもそも論になる。よってTsurugiでのバッチ処理の「窓口」は一義的な提供は、当然より間口の広いものになる。

とはいえ、個人的には「業務プロセスを考慮した、複雑なフロー制御を旨とした、SI前提のバッチ処理DSL」では現在、Asakusaを越えるものはないと思うし、今後OSSでは永久に出る気がしない。RDB上でAsakusaが直接動いて、通常のSQLの実行のパフォーマンスにまったく影響せず、シームレスに相互が利用できるのであれば、Tsurugiの使い勝手については確実にプラスにはなると思う。

 なので、検討はするが、NEDOプロの範囲ではやらない。あくまでNautilusの責任の範囲において行うし、結果どうであれ、必ず検討は行う。ここは変わらない。

 

その上で、DSLとしてどうみるか?という点だけど。

 

■AsakusaとTsurugiの組み合わせについてなにがうれしいか?

 

このあたりの議論は、現時点では整理も特にしていない。自分の個人のぱっとした思いつきでは大体以下3点。

 

1.SQLのハーネスとして

普通に複数のSQLの制御ハーネスとして欲しいというところはある。単純なSQLで処理が済むのであれば、それをUDFとしてAsakusaでコールして済ませたい。簡単なSQLをシンプルに書いて単純実行する内容をAsakusaで記述するとなかなかに面倒ではある。そういう場合は普通にSQLで簡単に処理したい。

 

2.Asakusaからのダイレクトの実行エンジンへのエントリーポイントの確保

SQLのハンドラーとしてではなく、普通のjoin処理での例外や泣き別れ・分岐処理については、そもそもSQLで記述するのは無理があるので、これらの複雑なjoinについてはAsakusaでハンドルしたい。簡単な字義通りのクエリーであればSQLで、複雑怪奇なjoinであればAsakusaで、それぞれ記述して、proceduralな形でまとめてTxとして処理し、ついでに例外処理もうまいことやっておきたい、そんなところかと。SQLを介さず(別に介してもいいがもうさすがにSQL生成はないでしょ)に直接スケジュールを叩き込むスタイル。CC側は別に普通にハンドルするだけで「Txが来ました。了解、処理開始します」で問題ない。

 

3.設計・テスト等のsuitesとして

そもそもAsakusaの存在意義・メッセージとしては「設計は残るが、実装は腐る」ということに尽きる。ちゃんと設計すれば、その分払ったコストに見合うだけのメリットはある、ということである。

SQLバッチでも同じようにちゃんと設計→実装→テストの枠組みを踏襲する仕組みとしてAsakusaを利用したい。ストプロSQLでのCI的な話であれば普通にAsakusaを利用した方が確実に見通しが良い気がする。ストプロってなにげにテストがつらいというかできないので。

 

とまぁこんな感じではあるが、これだと普通にSQLをUDFとしてAsakusaでコールできればよい、という話になってそれでおしまいなのか?とちょっと思う。これでは身も蓋もない気がするが・・・普通にアリな気がする。

 

例えば、集計系の単純なSQLを書いて、レポート生成系の処理やら例外ハンドラーをAsakusaで記述・処理して、出力をexcelにしたりして、RDB→集計→エクセルの流れをSIすると、なにかと楽な気がする。分散処理もしてくれるわけで。BI系処理でExcelからダイレクトにSQLコールもありだけど、処理を複雑にするとか例外処理とかそもそも多段のjoinとかやるとあっという間に100億太陽質量くらいの暗黒ブラックホールに成長したりするし、そもそもテストとかできん。

 

重い業務処理はそもそももちろんだけど、「軽いところの、もしかして重めかコレ」のところでAsakusaが使えたらいいよねとは思う。とにかく自動的に仕様が残るのがよい。いや、まじで。

 

■proceduralなAsakusaをTsurugiのCCから見たときにどう見えるか?

 

CC屋としてはAsakusaでのSQLのコールのタイミングとか、そのあたりのCC処理の方式とかは、なにも考えずにOKというわけにはいかないようも気がする。以下気になる点を書いておく。ただ、これはAsakusa、というよりもストプロのホスト言語一般に要求される仕様だとは思うし、もっというとある程度の低レベルのserializability空間しか担保できない仕組みではあれば不要だが、ある程度の高機能なCCができる仕組みで必要なものではないかと思う。

 

・externalityの確保

連続でSQL(Tx)をなげた時の最低限確保しなければいけない順序の制約。あるTxがcommitされて、「それを確認して」から、別のTxが投げられたときは、必ず連続(logically serial)にならなければならない。コア数少な目なリソース制約が厳しい以前の環境であれば、ほぼ暗黙に守られるが、メニーコアや分散環境下では気をつけないとかなり怪しいことになる。

 

・明示的なparallelismの提示

このあたりは、前回のエントリーと同じ。ストプロでTxを仮にステートメント単位で区切って、並列的に処理が「必ずできる」場合や、「できそうなのでやってほしい」場合にハーネス側で、明示的に指示をだすということ。特にLoop処理では、「順に処理」と「並列に処理」は意味が違う。また明示的な並列処理でも、「必ずできるので後先考えなくてよい」と「できると思うのでやってほしい」ではこれまた意味が違う。

 

・transactionのcontrol境界の提供

どこまでのstatementを一括のTxにするか、という指示。一括にした場合普通にcommitの単位が単一になる。失敗時には全部roll backされる。基本的にはCC側が勝手に判断するので実は不要だけど、運用として必要な場合があるので。

 

CC側から見るとこんなところかな、とは思う。もちろんDSLとして必要なもの・あるとうれしいものは鬼のようにあるとは思うけど、普通に言語設計の話になるので、ここでは割愛。

 

そんな感じ