Agregator
@Agregator
Backend Developer (Yii2)

Кеш Yii2 не работает на высоких нагрузках, что делать?

У меня есть проксирующее апи, которое берет здоровенный json из одного места, кладет его в кеш, и отдает уже оттуда. При одиночных вызовах все как бы ок, в кеш кладется, первый запрос секунды 3, а потом с десяток миллисекунд. Но на стресс тесте запрос пролетает кеш, и валит на то апи на которое не нужно. Что делать то?
Код очень простой, ошибиться тут особо негде.

public function hallSeats($showtimeId){

        if ( !$result = \Yii::$app->cache->get('theater_halls_seats' . $showtimeId) ) {

            $result = $this->get('some/api/halls', [
                'showtimeId' => $showtimeId,
            ]);

            \Yii::$app->cache->add('theater_halls_seats' . $showtimeId , $result, 20);
        }

        return $result;
    }
  • Вопрос задан
  • 866 просмотров
Пригласить эксперта
Ответы на вопрос 2
@BorisKorobkov Куратор тега MySQL
Web developer
1. Ваш кэш протухает за 20 секунд.

2. Возможно, для кэша выделено мало памяти, а json большой. Новые записи просто вытесняют старых из кэша.

Update:
3. Если следующий запрос приходит, когда первый еще не записался в кэш, тогда
3.1. если в кэше ничего нет, то сначала записать туда true. Потом сделать запрос, удалить из кэша (иначе результат не запишется!) и результат записать в кэш.
3.2. если в кэше json - отдать его
3.3. если в кэше true - sleep, проверить кэш еще раз. Но не более N раз. Если так и не дождался - идти дальше (сделать запрос и потом его записать в кэш).
Ответ написан
@yiiworld
Тоже столкнулся с проблемой параллельных запросов и на формирование данных при пустом кэше.
Рабочее решение: (только свои константы подставьте)
static function getData($waiting_deep = 0)
    {
        //достаем $prts из кэша
        if (!(Yii::$app->cache->exists(self::CACHE_KEY__PRTS) AND is_array($prts = Yii::$app->cache->get(self::CACHE_KEY__PRTS)) AND isset($prts['version']) AND isset($prts['data']) AND is_array($prts['data']))) {
            // данных в кэше нет
            // выполняем процедуру подготовки данных с блокировкой для других процессов - чтобы другой процесс не начал делать тоже самое. В кэш поместить флаг блокировки Yii::$app->cache->set(CACHE_KEY__PRTS_FLAG_LOCK), а после подготовки данных и помещения их в кэш снять этот флаг блокировки. Если другой параллельный запрос не находит данных в кэше и при этом выставлен флаг блокировки Yii::$app->cache->exists(CACHE_KEY__PRTS_FLAG_LOCK), то он будет ожидать несколько(столькол сколько задана максимальная глубина ожидания) раз. Ограничиваем число ожиданий и интервал этих ожиданий.
            if (Yii::$app->cache->exists(self::CACHE_KEY__PRTS_FLAG_LOCK) AND Yii::$app->cache->get(self::CACHE_KEY__PRTS_FLAG_LOCK)) {
                if ($waiting_deep <= self::PRTS_FIND__WAITING_DEEP_MAX) {
                    usleep(self::PRTS_FIND__INTERVAL_WAITING_NEXT_CHECK_MICROSECONDS);
                    $prts = self::findPromoactionsRuleToShow($waiting_deep++);
                } else {
                    return false;
                }
            } else {
                Yii::$app->cache->set(self::CACHE_KEY__PRTS_FLAG_LOCK, true, 10); //сбросить флаг блокировки через 10 секунд в любом случае. Необходимо на случай ошибок при подготовки правил и следовательно невозможности в штатном режиме сбросить флаг блокировки.
                if (!($prts = self::prepareData())!==false){
					// кладем данные в кэш
					Yii::$app->cache->set(self::DATA_PRTS, $prts);
				}
				// Сбрасываем флаг блокировки
					// Yii::$app->cache->set(self::CACHE_KEY__PRTS_FLAG_LOCK, false);
				Yii::$app->cache->delete(self::CACHE_KEY__PRTS_FLAG_LOCK);
            }
        }
        return true;
    }
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы