Кеширование. Проблема одновременного обновление кеша

Предположим есть некоторый блок, который обновляется тяжелым запросом. Предположим этот запрос выполняется 5 секунд.

Есть посещаемый сайт, на котором этот блок показывается например 10 пользователям в секунду.

Допустим мы кешируем этот блок на некоторое время. Пусть это будет 10 минут.

Итак что будет когда эти 10 минут заканчиваются. Блок запрашивает пользователь №1. В кеше его нету - система пытается обновить кеш путем выполнения "тяжелого запроса". Запрос как выше упоминалось выполняется 5 секунд. За это время блок необходимо будет показать еще 49 пользователям и для этого снова будет запущено выполнение "тяжелого запроса"… Системе от этого станет плохо… и тяжелый запрос будет выполнятся еще медленее.

Все цифры из головы, просто для объяснения проблемы.

Поделитесь опытом, как это красиво можно решить.

кэшировать не блок, а результат запроса, без экспирации, записывать в тотже мемкеш дату обновления определеного блока, обновлять через хх минут кеш результата запроса.

Если это обновление делать общим кодом - то возникнет аналогичная проблема. При выдаче данных всем пользователям во время обновления - каждый будет пытаться обновить кеш данных.

Если подумать дальше, то ваша подсказка позволяет придумать вот такой ход.

Первый процесс, который посчитает, что "пора обновить кеш" в самом начале обновляет дату обновления блока. Это позволит другим процессам использовать старые данные, а у первого будет время неспеша получить данные и обновить кеш блока.

TrojaNFlash, спасибо за подсказку. Может еще у кого-то идеи будут?

Делайте обновление по cron’у раз в 10 минут :)

В свете вышеизложенных предложений, родилось более-менее красивое решение. Может кому-то пригодится.

Буду благодарен за критику и советы.




class MyCache extends CFileCache 

{

	public $expire_delta = 30; // Запас времени на обновление кеша

	public $expire_suffix = '_expire';


	protected function getValue($key)

	{

		$value = parent::getValue($key);

		if ($value) { // Если значение есть в кеше, проверяю не пора ли его обновить

			if (time() > parent::getValue($key . $this->expire_suffix)) { // Пора обновлять кеш

				parent::setValue($key . $this->expire_suffix,time()+$this->expire_delta,$this->expire_delta); 

				return null; // Делаю вид что кеш истек и его нужно обновить, но для следующих запросов в течении expire_delta секунд будет выдаваться старый кеш.

			};

		};

		return $value;

	}


	protected function setValue($key,$value,$expire)

	{

		return (parent::setValue($key,$value,$expire + $this->expire_delta) && parent::setValue($key . $this->expire_suffix,time() + $expire, $expire + $this->expire_delta));

	}

}