S
S
Stanislav2017-12-07 12:53:51
MySQL
Stanislav, 2017-12-07 12:53:51

Yii2 cache does not work on high loads, what should I do?

I have a proxy API that takes a hefty json from one place, puts it in the cache, and gives it back from there. With single calls, everything seems to be ok, it is put into the cache, the first request is 3 seconds, and then about a dozen milliseconds. But on a stress test, the request flies through the cache, and brings down the api for which it is not needed. What to do then?
The code is very simple, there is nowhere to make a mistake here.

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;
    }

Answer the question

In order to leave comments, you need to log in

2 answer(s)
B
Boris Korobkov, 2017-12-07
@BorisKorobkov

1. Your cache goes bad in 20 seconds.
2. It is possible that little memory is allocated for the cache, and the json is large. New entries simply force old ones out of the cache.
Update:
3. If the next request arrives when the first one has not yet been cached, then
3.1. if there is nothing in the cache, then first write true there. Then make a request, remove it from the cache (otherwise the result will not be written!) And write the result to the cache.
3.2. if json is in the cache - return it
3.3. if the cache is true - sleep, check the cache again. But no more than N times. If you still haven't waited, go further (make a request and then write it to the cache).

Y
yiiworld, 2017-12-07
@yiiworld

I also encountered the problem of parallel requests for data generation with an empty cache.
Working solution: (just substitute your constants)

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;
    }

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question