水曜日, 9月 30, 2009

【Google App Engine】 Low level APIで前方一致検索およびPagingについて このエントリーを含むはてなブックマーク


 JDOが全然使えないことが明らかになるにつれ、最近ではLow level APIを使う方も多くなってきた。Low level APIは、シンプルで扱いやすく、パフォーマンスにも問題ないことがうけて支持されているのだろう。しかし、機能的には全然足りないので、自分たちで何とか解決しなければならない課題も多い。前方一致検索もその一つである。

Low level APIで前方一致かつPagingが難しい件

 前方一致検索は、JDOではcontent.startsWithを使えばできるようであるが、Low level APIでも、検索項目をソートすることで一応できることはできる。しかしPagingについては、2Page目以降を特定する方法が必要ななため、できないと思っていた。
 例えば、下図のように、”コーヒー”で前方一致検索した場合に、同じ商品名の”コーヒー010”が複数存在する可能性があるため、次ページにおいて、どのレコードから表示すればよいかわからない。この問題は、Low level APIでは、GREATER_THAN_OR_EQUALなどのInequality filterが、1つしか使えないという制約に起因するものである。(追記10/29:LESS_THANも使えることがわかっている。参照=>Keyとカウンタは別々に考えるといいかも
)そこで、「複雑なクエリのためのプロパティを仕込んでおく方法」を参考に、前方一致検索でかつPagingする方法を考えてみた。ただ、さらに安い順でソートしたり、全文検索などはできない。これらについては、TaskQueueを使って対応するしかないと考えている。
 また、ここでは、商品のデータ件数は無限にあるという前提で考えている。小規模なものであれば考える必要はないのかもしれないが、これまで繰り返し述べているように、クラウドの最大のメリットはスケーラビリティだと思っているので、それを阻害するような制約は排除すべきであると思う。



実装方法およびサンプル

具体的には以下のようにする。

1Page目:

キーワード(word=”コーヒー”)で検索する。Low-level Datastore APIのQueryで、商品名プロパティに対して条件をGREATER_THAN_OR_EQUAL指定。Reflex GAEのEntityConverter#convertでCondition指定をし、対象項目が指定されたwordで始まっていなければ結果に含めないようにする。(この処理はIn Memory)

2Page目以降:

キーワード(word=”コーヒー” )、最後の商品名(nextword=“コーヒー010”)、および、最後の連番(number=009)を指定する。
Low-level Datastore APIのQueryで、商品名+連番プロパティに対してnextword + number条件をGREATER_THANで指定。 EntityConverter#convertでCondition指定をし、対象項目が指定されたwordで始まっていなければ結果に含まないようにする。 (この処理はIn Memory)



前方一致でかつPagingを行うサンプル

1ページ目
2ページ目

火曜日, 9月 15, 2009

【雑記】 補償トランザクションの悪夢 このエントリーを含むはてなブックマーク


 数年前、補償トランザクションを駆使したシステムを構築したのだが、うまくいかずに酷い目にあった経験がある。以下の、Aさん宛てに書いたメール内容を読んでもらえれば想像つくと思うが、これは本当に酷いものだった。私はこのとき、補償トランザクションは絶対やるべきではないと心に誓ったのだった。たぶん、スーパーなプログラマーであれば、こんな酷いことにはならないのだろう。でも、普通のプログラマーであれば、十中八九、同様の結果を招くのではないかと感じている。名誉のためにいっておくと、Aさんはかなり優秀なスーパープログラマーの部類に入ると思う。本来、補償トランザクションは、とても難しいものなのだが、Aさんだから大丈夫かなと安心もしていた。ソースレベルでは完璧なロジックに見えて、テストにおいても問題ないような結果が出るのが補償トランザクションの罠。そこまで示されて、「何をそんなに心配されるのか」といわれると否定のしようがない。しかし、その難しさは、実際に運用してみないと本当の意味でわからない。なぜなら、補償トランザクションが問題になってくるのは本番運用以降であるからだ。想定していなかった補正失敗が発生するのを目の当たりにして大騒ぎになるのはよくあることである。補正失敗時のリカバリーは基本的に運用マターであり、そのあたりの運用リスクをよくよく考慮しなければならないのだが、設計段階ではほとんど気づくことができないものなのだ。今から思えば、補償トランザクションの理屈の甘さが見抜けず、安易に採用を判断した私にも責任があると感じている。Aさんにも苦労をかけて申し訳なかった。しかし、頑張ってACIDにさえしておけば、これほどトラブらなかったのではないかとも思う。ACIDであれば、All or Nothing的に管理されるので大きな問題は起こらない。多少、遅くなったり、実装で苦労するかもしれないが、そこはなんとか乗り越えて、ACIDを使うべきと考える次第である。
 ということで、Entityとトランザクション3で述べたように、GAEにおいてもEntityGroupを使って、最低限のACIDトランザクションを使うべきだと思っている。


Aさん

かねてから申しているように、この不具合は致命的なもので、運用チーム、データリカバリーに深刻な影響を及ぼしています。結果、多くの方に迷惑、システムへの不信をいだかれていることになっています。トランザクション処理がきちんとできていないシステムは基幹システムとはいえません。

また、Bさんにも調査していただいておりますが、本来は開発者であるAさんが調査すべきところだと思います。データ修正も、運用チームやBさんではなく、実際にプログラムを作成されたAさんの方でリカバリーすべきでしょう。厳しいことを申し上げているように思いますが、不具合というのはそれほど重いものであるということを認識すべきだと私は思います。

さらにいえば、この不具合は昨年秋のサービスイン当初から続いており、ずっと問題報告がなされていたにもかかわらず今だに改善されていません。3月のXプロジェクトでも再度依頼しましたが、結局、直りませんでした。このプロジェクトは不具合をすべてなくすことが目的で、それなりの予算を組んで実施したプロジェクトでした。その際、私は会社に全部直すことを約束しております。Aさんもその認識はあったはずです。

もう、トラブルは半年以上続いています。当社としては、この件に関して相当なコストをかけてきましたし、今後も発生するのはいかがなものかと頭を悩ましています。

酷な言い方かもしれませんが、もし、ご自身の技術(スキル)的に解決する自信がない、ということでしたら、他の方に協力を仰ぎ、ソースコードを全部見てもらうぐらいのことはやっていただきたいと思います。いずれにせよ、何かのアクションプランを早急に提示ください。もう限界です。


金曜日, 9月 04, 2009

【Google App Engine】 ついにキター! TaskQueueとXMPP このエントリーを含むはてなブックマーク



ついにきたぞ。

App Engine SDK 1.2.5 released for Python and Java, now with XMPP support


Task Queue API for Java

Python developers have been processing tasks offline using App Engine Task Queues since mid-June, but until now the feature was not available in the App Engine for Java SDK. The 1.2.5 SDK now includes support for creating Tasks and Queues in our Java runtime.


今週末は眠れなさそう。

木曜日, 9月 03, 2009

【Google App Engine】 datastore.Entityにおけるプロパティ変換について このエントリーを含むはてなブックマーク


Reflex ore 1.2.2におけるJSON変換

 今回はまず、Reflex Core 1.2.2の紹介からしたい。このバージョンから、JSONにおいてもハイフン、コロン、属性、テキストノードの表現ができるようになった。
 もともとJSONはプロパティ名と値がペアになったシンプルな構造表現しかできないが、XMLからJSONに変換する際には、名前空間や属性、テキストノードといったものをムリクリ表現しなければならない。先日、ココでお伝えしたとおり、実は1.2.1でも属性表現ができるようになっていたのだが、これには一部不具合があったので修正したのと、名前空間やテキストノードなどにも対応したので、1.2.2として公開させていただくことにした。

1. SONでは、要素名の"-"が"__"に変換されるため"type_str-ing"は、"type_str__ing"となります。
2. XMLの名前空間で用いる”:”は、JSONでは"___"に変換されるため、"xml:atom"は、"xml___atom"となります。
3. XMLの属性は、JSONでは"要素名____属性名"プロパティとなるため、"attr1"は、JSONでは"type_str__ing____attr1"プロパティとして表現されます。
4. XMLのテキストノードは、JSONでは、"_______text"プロパティに変換されます。


datastore.Entityのプロパティ変換について

 次にGAEのdatastore.Entityのプロパティ変換の話。
 GAEには、低レベルAPIが用意されているが、これを使うと検索結果はdatastore.Entityクラスのなかのプロパティと値という形で格納される。このままでは不便なので、JDOのようにJavaオブジェクトに変換する必要があるが、GAEのAPIでは変換APIが用意されていないので、私たちは自作のものを利用している次第である。(ReflexGaeのなかのEntityConverter)
 ところが、datastore.Entityのプロパティ変換は以下のような癖があるため、注意しなければならない。Javaオブジェクトとプロパティ変換という意味ではReflexと同じように一筋縄ではいかないようで、ここでもムリクリやっているのがよくわかる。

 1)元のクラスの項目名(JavaのField名)に"$"があると、datastore.Entityのプロパティでは"24"に変換される。
 2)元のクラスの項目名の先頭に"_"があると、datastore.Entityのプロパティでは""に変換される。項目の間の"_"はそのまま。
   o 例)modelの項目名「_$xml$lang」は、datastore.Entityのプロパティ名が「24xml24lang」となる。
    + -> "$"と先頭の"_"について、プロパティ名を変換して読む必要がある。
 3)プロパティ名が重複すると、名前の末尾に「"_"+番号」が付加される。
   o アルファベットの昇順で、番号なし、"_0"、"_1"、・・・という順番のようである。
   o 例)modelクラスに「test」「_test」「__test」という3個の項目があると、Entityクラスのプロパティ名は順に「test_1」「test_0」「test」となる。
     (abc...よりも、"_"の方が先)


 1)および2)に対応したEntityConverterは、ReflexGae 0.9.6に含めているが、3)については未だ対応していないのでご注意願いたい。これは今のところ、modelの項目名に、「test」「_test」のように、頭にアンダースコアがあり、かつ、同じ名前をつけないようなルールで対応するしかない。 

ReflexGae 0.9.6

 * KeyUtils : 親キーのnameを指定できるようにした
 * EntityConverter : Entityのプロパティ名を指定する時、"$"を"24"に、先頭の"_"を""に変換するよう修正


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