Как стать автором
Обновить

Комментарии 9

На моменте взятия блокировки в БД создаётся запись о текущей операции?

Как обрабатывается ситуация, когда ошибка произошла на этапе снятия блокировки или сохранения информации о выполненном запросе?

Спасибо за отличные вопросы.

Перед началом исполнения запроса нам нужно сохранить информацию о самом запросе и ключе. Если говорить о способе установки блокировки, то мы изначально создаем запись с блокировкой (она "логическая" и контролируется самим блоком), заполнив поле locked_at. Тут важно еще учесть, что на ключ в бд нужно повесить ограничение уникальности, чтобы вопрос о создании одинаковых ключей в один момент времени решала бд. Один из запросов успешно добавиться и продолжит обработку, а другой уже будет в ситуации, когда ключ с блокировкой уже есть и дальше логика как с блокировками.

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

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

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

Защита от дюпа для начинающих. Да еще и с блокировкой запроса. Но зато много-много умных слов.

На самом деле этот подход просто отправит систему в аут при наличии 10-20 RPS по такой схеме. Просто мечта дудосера.

А транзакции в БД уже перестали работать? Кейс на превой картинке укладывается в транзакционный запрос или это очередные микросервисы?

Эта самая идемпотентность становится довольно сложной и очень нужной при накате изменений на структуру БД. Но там uuid'ами не обойтись :-(

Хорошая статья. Более четверти века назад использовал именно такой механизм контроля передаваемых платежных документов от клиентов (помимо шифрования сообщений и ЭЦП. Использовал, в те "темные" времена, как первый уровень обеспечения ИБ, и надо сказать очень успешно. Формирование "ключа идемпотентности" зависело от ряда полей сообщения (включая номера счетов и суммы) и параметров привязки к "железу" клиента. Сам ключ был небольшой от 12 до 32 символов. Такой подход ни разу не дал сбой в отличии от "стандартных" подходов с ЭЦП. В общем ни одного "ложного" поручения клиента мы ни разу не выполнили и разбираться было очень просто, было всего несколько инцидентов на уровне внутреннего расследования, без привлечения специалистов по безопасности. А несколько месяцев назад на собеседовании меня "завалили" вопросом про идемпотентность (( https://yandex.ru/video/preview/13950763043213699807

Поделюсь опытом реализации хранилища ключей для идемпотентных операций.
Тут полный пример как хранить в БД и в памяти 1.000.000.000 (миллиард) ключей и запускать сервер за 17 секунд с полной загрузкой ключей в память.
Всё сделано на C# как готовый компонент.
https://github.com/KlestovAlexej/Wattle3.Examples?tab=readme-ov-file#пример-старта-реестра-на-1000000000-уникальных-ключей-1712-секунд

Реестр уникальных ключей реализует :

  • Хранение ключей в оперативной памяти и в БД.

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

  • Поиск ключа или данных по ключу происходит только в оперативной памяти и не задействует БД.

  • Параллельная регистрация одного и того же уникального ключа не приводит к задвоениям или рассинхронизациям с БД.

  • Есть сборка мусора - устаревшие ключи и данные автоматически удаляются из оперативной памяти по критерию заданным программистом.

  • Адаптирован для применения партиционирования PostgreSQL таблицы ключей для изятия устаревших ключей из памяти и БД.

  • Хранение ключей и данных в оперативной памяти оптимизировано и не нагружает сборщик мусора.

  • Ключи и данные в оперативной памяти занимают места O(Число ключей * (размер ключа + размер данных)) и не расходуют память на служебную информацию CLR.

  • Регистрация нового ключа задействует БД только в момент подтверждения Unit of Work.

  • Регистрация нового ключа отменяется автоматически в аварийных ситуациях работы Unit of Work.

Почему добавление логики контроля в контроллер не является оптимальным решением? При хранении данных об идемпотентности в хранилище, в текущей реализации будет 2 транзакции, одна при проверке (контроле) идемпотентности в фильтре и другая на сервисном уровне в бизнес-логике. Можно ли проверку идемпотентности перенести на сервисный уровень (бизнес-логики), тогда и аннотирование того, что эндпоинт идемпотентен, не понадобится?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий