Что такое ACID и что означает каждая буква
Что проверяют
Понимаете ли вы гарантии транзакции настолько, чтобы принимать решения в коде: где хватает одной вставки, где нужна транзакция, а где и её мало без явно выбранного уровня изоляции. Для разработчика на Go это ещё и вопрос про аккуратную работу с database/sql.
Как отвечать
Atomicity: транзакция применяется целиком или не применяется вовсе, промежуточного состояния снаружи не видно.
Consistency: транзакция переводит базу из одного корректного состояния в другое, где корректность задана ограничениями, внешними ключами и логикой приложения. Эту букву обеспечивает не только движок, но и разработчик.
Isolation: параллельные транзакции не видят чужих незавершённых изменений. Насколько сильно они изолированы, определяет уровень изоляции, максимальный из них по умолчанию нигде не включён.
Durability: после успешного коммита данные переживут падение процесса и потерю питания, за это отвечают журнал упреждающей записи и сброс на диск.
В Go важно довести транзакцию до конца в любом сценарии:
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
if err != nil { return err }
defer tx.Rollback() // после успешного Commit это безвредный вызов
// ... запросы только через tx
return tx.Commit()Где спотыкаются
Думают, что уровень изоляции по умолчанию защищает от всего. Типовые уровни допускают неповторяющееся чтение и фантомы, а проверка условия с последующей записью в двух транзакциях сразу даёт потерянное обновление. Нужен либо более строгий уровень с повтором при конфликте, либо блокировка строки при чтении.
Не проверяют ошибку на коммите и считают, что раз запросы внутри не падали, транзакция прошла. Отказ приходит именно в этот момент, и запись тихо теряется.
Работают внутри транзакции через исходный пул соединений вместо объекта транзакции. Запрос уходит по другому соединению, своих же изменений не видит, в транзакцию не попадает, а при нехватке свободных соединений всё встаёт в ожидании.