土曜日, 5月 31, 2008

【EC開発体験記 -リソース志向-】 APIをやめデータ構造に着目する このエントリーを含むはてなブックマーク


あらかじめ断っておくが、ここで説明するリソース志向は、私自身の開発の経験から必然的にたどり着いた解であって、一般的に説明されているROAとは多くの部分で異なる。ここでは、正確なROAの説明をするつもりはないし、本当のROAとは?などと、不毛な議論もやりたくない。ROAもしくはリソース志向という言葉を便宜上使わせていただいているが、それは、自分の解に一番近いと思われるからである。したがって、私の説明をそのまま世間で使うと痛い思いをするかもしれないので気をつけていただきたい。

まず、リソース(以下Resource)の定義から説明したい。Resourceとは、分析クラスから導き出されるEntityを具現化したものである。噛み砕いていうと、モデリングにより定義された自己完結したデータの集合という感じだ。前記事で、分析クラスのBoundary、Control、EntityをStrutsのMVCモデルに落とせない話をしたが、リソース志向では、EntityがそのままResourceとなる。これは非常に分かりやすい。(Entity設計の責務は誰が負うべきかという課題はあるが、これはまたの機会に説明したい)また、Resourceは、それを指し示すID(URI)をもち、データ構造を公開できる。公開の手段は、XML、JSON、YAMLなんでもいい。これについては、インターオペラビリティの話として後ほど詳しく説明する。それから、ResourceのAPIはCRUDの4つだけとする。トランザクションを管理し、それぞれのAPIは1つのUOW(UnitOfWork)で実行されなければならない。注意していただきたいのは、このAPIはHTTPに限った話ではないということ。言語のAPIにもなりえる概念的なものということだ。また、RESTはステートレスといわれるが私はそうする必要はないと考えている。単純にステートレス=揮発性と考えると、リソースが揮発性になってしまうのはむしろ不都合になるからだ。リソースは不揮発性だが、トランザクション管理という意味では、不完全なものは破棄するという性質(揮発性)は持たなければならない。ステートレスの話はトランザクション管理に帰結すると思うので、私の定義では1UOWとなった。

<私が考えるリソース志向>

1)リソースは自己完結したデータの集合
2)リソースはデータ構造をもち公開できる
3)リソースに対してCRUDで操作する
4)リソースは特定できるアドレス(URI)をもつ
5)リソースに対して1UOWで操作する

ここで話が戻るが、公開するデータ構造は何でもいいという話をしたい。公開用のスキーマ言語は、必要最低限のデータ構造を表現できれば十分。それは、きちんとインターフェースを定義したい方はスキーマ言語を使うだろうが、それでもセマンティクスを十分に定義できないのは同じであり、いずれにしてもデータ長や意味などを説明した資料は必要になるからだ。
 私はかつて、Webサービスの最も重要な性質の一つとして、インターオペラビリティを挙げていた。しかし多くの経験から学んで今ではかなり否定的になっている。数年前になるが、XMLコンソーシアムで、参加企業十数社がそれぞれの製品をもちよってWebサービスの接続実験を行ったことがある。その実験では、たしかに、言語やプラットフォームを越えて接続できるという利点は認められたが、各製品の制約が多すぎて、WSDLを変更しなければならないことがしばしばあった。製品に合わせてWSDLを変更するなんて本末転倒である。実際に接続するには相当な苦労があったわけで、ダイナミックバインディングなんてのは夢の世界。絵に描いた餅であった。そもそも、スキーマ言語でセマンティクスを完全に定義できないことの重要性に早く気づくべきだった。
 インターオペラビリティの問題は本当に難しい。WSDL(XMLSchema)はCORBAにはできなかった言語の壁を越えることができたがセマンティクスの問題は解決できなかった。AtomPubは、この問題を解決できる可能性があるとは思ったが、普及していない現状を見る限りでは難しいように思える。(AtomPubにはすごく興味があって、ReflexではRFC前であるにもかかわらず実装を行った。たしか、2005年頃までのdraft-ietf-atompub-protocol-04.txtの実装を行ったが、自分の感性に合わなくなったことと、インターオペラビリティに疑問を感じたのでやめてしまった。)結局のところ、AtomPubにも同様の困難が待ち受けていると思われる。

そこで得た結論は、否定の3と7

3.各サブシステムで共通のエンティティを使用(最小単位は1品1葉1レコード)
7.サブシステム間を密に結合させない(サービスだけで繋ぐ)

これは要するに、システム共通のentityを定義して、それをやりとりすることだけに注力しようということだ。SOAPやAtomPubなどのAPIを介さないし、そのような共通APIに該当するものを敢えて開発しない。しかし、最低限は必要になるので、先に挙げたリソース志向を踏まえて定義する。具体的には次のような非常にシンプルなAPIを定義することになる。

<API例(entityはリソースの1インスタンス)>

entity = create(entity);
entity = retrieve(entity);
entity = update(entity);
entity = delete(entity);

サブシステム内であれば上記APIをそのまま使う。サブシステム外ではHTTPのPOST/GET/PUT/DELETEがそれぞれ対応する。例えば、GETを実行するとシリアライズされたentityが返ってくる。繰り返しになるが、entityの表現はJSONでもXMLでも何でもいい。実際のプロジェクトではReflexによりJSONとXMLの両方を使っている。(Reflexはentityの写像を取るフレームワーク。entityをJSONとXMLにシリアライズできる。)

自己完結したデータの集合が意味するところは、一つは自分から他のリソースへの依存がないということ、もう一つは、外からの影響を受けないこと。例えば、他リソースのインターフェースが変更になっても、スケルトンを取り込みなおす必要がない。
 サービス志向では、インターオペラビリティの問題があって、セマンティクスに関係した部分が不安要素として残っていた。自己完結したデータの集合、すなわち、リソースを中心に設計していくことで、より安定した大規模なシステムを並行開発できるようになる。


日曜日, 5月 25, 2008

【Hadoop】 究極のWebサービス。やられた感いっぱい ><。 このエントリーを含むはてなブックマーク


AWSとHadoopでiText変換はまさに考えていたところ。ショック大きい><;。
でもこれがWebサービスの究極の姿であり、Virtual Technologyが目指すところ。めげずに頑張ろう・・・。
Self-service, Prorated Super Computing Fun!
解説
Running Hadoop MapReduce on Amazon EC2 and Amazon S3

Hadoop MapReduce
Hadoopアーキテクチャ
Hadoopクイックスタート日本語訳
Hadoopを触る
Hbaseアーキテクチャ
Hadoopいろいろ
Hadoop HDFSのRead/Writeサンプル
それからIBMもHadoopに注目していることが分かった(前記事の発言は撤回しますm(_ _)m)
IBMのBlue Cloudは、ウェブコンピューティングの別名

金曜日, 5月 23, 2008

【EC開発体験記-設計-】 MVCモデルは進化する このエントリーを含むはてなブックマーク



アーキテクチャスタイルと重きを置いているレイヤを見ていただきたい。これは2年ぐらい前の絵なので、ちょっと古いし、正直、適切でない言葉が使われていると思う。RESTはSOAと同列に書くとすればROAだろう。SOAなんてカッコつけないで、Webサービスぐらいにしておけばよかった。また、Web2.0をBoundaryに置いているが、広範囲に使われる言葉になった今では違和感がある。当時はAJAXブームもあり、クライアント技術が先行していたので、それほど違和感なく見れたのだが、それにしても、この業界の進化は早い。今これを書き直すとすれば、View、Mashup、Resource、といったところかな。実装技術では、Viewのところが、JavaScript, Flex2 AIR, PHPで、Mashupのところが、PIPES, Plagger, sMash、Resourceのところが、Hadoopやhbase, ネット上のRSS,ATOMとするのがしっくりくる。Reflexはどのレイヤにも当てはまるがResourceに置くのが一番適していると思う。(書き直した絵=>RESTful設計におけるレイヤと主な実装技術
 長々とわけ分からん絵の説明をしたが、いいたいことは設計をどうすべきかということ。
 オブジェクト指向設計では、まず、ユースケースの図と記述を作成して分析クラスを導き出す。プロジェクトでも同様にやったのだが、これは非常に有効な手法であった。しかし、分析クラスから、その後の設計クラスをどのように作っていくかについては、モデラーの感性に依存するというか、正直なところ曖昧で、あまり機能していないのではないかと感じている。世の中にはこれを課題と感じないスーパーなモデラーがいるのは承知している。分析クラスをそのままStrutsのMVCモデルに落とし込めないのが分かっていても、彼らは平気な顔して設計クラスをサクサク作っていける。(もしかしたら彼らには分析クラスは必要ないのかもしれない)あるいは、経験を積んだモデラーにとっては(※)モデリングがパターン化されており、単純な工程で済んでいるのかもしれない。
(※ オブジェクトを設計していくことをモデリングという)

分析クラスのBoundary、Control、EntityがそのままStrutsのMVCモデルに落とせない、なんていいだすと、スーパーモデラーじゃなくても異論が噴出するかもしれない。(面倒なので詳細は省略するが)単純にいうと、分析クラスのBoundaryは主に画面(入出力装置)、Entityはデータ、Controlは要求に応じてデータを集めたり加工して返すところとなる。問題は、Strutsに落とし込んだときに、Controlのおき場所に困ることだ。StrutsにおけるControlerはフレームワーク自身を指す(あるいは含んでいる)ので、アプリケーションのControlerの立場はどうするの?といった話になる。多くはModelに集約されることが多いようだが、これはこれで面白くない。なぜなら、せっかく分析クラスで分けたものを設計クラスで一緒にするなんてモデリングの意味ないじゃん、と思うからである。稀に、Controlerのパッケージを作って、きっちり別管理しているプロジェクトもあるようだが、管理に強制力はないため、プログラマーによってグチャグチャにされてしまい、どれがModelでどれがControlerか区別がつかなくなるのが現状のようだ。まあ、ControlerとModelをきっちり分けることで、いいことがあるとすれば、ワークフローを導入してControlerを外だしにするような場合だろう。そんな話は滅多に聞かないから、グチャグチャになっても、それほど大きな問題はないと思われるが・・。
いずれにせよ、Modelの実装はけっこう面倒で工数がかかる。テーブルはRDBの世界であり、一方のEntityはObjectの世界なのでインピーダンスミスマッチを解消するためO/Rマッピングが必要となる。私はこのO/Rマッピングが工数増大の大きな要因と見ている。オブジェクト設計のドメインモデルでは、Entityからテーブルを作成していくのが基本だが、テーブルは非常に重要な部分なので、人によってはテーブル設計から先に始めるかもしれない。(分析クラスがなくてもモデラーが平気な顔して設計できるのはテーブル設計があるからだろう?)しかし、ボトムアップ的にそうやってしまうと、Rの都合により作成されたEntityクラスとなって、モデリングが無視されたオブジェクトになる。そのままだと今度はControlerやViewから使用できなくなるので、O/Rマッピングしたクラスをさらに組立て直して、Modelの形を崩さないよう努力するはめになる。そのような組立直しクラスをDXOと呼ぶそうだが、そんな無駄な処理をモデルの中でゴニョゴニョやっているのである。これはもはやオブジェクト設計ではない。

 そこで、得た結論は、
否定の6.オブジェクト設計をしない

である。正確には、設計クラスのModelをオブジェクトにはしないで、オブジェクトで扱うデータに関心を集めようというもの。Modelとは、モデリングによってできた成果物(※)でありオブジェクトであるから、データとプログラムがくっついている状態。それからプログラムを切り離してデータのみに関心を集めて考える。データだけであればオブジェクトではないのでオブジェクト設計をしないことを意味する。とはいえ、分析クラスまでは設計して論理ビューを作る。論理ビューで定義したサブシステム内であれば、勝手に設計/実装して構わないという感じになる。具体的な実装例の記事を書いているのでこちらを参照願いたい。

ZEROからRESTfulアプリを作成
ビューの実装
リソースの実装
フローの実装

(※ Modelの定義は曖昧で様々な説明が存在する。私はこの説明が一番好きだ。)

しかし、これでも同様にO/Rマッピング問題は存在する。Entityは本来はオブジェクトの視点で設計するが、実際にデータを格納するテーブルの設計とどう整合をとるか。これは次の2点で解決する。要は、設定よりも規約(Convention over Configuration,CoC)をやろうということだ。参考:Entity設計

1.Entityはデータ構造の集まりであるから、これをそのままRDBにマッピングする(Reflexはその機能をもっている)
2.ControlやViewからEntityへのアクセスはCRUDのみとする

Entityの設計では、まず、データ構造を定義して単純なAPIを公開する。公開の手段は、XMLやJSONなどを使って表現する。また、APIはデータ操作だけなのでCRUDのみとする。HTTPであれば、GET/POST/PUT/DELETEの4つだけだ。これはRESTと呼ばれるアーキテクチャスタイルだ。Webサービスであればサービスごとに定義されたインターフェースとなるところを、RESTでは4つのAPIでリソースを操作することになる。

データのみに関心を集めたEntityでは構造化はされているがRのイメージに近い。
テーブルのカラム名がクラスのフィールド名と同一となり、それをシリアライズした、JSON、XMLのタグ名も同じになる。テーブルは、O/Rマッパで隠蔽されているイメージから開放され、語彙が統一できてシステム全体で見通しがよくなる。

あと、忘れてならないのはビジネスロジックの存在だ。これは今までModelの中に実装されていたが、Modelはデータだけになったので、別途管理する必要が出てくる。また、実行時にModelをパラメータとして渡すことになるが、まあ、これはこれでテストが楽になるのでOKかなと思っている。

<関連>
RESTfulな新3層アーキテクチャ
ドメインモデル貧血症、サービスと振る舞いについて
3分で分かる設計の話

水曜日, 5月 21, 2008

【雑記】 何やってんだ? このエントリーを含むはてなブックマーク


最近オープンしたgeohistサービスは、地図と携帯のマッシュアップブログだ。作者は、あの横井勝商店。目的はいまいち不明だが発想は面白い。中をのぞくと横井さんの行動が全部見れる。そのなかで、もっと面白いものを見つけた。なんと、geohistのティッシュを新宿で配っているではないか。

「ユニクロの袋でティッシュ配りはまずいか」だってさ。心配なのはユニクロの袋じゃないだろ?

腹がよじれるかと思った。

<追記>
geohistとGeohashing 座標とハッシュ値でオフ会を組み合わせると超クレイジーなイベントができるかも。集まるまでの途中報告をgeohostで行うことにすればいい。

火曜日, 5月 20, 2008

【JUI Tokyo】 JavaScriptいろいろ、個人的にOpenID このエントリーを含むはてなブックマーク




昨日、JUI Tokyoに行ってきた。jQueryのPaul Bakausさんによるプレゼンでは、新機能のデモが印象的だった。次々にインパクトのあるデモが紹介されたがソースは非常に短い。ほとんどがプレゼン1画面に収まっていた。Q/Aでは、経歴とか、チーム開発の困難さとか、いろいろ。彼はJavaScript暦5年だそうだ。チーム開発はあまり困難を感じてないようだった。フルタイムは僕一人だからといっていた。JavaScriptの大規模な開発は私も興味があるところだが、ちょっと拍子抜けした感じ。jQueryの今後に期待したい。その他は、amachanやma.laさんの高速化の話が参考になった。今困っているところなので、こういう話は非常に助かる。

下は懇談会の様子。右からshinichitomitaさん、ma.laさん、amachangさん。

amachangさんは私の困っている話を「ウン、ウン」と聞いてくれたが、ma.laさんは半分あっちの方を向いて聞きたくなさそうだった。でも、「表の項目全部にイベントハンドラーを登録している」というようなことをいったら、「そりゃいかんだろう。数百、数千になると動かなくなるよ。1つで十分」とおもいっきり反応してくれた。(onclickを使え!と返されると思ったのだが本音は違うみたい・・?)その後、shinichitomitaさんがやってきてOpenIDの話をした。OpenIDはなんで流行らないか。特にRP側(*)で流行らない理由がわからないというので、一応、RPを導入する可能性のある立場でもある私の気持ちを率直に伝えた。一番の理由は、必要性を感じていないということだ。がんばって導入したところで効果が今の段階ではよく見えない。あえて挙げるとすれば、ユーザビリティの向上が図れた結果、以下のような効果があることぐらい。

 1) 既存OpenID会員など多くのお客様を呼びこめる。
 2) 機会損失を小さくできるのでリピータを増やせる 。

ちょっと買ってみようと思った「一元さん」は、会員登録とかいろいろ面倒だからすぐに逃げてしまう。たしかに簡単に買える仕組みは必要だろう。でもよく考えてみたら、ECサイトではお客様に商品をお届けしなきゃいけないわけで、最低限、名前と住所などの個人情報の登録は必須となる。(補足すると、OpenIDでできるのは本人確認のみ。例えば、takezakiというIDでログインしていれば、確かにtakezakiだということを確認できるが、それ以上のことはない。本名もわからなければ住所も分からない。また認可は別の仕組みが必要となる)
 こう考えるとなおさらOpenIDの導入は余計なことのように思えてくる。そもそも、OP(*)がRPに個人情報を渡すとは思えないので、RP自身で個人情報を集めなければならない。ユーザビリティの向上は何もOpenID使わなくてもできるし、機会損失だって本当のところどうだかわからない。結局、キラーアプリケーションでも出てこない限り、OpenIDはあまり流行らないんじゃないかなあ、なんてことを懇談会では話した。

 ちょっと宣伝になるが、ここでは、「一元さん」のためにメールアドレスだけで買えるような工夫をしている。お届け先はメールに飛んできたリンクをクリックして登録すればよい。最低限必要な本人確認および、お届け先入力をこのように実現することで、ユーザビリティ向上を図っている。また、メール注文サービスというのもやっていて、k@g.edionjpにカタログの番号をメールするだけで商品が買えるようにもなっている。原理は一元さん購入と同じだ。

 でも今、いいアイデアを思いついた。ポイントをOPで管理するというのはどうだろう。

1)OPでポイントを管理する(RPで購入するとOPのポイントが増える)
2)すべてのRPでポイントを使用できる

ポイントはお金と同じように扱われるから、ECだけじゃなく、いろいろ利用するケースはあるかも。SuicaとかEdyとかに変換できると嬉しい。使えるところが増えれば増えるほどポイントの価値は高くなるので、それであれば自社ポイントはやめてでも導入したい。
それから、すべてのRPの商品情報がOPで検索できるといいなあ。

(*) OPとは. OpenID Providerの事。
RPとは. Relying Partyの事。(要はリクエスタ)

月曜日, 5月 19, 2008

【雑記】 CAPTCHAの話 このエントリーを含むはてなブックマーク


CAPTCHA絵画 <=これ、CAPTCHAが芸術になるなんて面白い話だ。

CAPTCHAは文字などを人間しか認識できないような絵にする。これを逆に機械が読み取って文字にするのはとても難しいという。完全に破るプログラムは人工知能の一つといえるかもしれない。人口知能の定義はたしか「端末越しにやりとりしたときに人か機械か見分けがつかないもの」だったと思う。

面白い話をもうひとつ。アダルトサイトに仕掛けて多くの人にログインを促して裏ではCHAPHA破りに使うという技術がある。破られる側からしたら、あたかも人工知能を使われてるように見えるだろう。これを「エログリッドコンピューティング」というらしい。その命名が笑える。

[ロ]CAPTCHA と「エログリッドコンピューティング」

土曜日, 5月 17, 2008

【EC開発体験記-サービス志向-】 疎結合で真価を発揮 このエントリーを含むはてなブックマーク


以下は「Webサービスによる非集中化プログラミングモデル」、懐かしいけどちょっと恥ずかしい自分の論文(ProVision掲載 2000年)からの引用。

 非集中化プログラミングモデルでは、サービス提供者の責任範囲を、インターフェース定義に従って「あるリクエストに対して処理する、あるいはメッセージとしてレスポンスを返す」ことだけに限定しており、サービス提供者は、全体がどのようなアプリケーションで、どのように利用されているかを関知しません。このように、責任が明確になることで独立性が高まり、また、管理の隠蔽という性質から、プログラム変更などでシステム全体に及ぼす影響を最小限にできます。


今のSOAのエバンジェリストに読ませたら鼻で笑われそうな内容だが、当時はこれでも脚光を浴びた(と本人は思っている)わざわざ自分の論文を引用したのは、それを自慢したいからではなく、1年かけて実際にECサイトを構築し、サービス志向設計を経験していかに重要で効果的かを知ることができたこと、また、SOAを活用したすばらしいシステムであることを自慢したいだけなのだ。(結局自慢かよ・・・)

否定の7
「7.サブシステム間を密に結合させない(サービスだけで繋ぐ)」


言語の記事でも触れたが、私たちのECシステムでは、PHPとJavaの両方を使っている。PHPが主にリクエスター、Javaが主にプロバイダーだ。実は、PHPの開発者はJavaを知らないし、Javaの開発者はPHPを知らない。共通スキルはMySQLとLinuxだけだ。一昔前であれば考えられなかったチーム編成なんじゃなかろうか。お互いを干渉しない。干渉しようと思ってもできない。結果、自然と自分の責任範囲に集中することになる。これで本当の意味でサブシステム化が可能となる。私はサブシステム化する目的の一つは並行開発にあると考えている。密結合では実質的に無理な状況であった並行開発を、サービス志向であれば可能にできる。「完全なる隠蔽」によりシステム全体に及ぼす影響を最小限にできるため、分業・並行開発ができるようになるのだ。実際にはそうなっていないStrutsプロジェクトを見ると哀れみの念さえ憶える。(言い過ぎか・・)ただし、何から何まですべてサービス化するのは冗長になりすぎるのでやめた方がよい。うまく設計しないと、パフォーマンスが悪くなったり、不安定になったりする場合があるので注意が必要だ。システムはシンプルに作るのが一番である。お金をかけた分、それに見合うコード量を要求する客はまずいないし、シンプルでかつ品質が良ければ結構なことだ。なので、必要ないところは極力サービス化しないようにすべきだろう。おすすめは、サブシステム間の接続点でのみ使用すること。また、入力後のデータ参照は自由に行ってよいが、入力の時点では、あらゆる「狂ったデータ」にも耐えられるように厳しく設計すべきだろう。それに、入力の接続点は唯一にした方がいい。

今回のシステムでいうと、受注サブシステムへの入力は、インターネットサイトからのWeb受注以外に、コールセンターにおける電話受注がある。電話受注ではPHP画面から受注テーブルに直接SQLのINSERTを実行しており、一方のWeb受注ではサービスプロバイダー経由で受注テーブルに入るようにしている。このようになったのはインターネットサイトを電話受注システムより後に構築したからだ。結果的に入力における接続点は2つ存在することになったのだが、これが大きな不幸を招く結果となった。テーブル設計書や仕様書レベルにはない細かい違いが災いして、入力経路が異なる受注で「狂ったデータ」のような振る舞いを起こすデータがしばしば見られるようになったのだ。このように、2つの接続点があるのは管理上もよろしくないので、ここは思い切って受注プロバイダー1本に統一すべきであった。そうすればデータの信頼性も高くなる。

お気づきかもしれないが、サービスの接続点は厳しくした方がよい、というのは前記事と矛盾することをいっている。わけわからんようになってしまったが、それだけ前記事はチャレンジングであるということで、ご容赦願いただきたい。

<追記>
 ちょっと前になるかもしれないが、サービスのインターフェースを厳密にしようという考え方があった。すべてのサービスやそのクローンはWSDLだけで作れるし、それに従えば自由に接続できると考えた。(ダイナミックバインディング)しかし、実際には予測できない「狂ったデータ」に対応できないので、うまくいかないことの方が多い。あらゆる可能性を考慮して厳密に作ろうとするのはそもそも無理がある。今回の2つの接続点の件がいい例だし、前記事のいわんとしているところもここにある。
なので、今の私のなかではエンティティに着目する考え方に大きく傾いている。どういうことかというと、サービスをエンティティとビジネスロジックに分離して、エンティティをプロバイドするという考え方だ。これはRESTに近い。近いうちに詳細は説明するつもりだ。

木曜日, 5月 15, 2008

【EC開発体験記-スケーラビリティ-】 信頼性とジレンマ このエントリーを含むはてなブックマーク


前記事で、トランザクション処理は厳密にしないといいながら、でも「これはこれで問題だ」と書いた。厳密にしないとエンバグになることは前述した通りだし、結果的にデータの信頼性が損なわれるので問題と書いた。しかし、あながちそうでもないらしい。ここによると、エラーデータについては無視すべきという、一見暴論のような考えが述べられている。エラー処理を厳密にするのはデータの信頼性確保と同義と考えるとトランザクション処理も同様に考えることができると思うので混同して説明する。(私の頭の中ではエラー処理とトランザクション処理を混同していいことになっている)
抜粋すると、

いかに注意深くエラー処理を設計したとしても,広大かつ多様なデータを扱ううちには,誰も考え得なかったような驚くべきデータに出くわすことがある。


それはそうだ。そして、

言わば「狂ったデータ」に対して逐一丁寧な対処を行っていくのは,果たして賢い方法であると言えるだろうか? Pike らは, Sawzall が扱うような種類のデータに限って言えば,そのような「狂ったデータ」を無視してやるだけでシステムの安定性を確保することができると主張する。


これはデータの信頼性の考え方とは全く逆の発想のように思える。言い方をかえると、スケーラビリティを確保するには信頼性を犠牲にしてもしかたがないといったような乱暴な話に聞こえる。トランザクション処理にしても、下手したらパフォーマンスに大きく影響してしまうので、無ければそれに越したことはないが、それを犠牲にするなんて聞いたことがない。

ミッションクリティカルな基幹システムにおいては厳重にチェックされた矛盾のないトランザクションだけがデータベースに格納される。そうしないと様々な部分で弊害が出てシステムが動かなくなる。「狂ったデータ」はデータベースに存在してはならず、入り口のところで逐一丁寧な対処を行っていく必要がある。(基幹システムとは企業の中核を担うビジネスの基本部分をシステム化したものである。システムで処理できなくなると業務が止まるのでミッションクリティカルなシステムとも言われる。)

一方は入力を厳しくして正しいデータだけを管理する方法、他方は狂ったデータも管理するが無視する方法。要は、どうすればスケーラビリティのあるシステムを構築できるか。これがポイントになる。例えば、ミッションクリティカルなシステムであっても、数百件/日、多くて千件/日の受注件数しかなく、蓄積されるデータ量が数G未満であれば、メインフレームを使わなくても構築/運用できる。Java、Tomcatでもいいし、PHPやPerl、Rubyでも、MySQLと組み合わせればそこそこ動くシステムを作ることは可能である。そこそこ動くとは、トラブルがあっても運用でなんとかカバーできるという意味である。(そんなもの、ミッションクリティカルと呼べるか!というIBMの偉い方からツッコミが入りそうだが、クリティカルな業務をなんとか運用できるわけだからOKだ)しかし、千件を超える受注件数であったり、データ量が多かったりすると難易度は非常に高くなる。実際に運用を経験すると分かるが、システムの小さな不安定要素が運用に大きな負荷となって圧し掛かることになる。

ECの受注業務は、単純な定型業務と思われがちだがそんなことはない。バッチがコケたり、通信エラーが発生したり、毎日のように新しいトラブルが発生している。混乱の原因は大体は見当がつく。そう、「狂ったデータ」である。トラブルが発生したとしても、時間内に受注確定作業を完了させて取引先に送信しないと、お客様の希望日に商品が届かなくなってしまうので、逐一丁寧な対処を行い、臨機応変に対応しなければならない。これを解決できないと「狂ったデータ」が蓄積されて、翌日の業務にも支障が出る。さらには「解決数<蓄積数」となった時点で、問題が発散してシステムはダウンする。最悪の場合はサイトクローズだ。安定して動いているように見えるシステムでも、マニュアルによる「待ったなし」の対応をどれだけ現場がやっているか、これが運用の実態なのだ。このように、なかなかシステムは安定しない。安定させるには地味な努力と時間が必要である。なので、否定の16は確かに参考にすべきではない。

16.入力は寛大に受け付け、出力は厳しくせよ(参考にすべきでないより)

ところが、「狂ったデータ」を無視せよという話は、Sawzall が扱うような種類のデータに限ってとはいうものの、システムの安定性を確保することができるという。
「狂ったデータ」を無視せよとは、入力を寛大に受け付けよという意味にもなる。これは限定的とはいえ、非常に興味のある話である。特に興味があるのは、Googleのクローン技術と言われるHadoophbaseだ。この記事も参考になる。

私がスケーラブルなシステムに興味があるのは今の実装技術に限界を感じているからだ。私が担当しているECシステムは作ってまだ半年しか経たないのに多量のデータが原因でパフォーマンス劣化を引き起こしているし、これから担当するシステムも同様な問題を抱えて困っている。なんとかならないものか。ムーアの法則がサチッてきて今後もハードウェアによる解決が望めない。そう考えると並列技術しかないが、googleを見るとそれを解決しているように思える。そこに、ミッションクリティカルなシステムをスケーラブルにするヒントがあるかもしれない。そういう意味で、Reflexが参照するデータソースはHadoopでキマリ。Reflex+Hadoopにより、大規模なミッションクリティカルサービスを構築できる可能性が出てきたのは嬉しい限りだ。

よく考えてみれば、この分野はIBMの独壇場だった。大規模なミッションクリティカルシステムを構築できるのはIBMだけ、というのが殺し文句だったはず。いま、この分野で尖がったテクノロジーが見当たらないのは真に残念である。ぜひ今後もこの分野の第一人者であってほしいものだ。(MapReduceはGoogle論文にあるらしいけど、IBM論文ネタとしても最高だよなあ。・誰か書いてる人いるのかなあ)

まあ間違いなく言えるのは、トランザクション処理は厳密にしない、とか、「狂ったデータ」を無視せよとか、一昔前であれば稚拙といわれただろう、ということ。

日曜日, 5月 11, 2008

【EC開発体験記-トランザクション処理-】古くて新しい永遠のテーマ このエントリーを含むはてなブックマーク


トランザクション処理といえば、システムの基本中の基本で古くて新しいテーマである。ミッションクリティカルな基幹システムでは特に重要な要素だと言われて久しい。何で重要なのかはさておき、IBMの新人研修でもそう教えられるので、それが「常識」であることは間違いない。では、トランザクション処理って何のためにあるのか、結論を一言でいうと正しい情報を管理するためである。コミットで正しい情報、ロールバックで正しくないか、まだ受け付けられていない情報となる。長年IBMにいたせいか、ミッションクリティカルな基幹システムはメインフレームが最適という言葉が身に染み付いていて離れない(=洗脳されている)。最初はIBMがいうところの「真のミッションクリティカルな基幹システム」を研究したかった。実際に92年から94年までの担当はメインフレーム一色で、OS390、CICSやIMS、他社メインフレームまでいじっていた。(そういう私が今はLAMPで基幹システムをやるなんて・・思えばかなり冒険したものだと思う。)その時代はホストでもオープン化という大きな波があり、OS390がUNIX APIを搭載しはじめた頃で、新人ではあるものの、元々オープン系を得意としていた私にもいろいろと仕事はあった。MVSでHTTPサーバってどうやって動かすの?あのOSの構造を知っているものには想像すらつかない。実際、他社メインフレームのDBとOS390IHS(HTTPサーバ)を接続したときは自分でも感動したものだ。(この経験がY社での荷物追跡システムに生かされることになるのだが・・)
トランザクション処理はこの頃勉強した。commit/rollbackするまでの間においてACIDを保障する仕組みぐらいしか理解してなかったが、実際、DBやMQなどのサブシステムにはそう説明されているので、仕組みとしてはそれ以上でも以下でもない。しかし本当はもっと奥が深い。例えば、ロックを前提とするものが多いのでパフォーマンスに大きく影響する。また、アプリケーションが複雑になるとトランザクション開始点を覚えておかないといけないので管理が大変になる。実際に運用するとわかるのだが、厳密に管理できてないアプリはすぐにlockwaitで糞づまる。そのlockwaitの原因を紐解くと、トランザクション処理のバグであることが多い。しかし、すぐにはバグを認めたがらない開発者もいるので要注意だ。lockwaitというエラーメッセージの意味だけを説明して「DBサーバの増強が必要です」なんてヌケヌケといってくることもある。(私は直感的にバグと分かるのでカッときてバトルになることが多い)とはいえ、その「lockwaitの原因を紐解く」作業は結構大変だったりする。なぜかというと、実際にエラーメッセージを出している(lockwaitを引き起こしている)処理は無実であり、それより前にロックしている処理の方が有罪であることが多い。しかし、これが簡単には見つからないのである。また、これは私たちのアプリ特有のエラーではあるが、Duplicateエラーも同様に発見が困難である。これは履歴テーブルに受注レコードを移動させる際にキー重複が原因で起こる。移動処理では、受注テーブルにあるレコードを履歴テーブルにINSERTして元の受注テーブルからDELETEする。これが一つのトランザクション処理となっていれば問題ないのだが、エラーとなったトランザクションが正しくrollbackされていないために、残骸となって履歴テーブルに残ってしまっている。次に正しいトランザクションにより移動処理が実行されようとしても、この残骸のためにDuplicateエラーとなってしまっているのだ。結局、これらを解決するために、ソースを全部引っ張り出して「抜け」を探すことになる。「抜け」とは、開始点だけあるような処理、例えば、SELECT FOR UPDATEだけ実行されていてcommmit/rollbackされてない箇所のこと。他人のソースをひっくり返してロジックを追う作業は本当につらい。不具合を見つけたら見つけたで、よく説明しないと開発者との関係がギクシャクする。彼らはバグではないことを主張していたので自分が理解できるまで納得がいかない。そこでトランザクション処理の基本を講義したり、なぜlockwaitになるかをサンプルプログラムでシミュレーションするハメになる。パフォーマンス劣化の原因まで説明するのは本当に難しい。・・・ああ、面倒くさい、もう理解しなくていい。全部AutoCommitでいいよ。となって前記事に載せた、否定の1項目となる。

「5.トランザクション処理は厳密にしない」

これはこれで問題なのは明白だ。いいたいことは要するに、開発者にトランザクション処理を意識させない、にもかかわらず、厳密にトランザクション処理がなされること。AutoCommitのノリで全部書けちゃうのが理想だ。

思いかえすと、メインフレームやDB、Java(JDBC)で使われてきた、commit/rollbackに代表されるコード埋め込み型のトランザクション管理は元々問題あるとされてきた。それだから、EJBのトランザクション(コンテナによるコンテキスト化)に触れたときはショックだった。EJBは結局、コンテナが重くて使い物にならなかったが、Seasarはいけると思った。トランザクションコンテキストをAOPで持ちまわるなんて、なかなか素晴らしいアイデアである。これをJ2EE仕様を損なわずに実現している。しかしまだ不十分な感は否めない。

繰り返しになるが、開発者にトランザクションを意識させている点は大きな課題だ。それがJ2EE仕様を外れたとしても、意識させないことの方が重要だと思う。

一つの解として、すべてをサービス化して、その入り口と出口にトランザクションポイントを置く方法がある。トランザクションスクリプトというデザインパターンであるが、要は、サービスの実行が全部成功すれば、commitとし失敗すればrollbackとする。サービスの最小単位がAtomicとなるのは異論はあるかもしれないが、現時点では、これが一番分かりやすいパターンのような気がする。というわけで、Reflexはトランザクションスクリプトパターンを採用する。サービスの実装はトランザクションを意識せず自由に書いてもらって結構。ただし、エラー時はExceptionを親にスローすること。これだけですべて解決!・・・とうまくいくのかなあ。

 
© 2006-2015 Virtual Technology
当サイトではGoogle Analyticsを使ってウェブサイトのトラフィック情報を収集しています。詳しくは、プライバシーポリシーを参照してください。