Keep on moving

あんまりまとまってないことを書きますよ

Google App EngineでのMemchaceの排他処理を書いてみるテスト

id:kazunori_279さんのMemcacheでスピンロックを実装してTask Queue処理結果を集約してみるテストに感動したのでpythonで書いてみました。
pythonの練習用にデコレーターにしてみました。*1
ただこれで上の記事の内容をちゃんと実装できているかはちょっと自信がありません。。

#! /usr/bin/python
# -*- encoding: utf-8 -*-

from google.appengine.api import memcache
from functools import wraps
import time

# 例外
class MemcacheSpinLockError(Exception):
  def __init__(self,value):
    self.value = value
  def __str__(self):
    return self.value

# MemcacheのLock取得
def _acquireLock(key):
  data = memcache.incr(key)
  if data is None:
    memcache.add(key,0)
    return False
  else:
    if data == 1:
      return True
    else:
      memcache.decr(key)
      return False

# MemcacheのLock解除
def _releaseLock(key):
  memcache.decr(key,1)

# MemcacheのLock取得し、関数実行後、Lock解除するデコレータ
def runSynchronized(key):
  def inner(func):
    def wrapper(*args, **kwargs):
      n = 0
      # 10回Lockを取得しようとして失敗した場合は例外を返す
      while n < 10:
        locked = _acquireLock(key)
        if locked == True:
          break
        else:
          if n <9:
            n += 1
            time.sleep(0.5)
          else:
            raise MemcacheSpinLockError(key)

      try:
        func(*args,**kwargs)
      finally:
        _releaseLock(key)

    return wraps(func)(wrapper)
  return inner

使い方

from memcachespinlock import runSynchronized,MemcacheSpinLockError

KEY_NAME = "count_up"

@runSynchronized("count_up_lock")
def count_up():
    data = memcache.get(KEY_NAME)
    if data is not None:
      data += 1
      memcache.set(KEY_NAME,data)
    else:
      memcache.add(KEY_NAME,1)

ソースは以下にあります。
http://bitbucket.org/ehren/gae_memcachespinlock/

*1:モテAPIで使う予定です。