[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Memcached++
kirik
Приветствую усех!

Давненько я не появлялся тут, соскучился ужо smile.gif
Вот накарякал тут класс для более удобной работы с мемкэшем. Переписывал только то что мне пригодилось.
(да, да.. про класс DklabCache я знаю)

Тут код найти можно
class MemcachedPP extends Memcached {
/**
* KEY_*
* название ключей в массиве данных
* (выбраны специально короткие, чтобы меньще места занимать)
*/

const KEY_EXPIRE = 'e';
const KEY_VALUE = 'v';
const KEY_TAGS = 't';
/**
* LOCK_PREFIX
* префикс ключа лока
*/

const LOCK_PREFIX = 'LOCK_';
/**
* TAG_PREFIX
* префикс ключа тэга
*/

const TAG_PREFIX = 'TAG_';
/**
* REBUILD_TIME
* дефолтное время, затрачиваемое на перестройку кэша
* (ни одна перестройка кэша не должна превышать это значение по длительности)
*/

const REBUILD_TIME = 10;

/**
* Функция save()
* Служит для сохранения некоторого значения в кэше
* Отличия от оригинальной set():
* - поддержка тэгов [
http://www.smira.ru/2008/10/29/web-caching-memcached-5/]
* - нет ограничения по времени жизни [http://www.php.net/manual/en/memcached.expiration.php]
*
*
@param string $key [ключ кэша]
*
@param mixed $value [значение кэша]
*
@param int $expiration [время жизни в секундах]
*
@param array $tags [массив тэгов]
*
@return bool
*/

public function save($key, $value, $tags = array(), $expiration = 0) {
$tagsAndVersions = array();
if($expiration > 0) {
$expiration = time() + $expiration;
}
if(!empty($tags)) {
foreach($tags as $tagName) {
$tagName = self::TAG_PREFIX . $tagName;
$tagVersion = microtime();
if(parent::add($tagName, $tagVersion, 0) === false) {
$tagVersion = parent::get($tagName);
}
$tagsAndVersions[$tagName] = $tagVersion;
}
}

$data = array(
self::KEY_EXPIRE => $expiration,
self::KEY_TAGS => $tagsAndVersions,
self::KEY_VALUE => $value
);
return parent::set($key, $data, 0);
}

/**
* Функция load()
* Служит для изъятия некого значения из кэша, если значение есть, и перестраивает кэш если его нет
* Отличия от оригинальной get():
* - перестраивает кэш правильно (с локами)
* - есть возможность указать время перестройки кэша
* - через функцию-перестройщик есть возможность также указывать тэги и время жизни для ключа (как и при set())
*
*
@param string $key [ключ кэша]
*
@param callback $rebuilder [функция которая будет перестраивать кэш]
*
@param int $rebuildTime [приблизительное время, требуемое для перестройки кэша; если не установлено, то используется дефолтное REBUILD_TIME]
*
@return mixed [вернет false если нет старого кэша и невозможно перестроить кэш; и данные в другом случае]
*/

public function load($key, $rebuilder = null, $rebuildTime = 0) {
$data = array();
// Если у нас в хранилище есть кэш
if(($data = parent::get($key))) {
// и его срок годности неограничен или еще не истёк
if($data[self::KEY_EXPIRE] == 0 || $data[self::KEY_EXPIRE] > time()) {
// проверяем валидность его тэгов
$freshTags = parent::getMulti(array_keys($data[self::KEY_TAGS]));
// если отличий в версиях тэгов нет - возвращаем значение
$diff = array_diff_assoc($data[self::KEY_TAGS], $freshTags);
if(empty($diff)) {
return $data[self::KEY_VALUE];
}
}
}

if(is_null($rebuilder)) {
// если кэш в хранилище отсуствует и нет функции
// восстановления кэша - вернём false

return false;
}

// во всех остальных случаях перестраиваем кэш
return $this->rebuildCache($key, $rebuilder, $rebuildTime, $data);
}

/**
* Функция rebuildCache()
* Нужна для корректной перестройки кэша
* Функция $rebuilder должна принимать по ссылке значения тэгов, времени жизни и значения.
* Она может изменять эти переменные, при этом если она возвращает true то данные $value
* запишутся в кэш, если false то нет.
* function rebuilder(&$value, &$expiration, &$tags) {
* $expiration = 10;
* $tags = array('tag1', 'tag2');
* // ...
* $value = 'value';
* // ...
* return true;
* }
*
*
@param string $key [ключ кэша]
*
@param callback $rebuilder [функциф перестройки кэша]
*
@param int $rebuildTime [примерное максимальное время перестройки]
*
@param array $data [старое значение кэша]
*
@return mixed [дфнные или false при ошибке]
*/

private function rebuildCache($key, $rebuilder, $rebuildTime, $data) {
$lockName = self::LOCK_PREFIX . $key;
if($rebuildTime == 0) {
$rebuildTime = self::REBUILD_TIME;
}
// получили блокировку - понеслась перестройка
if(parent::add($lockName, 1, $rebuildTime)) {
$value = '';
$tags = array();
$expiration = 0;
if(call_user_func($rebuilder, &$value, &$expiration, &$tags)) {
$this->save($key, $value, $tags, $expiration);
parent::delete($lockName);
return $value;
}
}
elseif(!empty($data)) {
// блокировку не дали, но есть старое значение - его и возвращаем
return $data[self::KEY_VALUE];
} else {
// блокировку не дали.. и старого значеия тоже.. ждем пока кто-нить перестроит кэш
$i = 0;
while(parent::get($lockName)) {
if($i++ > $rebuildTime) {
break;
}
sleep(1);
}
if(!parent::get($lockName)) {
return $this->load($key, $rebuilder, $rebuildTime);
}
}

return false;
}

/**
* Функция clean()
* Тоже самое что и стандартная delete(), но если за $keys передать массив, то будут очищаться тэги
*
@param mixed $keys [если string то ключ значения, если array, то тэги]
*
@param int $time [время через которое нужно удалить]
*/

public function clean($keys, $time = 0) {
if(is_array($keys)) {
foreach($keys as $tagName) {
parent::delete(self::TAG_PREFIX . $tagName, $time);
}
}
else {
parent::delete($keys, $time);
}
}
}




Здоровая критика приветствуется! smile.gif

UPD1
Немножко изменился код. На ночь глядя вчера написал так, что при сохранении нового ключа для всех тэгов изменялись версии (что влекло за собой бесконечную перестройку всех кэшей). Теперь все ок.

UPD2
Из-за strict-ошибки в функции set() решил переименовать ее в save(), а так же get() в load() во избежании недоразумений



Спустя 1 месяц, 9 дней, 21 час, 9 секунд (25.03.2010 - 06:42) kirik написал(а):
Я дурак и никто мне об этом не сказал.. huh.gif
Писал эти костыли с локами, хотя есть вполне стандартная операция для расширения Memcached - cas().
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.