こんにちは市丸です。
Zynga Japanでは単純なPrimaryKeyをキャッシュする際、symfonyのPeerをオーバーライドし自動的にキャッシュ&クリアしています。
UserPeer.php
class UserPeer extends BaseUserPeer { public static function retrieveByPK($pk, PropelPDO $con = null) { if (!$data = /* Cacheからとるよ */) { $data = parent::retrieveByPK($pk, $con); /* "User_$pk"みたいなキーでCacheするよ */ } return $data; } public static function doInsert($values, PropelPDO $con = null) { /* Cacheクリアするよ */ return parent::doInsert($values, $con); } public static function doUpdate($values, PropelPDO $con = null) { /* Cacheクリアするよ */ return parent::doUpdate($values, $con); } public static function doDelete($values, PropelPDO $con = null) { /* Cacheクリアするよ */ return parent::doDelete($values, $con); } }
ただし、ソーシャルゲームのように、2人以上のユーザーが同じテーブルを参照する場合、MemcacheとMySQLのデータ不整合に気をつけないといけません。
このように、トランザクションのcommit前に別のユーザーからアクセスされると、CacheとMySQLのデータに不整合が起こります。
正しくは、このようにcommit後にキャッシュを削除しなければなりません。
そこで、PropelPDOを改変しcommit後にキャッシュ削除しましょう。
MemPropelPDO.php
class MemPropelPDO extends PropelPDO { protected $memcacheKeys = array(); public function addMemcacheKey($key) { $this->memcacheKeys[] = $key; } public function commit(){ parent::commit(); foreach($this->memcacheKeys as $key){ // よしなにCache削除してくだされ。 } } }
database.yml
all: db1: class: sfPropelDatabase param: classname: MemPropelPDO (略)
User.php
class User extends BaseUser { public function save(PropelPDO $con = null) { if (!is_null($con)) { $con->addMemcacheKey("User_".$this->getPrimaryKey()) } return parent::save($con); } }
【仕上げ】トランザクションを使う場合はsaveメソッドにMemPropelPDOを渡します
$userModel = UserPeer::retrieveByPK(1); $userModel->setStatus(2); //statusを1->2にしてみる $con = Propel::getConnection(); // database.yml で指定したMemPropelPDO $con->beginTransaction(); try { $userModel->save($con); $con->commit(); } catch (Exception $e) { $con->rollback(); throw $e; }
まあ、忘れずにcommit後にキャッシュクリアすればいいんだけどね!(爆)