Приходилось ли работать с транзакциями в Java
Что проверяют
Вопрос из чужого стека: специальность Go, а спрашивают про Java. Проверяют не знание Spring, а честность и умение переносить понятия. Границы транзакции, откат, изоляция и конкурентный доступ устроены одинаково везде.
Как отвечать
Если с Java дела не имели, скажите это прямо и переведите разговор на суть. В Java транзакции обычно декларативные: аннотация над методом, прокси вокруг объекта, открытие и коммит происходят снаружи кода метода. В Go магии нет, транзакция передаётся явно:
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
if err != nil {
return err
}
defer tx.Rollback()
if err := repo.Debit(ctx, tx, from, sum); err != nil {
return err
}
return tx.Commit()Rollback после успешного коммита вернёт sql.ErrTxDone, и это штатное поведение, а не ошибка. Дальше стоит сказать, что транзакция занимает соединение из пула на всё своё время, поэтому долгие вызовы наружу внутри неё это прямой путь к исчерпанию пула. И что отмена контекста откатит транзакцию за вас.
Если спросят про разницу подходов, отвечайте по делу: аннотация экономит код, но прячет границы и даёт неочевидные правила отката, явная передача многословнее, зато границу транзакции видно глазами прямо в функции.
Где спотыкаются
Внутри транзакции продолжают ходить через db, а не через tx. Запрос уходит по другому соединению, не видит незакоммиченных изменений и может встать в ожидание блокировки, которую держит эта же горутина.
Забывают отложенный откат или расставляют его руками в каждой ветке. Один пропущенный выход из функции оставляет транзакцию открытой, а соединение занятым до таймаута.
Изображают опыт с Java, которого нет. Достаточно одного уточнения про поведение при проверяемом исключении, и легенда рассыпается. И не стоит сыпать цифрами вроде числа транзакций в сутки: про сложность системы они не говорят ничего, а проверить их нельзя.