Кейс: дедлок в Postgres из-за разного порядка блокировок строк

Два сервиса обновляли заказ и остаток на складе в одной транзакции, но в разном порядке. Один код сначала блокировал строку заказа, потом строку товара. Другой — сначала товар, потом заказ. При параллельных запросах транзакции время от времени схлопывались в deadlock.

Postgres такие ситуации находит сам: детектор дедлоков ждёт deadlock_timeout (по умолчанию 1 секунда), потом откатывает одну из транзакций с ошибкой deadlock detected. Проблема не в базе — она честно ловит взаимную блокировку. Проблема в коде, который блокирует строки в непредсказуемом порядке.

Фикс — договориться о едином порядке блокировки во всех местах: например, всегда сначала SELECT ... FOR UPDATE по возрастанию id, независимо от бизнес-логики метода.

Спросят, почему не поставить SELECT ... FOR UPDATE NOWAIT везде, чтобы упасть сразу вместо ожидания deadlock_timeout. Это меняет модель поведения на fail-fast — код должен уметь ретраить транзакцию, а не просто отдавать пользователю ошибку блокировки.

Единый порядок блокировки строк — это соглашение на уровне всей кодовой базы, а не одного сервиса.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки