読者です 読者をやめる 読者になる 読者になる

couchbase-python-clientをmemcacheクライアントとして使う

Python Memcache

couchbase-python-clientはCouchbase社が開発しているCouchbaseのクライアント・ライブラリ。「Couchbaseとは何ぞ?」という方は、CouchDBのストレージをMemcache/Membaseに置き換えたKVSだと考えて頂きたい。

PythonのMemcacheクライアントたち

PythonのMemcacheクライアントだと、Pure Pythonなライブラリであるpython-memcachedと、libmemcachedバインディングのpylibmcが標準的なライブラリだと思う。

が、両方とも標準的なニーズはほぼ100%満たせるとはいえ、分散アルゴリズムやCASの振る舞い、データのシリアライズや圧縮の仕方を少しカスタマイズしたいとなると、拡張性に不満を覚えることがあった。

個人的に欲していたのが、最低限のことだけが出来て、しかもバイナリプロトコルが使える、非常に薄いMemcachedクライアントで、bmemcachedというやつは結構よさそうと思って注目していた。

しかしながら、couchbase-python-clientがすごい勢いでリファクタされつつあるのを見て、もはやcouchbase-python-clientが標準的なMemcachedクライアントの座を獲得するのではないかという感すらある。

couchbaseをMemcacheクライアントとして使う

couchbase-python-clientをインストールするには、PyPIから

$ pip install couchbase

で最新版(現時点では0.8.0)をインストールするか、gitでgithubのmasterをインストールする。

$ pip install git+git://github.com/couchbase/couchbase-python-client.git

いろいろ説明をすっ飛ばして簡単にいうと、Couchbaseは永続化機能を備えたMemcacheを複数束ねた分散DBなので、各ノードはMemcacheプロトコルを話すKVSになっている。故に、このcouchbase-python-clientクライアントライブラリはMemcacheクライアントも実装している。

非常に乱暴な方法だが、下記のようなコードで、couchbase-python-clientをMemcacheクライアントとして使えてしまう。

from couchbase.memcachedclient import MemcachedClient as Memcache

host = '127.0.0.1'
port = 11211

client = Memcache(host=host, port=port)
opaque, cas, data = client.set("KEY", exp=600, flags=0, val="value")
print((opaque, cas, data))

opaque, cas, data = client.get("KEY")
print((opaque, cas, data))

getlを使う

全然知られていない特徴だと思うが、Membase/Couchbaseが使っているmemcache拡張プロトコルにはgetlというコマンドがある。

getlとは、ロック期間を指定してキーに対応する値を取得し、そのロック期間が過ぎるまでsetやcasによる更新を失敗させることができる、という機能。

公式ドキュメントだと、以下のあたりが詳しい。

「こんなんで排他制御が上手く行くのか?」と思われるかもしれないが、案外この機能を使うと実装できてしまう。少なくとも、Zyngaで上手く行っている程度には上手くいく。

以下はgetl拡張コマンドを試してみる例。

# -*- coding: utf-8 -*-
from couchbase.vbucketawareclient import VBucketAwareClient as Memcache

host = '127.0.0.1'
port = 11211

client = Memcache(host=host, port=port)

opaque, cas, data = client.set("KEY", exp=600, flags=0, val="OK")
print((opaque, cas, data))

opaque, cas, data = client.getl("KEY", exp=5)
print((opaque, cas, data))

# ロック期間が過ぎていないのでこのsetは失敗する
opaque, cas, data = client.set("KEY", exp=600, flags=0, val="ERROR")
print((opaque, cas, data))

プロトコルを試してみるという例で、本来ならば、VBucketAwareClientを直接使ってデータをロックしたり、取得したりするのはCouchbase的にはNGのはずだが、あのZとかいう会社は・・・。

unlockを使う

unlコマンドを使うとgetlで取得したロックを主導でアンロックできるようだが、なぜかJavaクライアントにしか実装されていないっぽい。

未実装なのはそれなりの理由があると思うが、unlの挙動を試すだけならば、以下の様なコードで実現できる。

# -*- coding: utf-8 -*-
import time
from couchbase.vbucketawareclient import VBucketAwareClient

class Memcache(VBucketAwareClient):
    def unlock(self, key, cas, vbucket=-1):
        self._set_vbucket_id(key, vbucket)
        return self._doCmd(0x95, key, '', cas=cas)

host = '127.0.0.1'
port = 11211

client = Memcache(host=host, port=port)

opaque, cas, data = client.set("KEY2", exp=600, flags=0, val="VALUE")
print((opaque, cas, data))

# 15秒ロック期間を設けて"KEY2"をget
opaque, cas, data = client.getl("KEY2", exp=15)
print((opaque, cas, data))

# 手動でunlock
opaque, cas, data = client.unlock("KEY2", cas)
print((opaque, cas, data))

# "KEY2"に再度set. アンロックしていない場合はここでエラーになる
opaque, cas, data = client.set("KEY2", exp=600, flags=0, val="VALUE2")
print((opaque, cas, data))