2011-09-11

Google App Engine memcache cas (compare and set)

Google App Engine SDK 1.5.3 で memcache cas (Compare and Set) 操作ができるようにななりました。今更ですが。 GvR のブログでも丁寧に解説されています。

で、どんなときに使おうっかなぁと思うわけです。データが揮発してよい、クライアント間のデータ共有の遅延を遅らせたい、整合性は保つ、みたいなときに意味があるのでしょうか。クライアントが自身のID を渡してサービスを呼び出し、最後に呼び出して一定時間以内のクライアント数を数える、みたいな機能とか。

"""
- /init to initialize service
- /update?uid=XXX to tell service XXX is active
- /count to get how many clients are active within last TIME_LIMIT sec.
"""
KEY = 'users'
TIME_LIMIT = 10
MAX_RETRIES = 3
import time
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.api import memcache
from django.utils import simplejson as json
class InitalizeHandler(webapp.RedirectHandler):
def get(self):
memcache.set(KEY, "{}")
self.response.out.write("OK")
class UpdateHandler(webapp.RequestHandler):
def get(self):
uid = self.request.get('uid')
client = memcache.Client()
for i in range(MAX_RETRIES):
users = json.loads(client.gets(KEY))
users[uid] = time.time()
if client.cas(KEY, json.dumps(users)):
self.response.out.write('OK')
return
else:
self.response.out.write('FAILED')
class CountHandler(webapp.RequestHandler):
def get(self):
users = json.loads(memcache.get(KEY) or "{}")
count = len([uid for uid,timestamp in users.items()
if time.time() < timestamp + TIME_LIMIT])
self.response.out.write('count: %d' % count)
def main():
application = webapp.WSGIApplication([('/init', InitalizeHandler),
('/update', UpdateHandler),
('/count', CountHandler)],
debug=True)
util.run_wsgi_app(application)
if __name__ == '__main__':
main()
view raw cas_test.py hosted with ❤ by GitHub

と、思ったんですが、cas のリトライに失敗したときにどうしましょうっかねぇ。どっか、別のエラーの回数を incr() しといて、エラー率とアクセス数を関連付けるとかでしょうか。むずい。


2011-09-11 22:43 追記: シャーディングで、ひとつのキーに集中しないようにする、というのは大前提で。