Решит ли пессимистическая блокировка проблему в рассматриваемом случае?
Что проверяют
Вопрос задают продолжением разбора конкретной гонки, поэтому проверяют не определение, а умение сопоставить механизм с задачей: какую именно аномалию вы лечите, где она возникает и не окажется ли блокировка бесполезной. Хороший ответ начинается со встречного вопроса, а не с рассказа про преимущества и недостатки вообще.
Как отвечать
Сначала назовите проблему своими словами: потерянное обновление, двойная бронь, двойное списание. Затем проверьте, попадает ли случай под блокировку. Она помогает, когда:
- конкурирующие транзакции меняют одну и ту же существующую строку;
- весь код, который её меняет, действительно берёт блокировку;
- чтение и запись идут внутри одной транзакции с базой.
В Spring Data это @Lock(LockModeType.PESSIMISTIC_WRITE) над методом репозитория, на уровне SQL то же самое даёт select ... for update. Важное уточнение: замок живёт до конца транзакции, поэтому метод без транзакции или вызванный изнутри того же бина в обход прокси блокировку фактически не берёт.
И назовите случаи, когда она не спасёт: строки ещё нет и две транзакции вставляют дубль, тут нужен уникальный индекс; гонка снаружи базы, например вызов платёжного шлюза; очередь на одну популярную строку упирается в пропускную способность. При редких конфликтах дешевле оптимистичная схема через @Version.
Где спотыкаются
Предлагают synchronized или ReentrantLock. Внутри одной JVM это сработает, но приложение почти всегда запущено в нескольких экземплярах, и соседний узел про такой замок ничего не знает.
Берут блокировку слишком поздно: сущность читают обычным поиском по идентификатору, считают новое значение и только потом блокируют. Между чтением и захватом успевает вклиниться чужая транзакция, и обновление теряется ровно так же.
Не считают цену. Замок держится до коммита, а внутрь транзакции попадает сетевой вызов или пересчёт отчёта. Плюс дедлоки, когда разные места берут те же строки в разном порядке: помогает единый порядок захвата и таймаут ожидания.