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 запускается не чаще, чем в исходной постановке задачи, а кеш стал надежнее и эффективнее.

Комментарии

  • config 20 сентября 2008

    ты это еще не реализовал, а в блоге написал :))

  • config 20 сентября 2008

    еще надо учесть момент когда php скрипт запустился потому кеша нет, но другой php скрипт уже занялся генерацией нового кеша(тот который самым первым не нашел файла в кеше). можно по идее из посмотреть нет ли файла (переименнованного из .old) и отдать его пользователю. иначе php опять полезет в базу или еще куда-то там, будет опять писать кеш и все такое, что совершенно ни к чему

  • coldFlame 20 сентября 2008

    Ага, точно, не учел. Интересная проблема.

  • www.katkovonline.com 22 сентября 2008

    эта проблема называется dog pile effect решений масса.

  • standov 2 ноября 2008

    Да, я такую штуку реализовал в одном из проектов где нужно было «на вот этом железе за месяц» сделать «2K запросов в сек»….в общем, решилось все очень просто, в MVC вреймворке на єтапе генерации URL, проверялся факт наличия сстраницы на диске и генерилась линка на него, иначе на фронт-контролер

Оставить комментарий

  • (или OpenID)
  •