はじめに
古いPHPのコードのテストでmemcacheのflushまわりで変な挙動をしているので調べていたのですが、PHPのMemcache::flushのマニュアルのUser Contributed Notesに気になる記述を見つけました。
Please note that after flushing, you have to wait a certain amount of time (in my case < 1s) to be able to write to Memcached again. If you don't, Memcached::set() will return 1, although your data is in fact not saved.
flushしたあと1秒待たないと、set
が成功するけど実際にはデータが保存されないと言っているのですが、ソースを見たら本当にそうだったので仕組みを説明します。
仕組み
memcached の expire の仕組みはアイテムごとに期限を持っていて、データを取り出すときに期限が現在時刻より過去なら期限切れ判定してデータを開放する、という感じだというのはうっすらと知っていたのですが、flushのために追加で以下のようなことをしています。
-
flushを実行すると、この時刻までのタイムスタンプを持っているアイテムはflushされたと判定するよという
settings.oldest_live
に現在の時刻(+与えられていればオフセット)を設定します。 -
itemがflushされたか判定する関数
item_is_flushed()
ではitemのタイムスタンプ <= settings.oldest_live ならflushされていると判定します
int item_is_flushed(item *it) {
rel_time_t oldest_live = settings.oldest_live;
uint64_t cas = ITEM_get_cas(it);
uint64_t oldest_cas = settings.oldest_cas;
if (oldest_live == 0 || oldest_live > current_time)
return 0;
if ((it->time <= oldest_live)
|| (oldest_cas != 0 && cas != 0 && cas < oldest_cas)) {
return 1;
}
return 0;
}
時刻の精度は秒なので、flushを実行したのと同じ秒の間は新しいアイテムを追加してもタイムスタンプが同じになるのでflushされたアイテムであるという判定になってしまいます。これがデータが保存されない仕組みです。(正確には保存されるけどflushされていると扱われるので取り出すことができません)