Может ли база данных сама разрешить проблему коллизий без нашего участия
Что проверяют
Понимаете ли вы границу ответственности: что СУБД гарантирует сама, а что остаётся на коде приложения. Под коллизией здесь почти всегда имеют в виду конкурентное изменение одних и тех же данных. Следом обычно просят показать, как вы защищаетесь от потерянного обновления в Spring и JPA.
Как отвечать
Короткий ответ: частично. База сама обеспечивает изоляцию, но не решает, чья правка по смыслу важнее. Разложите по механизмам:
- блокировки и MVCC: две транзакции не испортят одну строку физически, вторая подождёт или прочитает прежнюю версию;
- атомарный
UPDATE: запросSET balance = balance - ?база выполнит корректно при любой конкуренции, потому что чтение и запись идут одной операцией; - ограничения:
UNIQUEне пустит дубль, а в PostgreSQLINSERT ... ON CONFLICTзадаёт реакцию прямо в запросе; - детектор дедлоков: база находит взаимную блокировку и откатывает одну из транзакций. Но только откатывает, повторять её за вас никто не будет.
Дальше то, что без вас не работает. Схема «прочитал в Java, изменил, сохранил» на уровне Read Committed (по умолчанию в PostgreSQL) даёт потерянное обновление: база видит две корректные записи подряд. Защита ваша: оптимистическая блокировка через @Version в JPA, при конфликте Hibernate бросит OptimisticLockException, а Spring отдаст её как ObjectOptimisticLockingFailureException; пессимистическая через @Lock(LockModeType.PESSIMISTIC_WRITE), что превращается в SELECT ... FOR UPDATE. На уровне Serializable база сама обнаружит конфликт, но ответит ошибкой сериализации с кодом 40001, и транзакцию нужно повторить, например через @Retryable из Spring Retry.
Где спотыкаются
Отвечают «да, для этого есть транзакции». Одна аннотация @Transactional с уровнем изоляции по умолчанию от потерянного обновления не спасает.
Путают обнаружение конфликта с его разрешением. Дедлок и ошибка сериализации это сигнал приложению, и без логики повтора пользователь просто получит 500.
Ставят повтор на весь метод с внешними вызовами внутри, и письмо или платёж уходят дважды. Повторять стоит только работу с базой, а побочные эффекты выносить за коммит.