Стандартная ситуация — есть функция, которая при открытии страницы берёт данные из кэша или генерит их если кэш пуст:
function myfunction() {
if ($cache = cache_get('mydata')) {
$data = $cache->data;
}
else {
$data = ...;
cache_set('mydata', $data);
}
return $data;
}
Если процесс генерации данных будет достаточно длинным, то в определённый момент может возникнуть так называемое состояние гонки (race condition) — это когда множество параллельных потоков будут пытаться обновить пустой или невалидный кэш, что может положить ваш сервер.
В друпале для решения подобных проблем существует механизм блокировок, состоящий из четырёх основных функций:
- lock_acquire($name, $timeout = 30.0) — устанавливает блокировку
$name
и возвращаетFALSE
если блокировка уже установлена. - lock_may_be_available($name) — возвращает
TRUE
если блокировку$name
можно установить. - lock_wait($name, $delay = 30) — дожидается окончания блокировки
$name
. - lock_release($name) — удаляет блокировку
$name
.
Пример кода с использованием Locking API:
function myfunction() {
if ($cache = cache_get('mydata')) {
$data = $cache->data;
}
elseif (lock_acquire('mydata_generated')) {
$data = ...;
cache_set('mydata', $data);
lock_release('mydata_generated');
}
else {
lock_wait('mydata_generated');
return myfunction();
}
return $data;
}
Пример кода, когда данные в кэше нужно хранить определённое время (например 1 час):
function myfunction() {
if ($cache = cache_get('mydata') && (REQUEST_TIME - $cache->created < 60*60 || !lock_may_be_available('mydata_generated'))) {
$data = $cache->data;
}
elseif (lock_acquire('mydata_generated')) {
$data = ...;
cache_set('mydata', $data);
lock_release('mydata_generated');
}
else {
lock_wait('mydata_generated');
return myfunction();
}
return $data;
}
Тут интересный момент — если есть просроченный кэш и стоит блокировка, то ф-я вернёт данные из кэша без ожидания завершения блокировки. lock_wait()
же будет вызываться только при отсутствии данных в кэше.
Комментарии
А я всё сижу и жду, когда кто-нибудь реализует http://alekseykorzun.com/post/49520668105/how-to-gracefully-handle-cach… в друпале. Кратко суть: начать сбрасывать кеш чуть раньше (на ttl), а пока сбрасываешь, старый кеш продлить на ttl.
kalabro, блокировка предпочтительней предложенного в той статье кривого велосипеда.
@unic, вот в Vimeo дураки-то. https://github.com/AlekseyKorzun/memcached-wrapper-php#php-5-wrapper-fo…
Во втором примере кода строчку
lock_wait('mydata');
надо заменить на
lock_wait('mydata_generated');
исправил, спасибо
Добавить комментарий