Как сделать транзакции короче
Что проверяют
Понимаете ли вы, чем опасна долгая транзакция: она держит соединение из пула, блокировки строк и мешает очистке старых версий строк. Следом спрашивают, что конкретно вы вынесете за границы @Transactional в показанном коде.
Как отвечать
Главный принцип: внутри транзакции только работа с базой, всё остальное до или после.
- внешние вызовы: HTTP-запросы, отправку в Kafka, письма, работу с файлами выносят из транзакции. Событие после фиксации отправляют через
@TransactionalEventListener(phase = AFTER_COMMIT)или через паттерн outbox, если важна гарантия доставки; - узкая граница: вместо
@Transactionalна весь метод сервиса берутTransactionTemplateи оборачивают только запись. Подготовку, валидацию и расчёты делают заранее; - Open Session In View: в Spring Boot
spring.jpa.open-in-viewпо умолчанию включён и держит сессию Hibernate до конца HTTP-запроса. Его выключают, а нужные данные загружают явно, черезJOIN FETCHили@EntityGraph; - меньше запросов: убрать N+1, включить пакетную запись
hibernate.jdbc.batch_size, большие объёмы обрабатывать порциями, каждая в своей транзакции; - блокировки позже:
SELECT ... FOR UPDATEделать ближе к концу, а где конфликты редки, брать оптимистичную блокировку через@Version; readOnly = true: для чтения, аtimeoutкак страховка от зависаний.
Найти долгие транзакции помогают leakDetectionThreshold в HikariCP, запрос к pg_stat_activity на состояние idle in transaction и параметр PostgreSQL idle_in_transaction_session_timeout.
Где спотыкаются
Предлагают понизить уровень изоляции. Это меняет видимость данных и набор аномалий, но длительность транзакции не сокращает.
Режут одну бизнес-операцию на несколько транзакций без анализа. Если списание прошло, а зачисление упало, данные остаются несогласованными, и нужна компенсация.
Вешают @Transactional на приватный метод или вызывают его из того же класса. Прокси Spring не срабатывает, и граница транзакции оказывается не там, где вы думаете. Похожая ловушка: REQUIRES_NEW внутри внешней транзакции берёт второе соединение, и под нагрузкой пул исчерпывается.