https://symfony.com/doc/current/components/lock.html Ситуёвина: имеем webhook, в который внешняя система стучит очень важными данными. Проблематика: на этапе получения данных либо создается новая корневая сущность, либо новая запись привязывается к старой сущности. И тут внешнее апи начинает "подтраивать" и создавать сразу два запроса в одну наносекунду. Движок хранения наших очень важных сущностей - асинхронный кликхауз. Что по итогу имеем: при удачном стечении обстоятельств, оба запроса поищут старую сущность, не найдут, создадут свежую. И тут появляется еще один фактор: нельзя привязать уникальный ключ. Система теоретически поддерживает несколько сущностей по одному идентификатору (так исторически сложилось). В общем, классическая TOCTOU (Time-of-Check to Time-of-Use). TOCTOU (Time-of-Check to Time-of-Use) — это уязвимость и разновидность состояния гонки, которая возникает, когда состояние ресурса успевает измениться в промежутке времени между его проверкой программой и его непосредственным использованием. Из-за параллельного выполнения процессов или потоков злоумышленник или другой поток могут вклиниться в этот микроскопический зазор времени и подменить данные (например, файл или баланс счета), в результате чего программа выполнит действие на основе уже устаревшей проверки. У нас злоумышленника нет, мы сами себе злобные редиски. В подобных состояниях гонки нужно что-то асинхронное превращать в синхронное.

  1. Кликхаузу можно сообщить, что операция должна быть синхронной. Убираем одну точку отказа.
  2. Прикрутим lock механизм. И тут все сильно зависит от внешней системы. Если мы вернем TOO MANY REQUESTS - внешняя система повторит запрос попозже? 2.1) Да, повторит: $lock = $lockFactory->createLock('pdf-creation'); if (!$lock->acquire()) { throw new TooManyRequestsHttpException(); }

// critical section of code $service->method();

$lock->release(); 2.2) Нет, не повторит. А разработчики внешнего сервиса напишут гневное письмо о недоступности вебхука. $lock = $factory->createLock('pdf-creation'); $lock->acquire(true); // Вся "магия" в true

// heavy computation $myPdf = $pdf->getOrCreatePdf();

$lock->release(); При таком подходе, мы не выходим сразу на acquire, а просто ждем, когда ресурс разблокируется. ВАЖНО: есть ttl, поэтому если мы будем свои heavy computation делать долго - лучше зарезирвировать побольше времени.

https://symfony.com/doc/current/components/lock.html
Ситуёвина: имеем webhook, в который внешняя система стучит очень важными данными | Сетка — социальная сеть от hh.ru