Read-From, Write-Through: улучшаем кеш страниц 19 сентября 08
Предыстория
Есть кеш страниц, организованный по принципу 404-й. То есть – если страница лежит в кеше, она отдается сервером без участия PHP; если ее там нет – по обработчику 404-й ошибки вызывается MainController сайта, который генерирует страницу, кладет ее в кеш и отдает клиенту.
Кроме того, страницы экспайрятся по каким-то событиям. Неважно, каким. Важно, что экспайрятся, и время от времени их нужно повторно генерировать. Это решается удалением из кеша заэкспайренной страницы, после чего при запросе страницы снова вызывается PHP.
Проблема
Если в то время, пока страница генерируется скриптом, запросить ее еще раз, будет запущен еще один экземпляр скрипта и он тоже займется страницей. А при следующем запросе – еще один. И еще. В общем, race condition и в целом неправильная ситуация.
Задача
Устранить race condition с минимальным использованием PHP (предположим, что запуск и инициализация фреймворка на PHP – дорогостоящая операция).
Решение
Суть в том, что сервер каким-то образом должен понять, что страница уже генерируется, и новый скрипт не запускать. Но каким? Углубляться в настройки nginx, который стоит на продакшн, желания нет.
Первая попытка — при запуске PHP ставить флажок «занимаюсь этой страницей», а при следующем запуске – его проверять, и если флажок стоит, то просто подождать. Плохо. Это избавляет нас от лишнего прогона скрипта, но поток PHP все равно занят.
Вторая попытка — при запуске PHP немедленно записывать в кеш пустую страницу. Плохо. Кто-то ее увидит и расстроится. Так что пустая страница проблемы не решает.
Третья попытка (и последняя)
Экспайренные страницы не удаляются из кеша, а переименовываются – например, к ним приписывается суффикс «.old». Таким образом, при обращении к странице будет вызван PHP. А PHP, увидев, что есть старый (хоть какой-то) экземпляр этой страницы, немедленно переименует ее обратно – чтобы работал кеш – а уже после этого сгенерирует новую страницу и положит ее на место устаревшей.
Бинго! PHP запускается не чаще, чем в исходной постановке задачи, а кеш стал надежнее и эффективнее.

Подписаться на RSS

Комментарии
ты это еще не реализовал, а в блоге написал :))
еще надо учесть момент когда php скрипт запустился потому кеша нет, но другой php скрипт уже занялся генерацией нового кеша(тот который самым первым не нашел файла в кеше). можно по идее из посмотреть нет ли файла (переименнованного из .old) и отдать его пользователю. иначе php опять полезет в базу или еще куда-то там, будет опять писать кеш и все такое, что совершенно ни к чему
Ага, точно, не учел. Интересная проблема.
эта проблема называется dog pile effect решений масса.
Да, я такую штуку реализовал в одном из проектов где нужно было «на вот этом железе за месяц» сделать «2K запросов в сек»….в общем, решилось все очень просто, в MVC вреймворке на єтапе генерации URL, проверялся факт наличия сстраницы на диске и генерилась линка на него, иначе на фронт-контролер