Lock, Cache and Two Running Instances | OTUS
Запланируйте обучение с выгодой в Otus!
-15% на все курсы до 27.11 Забрать скидку! →
Выбрать курс

Lock, Cache and Two Running Instances

В жизненном цикле многих приложений в один прекрасный момент наступает время горизонтального масштабирования. Для одного из наших сервисов такое время наступило, и мы успешно превратили один инстанс в два. Однако вместо ожидаемого роста производительности получили совсем обратный эффект: при достижении определённого уровня нагрузки скорость обработки запросов очень сильно падала. В процессе поиска причины проблемы обнаружилась интересную особенность компонента Symfony, которой я и хочу поделиться в данной статье.

В Symfony есть компонент кэширования, в котором, начиная с версии Symfony 4.2, для предотвращения Cache Stampede были добавлены внутренние блокировки ключей кэширования. Эти блокировки используют механизм файловых блокировок (flock), а их количество по умолчанию в linux небольшое, да и используются они нечасто. В результате, выбранное разработчиками компонента решение автоматически не позволяет количеству одновременно запрашиваемых ключей кэширования в разы превышать количество доступных файловых блокировок, которые, к тому же, могут использоваться и другими процессами.

В нашем случае число ключей кэширования было достаточно большим, и данный подход внутри компонента кэширования приводил к тому, что в некоторых случаях запрос ожидал 20-30 секунд освобождения блокировки только для того, чтобы проверить наличие данных в кэше перед тем, как идти за ними в базу данных. Повторные попытки получить блокировку выполняются в цикле с ожиданием, и самые неудачливые запросы получали запрашиваемую блокировку с 74-го (!!) раза.

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

В качестве вывода из такой ситуации можно порекомендовать не использовать стандартный механизм, предлагаемый компонентом кэширования, если в вашем проекте большое количество одновременно запрашиваемых ключей кэширования (100+), небольшое время вычисления данных для помещения в кэш и редкое обновление самих данных (или долгий TTL).

В конфигурации компонента кэширования отключить этот механизм нельзя, поэтому необходимо реализовать свой класс MemcachedAdapter следующим образом:

<?php

namespace App\Symfony;

use Memcached;
use Symfony\Component\Cache\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
use Symfony\Component\Cache\Traits\MemcachedTrait;

class FixedMemcachedAdapter extends AbstractAdapter
{
    use MemcachedTrait;

    protected $maxIdLength = 250;

    public function __construct(Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
    {
        $this->init($client, $namespace, $defaultLifetime, $marshaller);
        $this->setCallbackWrapper(null);
    }
}

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

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

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

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто
Черная пятница в Otus! ⚡️
Скидка 15% на все курсы до 27.11 →