Posted at

GAE SE/pyのdatastore NDBが保存するmemcacheの場所

More than 1 year has passed since last update.

Google AppEngine Standard Environmentのpythonにはdatastoreのマッパーであるndbがとても便利で使いやすいです。

このライブラリは内部でキャッシングを行なってくれるため、平均取得時間の遅延を下げてくれます。

内部ではIn-Contextキャッシュとmemcacheを使ったキャッシュを併用しています。

NDB Caching


datastore更新後もmemcacheにキャッシュが残る

memcacheを使わないコンテキストでの更新やGCEや外部からの更新時に、memcacheの更新を怠ってしまうとキャッシュの削除が行われずに、いつまでも更新後のデータが取得できなくなるケースがあります。


AppEngineからキャッシュをdisableにして保存

AppEngineから保存する時に、contextのポリシーでmemcacheへの同期をoffにして保存等してしまうと、キャッシュを利用している別のcontextと不整合が起きてしまいます。


appengine_view.py

from google.appengine.ext import ndb

class Item(ndb.Model):
name = ndb.StringProperty()

item = Item.get_by_id('item1')
item.name = 'car'
item.put() # 通常はmemcacheにも保存されます

# キャッシュを使いたくないケースで、memcacheをcontext内でオフにしてしまう
ctx = ndb.get_context()
ctx.set_memcache_policy(False)

item = Item.get_by_id('item1')
item.name = 'train'
item.put() # キャッシュポリシーがOFFだとmemcacheに保存されません


実際に、memcacheをオフにするケースはtaskqueue等で非同期で更新していたりするケースが多いと思いますが、時々遭遇してしまいます。


memcacheにアクセスできない環境から保存

GCEや外部など、memcacheにアクセスできない環境からdatastoreの更新や保存をした場合に、memcacheに保存されずに不整合が起きます。

google-cloud-pythonライブラリなどを使うことが多いと思いますが、その時に遭遇することが多いです。


gce.py

# google-cloud-pythonを使った場合

from google.cloud import datastore

client = datastore.Client('my-project')
item = client.get(client.key('Item', 'item1'))
item['name'] = 'airplane'
client.put(item) # memcacheには保存されません。


(GCE上などからmemcacheの読み書きをする方法を知らないので、知っていたら教えてください。)


コンソール上で編集

コンソール上で編集する機能がありますが、これで編集してしまってもmemcacheには保存されない現象が起きてしまいます。

静的なconfig等をdatastoreで管理していて、それを更新した時に反映されない、といったケースに遭遇しました。


解決策


memcache全体をクリア(flash)すれば解決

memcache上に保存されているものを全て削除してしまえば、このような問題は解決します。

Memcacheのページからもフラッシュできます。

しかし、appengineからのdatastoreアクセスのパフォーマンスがその時に低下してしまうと思われるので、あまりやりたくないケースも多いですよね。


memcacheの該当データだけ削除

該当のデータだけmemcache上から削除すれば、そのデータだけが再取得されるので、全体のパフォーマンスへの影響を減らせるかと思います。

保存しているkeyを知って入れば良いのですが、ndb内部でmemcacheへ保存するときのkeyを決めています。

具体的にはprefix + entityのkeyのハッシュです。

prefixはndb.Context._memcache_prefixに定義されていますが、appengine sdk 1.9.55ではNDB9:となっていました。

なので以下のようにすれば削除できそうです。

item = Item.get_by_id('item1')

memcache.delete('{}{}'.format(ndb.Context._memcache_prefix, item.key.urlsafe()))

ndbライブラリ自体はappengineのpython sdkに含まれていて、github等で公開されていない?のでソースを見てみる必要があって少しわかりづらかったです。

中身もアップデートはあまりされていないみたいですし、先行きは少し不安ですね。