Google App EngineとMemcache API
こんにちは、某Perl界隈のIRCチャンネルでPythonがマイブーム的なKY誤爆をしてしまったtmaesakaです。
先日、以前から興味のあったGoogle App EngineとMemcache APIについて少し調べ、こちらに英文で報告したのですが、今日は日本語で要約したまとめを紹介します。
まず軽く前置きですがGoogle App Engine (GAE)とは、Googleが提供しているウェブアプリケーションをGoogleのインフラ上でスケーリングや冗長化など、ある程度のノウハウや資金を要求される面倒な事を気にせずに運営できるプラットフォームです。つまり、典型的なPaaSの例であり、サービスの運営コストをelastic(伸縮)にします。昨今バズワード化しつつあるクラウドコンピューティングの一種でもあります。
GAEのインフラはGoogleより提供されているAPIセットを用いて利用します。その中にはon memory cacheがMemcache APIという形で提供されています。インターフェイスはmemcachedと同様Key/Valueベースのもので、アプリケーションのトータルパフォーマンス向上に役立つAPIです。
このAPIはmemcachedを連想させられるネーミングですが、実際にGAEのドキュメントを読むと、こう記述されています:
The Memcache API has similar features to and is compatible with memcached by Danga Interactive.
つまりmemcached互換で、なおかつ似ていると書かれていているだけです。普通はこの辺で納得するのでしょうが、私はこういった文を見ると調べたくなる性格なので、ちょっと深入りしてみました。
実際にMemcache APIを使うのは簡単で、'memcache' モジュールをGAEパッケージからインポートします:from google.appengine.api import memcache
あとはアプリケーションから必要に応じて、各種APIメソッドをコールするだけです。
プロトコル違反なKeyでセットしてみる
memcachedのASCIIプロトコルでは、Keyの長さは250バイトまでという制限があります。それ以上の長さのKeyを送信するとmemcachedはERRORを返します。では、GAEはどうでしょう?遊び気分で300バイトのKeyで適当な値をSetしようとする以下のコードを走らせてみたところ:
from google.appengine.api import memcache memcache.flush_all() test_key = 'x' * 300 if not memcache.set(test_key, 'some_val'): print 'Failed to set' quit() print "Looks like we're good = " + memcache.get(test_key)
以下のエラーがローカルのApp Serverから返ってきました:
Keys may not be more than 250 bytes in length, received 300 bytes
あらあら、上記のエラーだけを見ると明らかにmemcachedっぽい動作ですが、memcachedに合わせているだけかもしれません。Keyの長さ制限はドキュメントに記述されていないので、このエラーに遭遇したら驚くデベロッパーもいるかもしれませんね(レアなケースですが)。
さて、次はもっとマジメに独特な情報で比較しましょう。
メモリ消費量で比較してみる
memcachedではサーバインスタンスがどれだけのデータ(総バイト数)をキャッシュしているかをstatsコマンドで容易に取得する事が可能です。同様に得られる情報は制限されるものの、Memcache APIでも同じ事が可能です:
from google.appengine.api import memcache stats = memcache.get_stats() if stats: print stats['bytes']
ここでトリビアですが、memcachedから取得できるキャッシュサイズは純粋に全てのkeyとvalueを合計した値ではなく、各レコードのオーバヘッド(item構造体のサイズ)を加算した値です。
この値を実際にGoogle上にデプロイしてあるアプリが返す結果と比較してみました:
なんとGAEは一切オーバヘッドを報告しません。これでMemcache APIのバックエンドに様々な可能性が広がりましたね。例えばネットワーク越しに分散されているGoogle Sparse Hashかもしれないし、アプリケーションのキャッシュマネージメントの事情でstats情報を独立した仕組みで保持しているのかもしれません。次のセクションで分散やマネージメントに関する推理を紹介します。
運用を考えてみる
「特定のアプリケーションに関するキャッシュ情報の取得」は言葉にすると簡単に聞こえるものの、実際は簡単ではありません。まず考えなくてはならない事は、アプリケーションがどの様にキャッシュスペースを与えられているのかという点です。例えばアプリケーションに対して必要に応じた数の専用インスタンスを用意するか、「専用」という概念を捨て、Keyに対しApplication Identifierをappend/prependして、他アプリケーションとキャッシュプールを共有するといったモデルです。どちらのモデルを採用するにしても、キャッシュのstatsという概念はインスタンス毎に存在するものなので、「このアプリケーションがxxx」という情報の管理と保持には独立した仕組みが必要と考えられます。つまりアプリケーション毎にindexが必要だという事です、例えばInverted Indexあたりのデータストラクチャ(appid -> stats_postings)で保持するなど。
上記の様な工夫を行わなければ、statsリクエストに対して毎回、ルックアップと演算がオンザフライで発生する事になり、低効率なシステムとなります。したがって下の層でmemcachedを使っていたとしても、統計管理が独立していれば、オーバヘッド抜きの純粋な値が保持される可能性もあります。GAEが保持と管理が楽な情報だけをstatsインターフェイスで提供している説明にもなります。
- hits
- misses
- byte_hits
- items
- bytes
- oldest_item_age
さいごに
Google App Engineで遊んでいて感動した事は、ドキュメンテーション(英語のやつ)がとてつもなく解り易い事です。あまり賢くなくて挫折屋な私でも、すぐに自立して簡単なコードが書けるレベルまでいけたほどです。プログラミング言語面では現状、Pythonしかサポートされていませんが、もし今後いろいろな言語、特にRubyあたり(私は書けませんが)が対応されたら実に凄いプロダクトになるんじゃないかな〜、と思いました。Perl対応も完了したらかなり盛り上がるかも。