hnwの日記

PHPでAPC/APCuのCAS機能を使うときの注意点

PHPAPC/APCuエクステンションをKVSとして使うときの話題です。


APC/APCuにはCAS(楽観ロックに基づくアトミックな値の書き換え)の関数として次の3つが用意されています。

  • apc_cas ― 古い値を新しい値に更新する
  • apc_inc ― 保存した数値を増やす
  • apc_dec ― 保存した数値を減らす


memcachedにも同様の機能があるので一見すると悩まず使えそうですが、実は罠があります。APC/APCu自体は全ての型の変数値をストアできるのですが、これらの関数については整数しか扱えないため、この機能を使う場合に限っては最初にストアする値を整数型にしておく必要があります。整数型以外の値がストアされていた場合、型エラーとして扱われて失敗します。


これの何が罠かというと、これらの関数がfalseを返したときに、ロック競合だったのか型エラーだったのかの区別が付かないという点です。ロック競合は一時的なエラーなのでリトライする必要がありますが、型エラーは永続的なエラーですのでリトライしても無意味です。エラーへの対応が異なるにもかかわらず、その区別がつかないインターフェースなので、かなり使いにくいと感じます。

楽観ロックと悲観ロック

用語の説明もしておきます。ロック方式の分類として、楽観ロックと悲観ロックという2種類があります。悲観ロックは、要するにRDBで使われるロック方式です。トランザクション処理を行う場合に明示的・暗黙的にロックを取得し、同時に同じロックを取ろうとするとブロックされて待たされます。


一方、楽観ロックは概念的にロックを取得しない*1ような方式です。その代わりに、全員が「以前自分が値を取得したときから他の人が値を更新していなければ、この値に更新したい」というリクエストを投げます。他人と同時にこの処理を行うと、一方の処理が成功し、他方はノンブロッキングで失敗が返ってきます。たとえばmemcachedなどではCASと呼ばれる楽観ロックに基づくアトミックなデータ更新機能を提供しています。

*1:実装上はごく短い区間のロックを取ります

続きを読む