Ответ
Вложенная транзакция — это транзакция, которая начинается внутри уже существующей (родительской) транзакции. Это позволяет создавать изолированные точки для отката части операций, не затрагивая всю родительскую транзакцию.
Стандартный пакет database/sql
в Go не поддерживает вложенные транзакции напрямую, так как он предоставляет общий интерфейс, а не все СУБД имеют такую функциональность.
Эмуляция с помощью точек сохранения (Savepoints)
Наиболее распространенный способ эмулировать вложенные транзакции — использовать точки сохранения (savepoints), которые поддерживаются многими СУБД (например, PostgreSQL, MySQL, SQLite).
SAVEPOINT
создает именованную точку внутри транзакции, к которой можно позже откатиться с помощью ROLLBACK TO SAVEPOINT
.
Пример реализации
// tx - это уже начатая транзакция *sql.Tx
// Начало "вложенной" транзакции
_, err := tx.Exec("SAVEPOINT my_savepoint")
if err != nil {
log.Fatalf("Failed to create savepoint: %v", err)
}
// Выполняем какие-то операции внутри "вложенной" транзакции
_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if err != nil {
// Если произошла ошибка, откатываемся к точке сохранения
log.Printf("Operation failed, rolling back to savepoint: %v", err)
_, rollbackErr := tx.Exec("ROLLBACK TO SAVEPOINT my_savepoint")
if rollbackErr != nil {
log.Fatalf("Failed to rollback to savepoint: %v", rollbackErr)
}
} else {
// Если все успешно, можно "закоммитить" изменения, удалив точку сохранения
// В PostgreSQL это не обязательно, но в некоторых СУБД может быть полезно
_, releaseErr := tx.Exec("RELEASE SAVEPOINT my_savepoint")
if releaseErr != nil {
log.Printf("Failed to release savepoint: %v", releaseErr)
}
}
// В конце родительская транзакция коммитится или откатывается целиком
err = tx.Commit()
if err != nil {
log.Fatalf("Failed to commit transaction: %v", err)
}
Важно
- Некоторые драйверы (например,
pgx
для PostgreSQL) предоставляют более удобные абстракции для работы с точками сохранения. - Синтаксис
SAVEPOINT
является стандартным для SQL, но всегда лучше сверяться с документацией вашей СУБД.