Add database transaction helpers
This commit is contained in:
parent
694f8ba1d3
commit
b5141d6a70
68
database/transaction.go
Normal file
68
database/transaction.go
Normal file
@ -0,0 +1,68 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.ntppool.org/common/logger"
|
||||
)
|
||||
|
||||
// DB interface for database operations that can begin transactions
|
||||
type DB[Q any] interface {
|
||||
Begin(ctx context.Context) (Q, error)
|
||||
}
|
||||
|
||||
// TX interface for transaction operations
|
||||
type TX interface {
|
||||
Commit(ctx context.Context) error
|
||||
Rollback(ctx context.Context) error
|
||||
}
|
||||
|
||||
// WithTransaction executes a function within a database transaction
|
||||
// Handles proper rollback on error and commit on success
|
||||
func WithTransaction[Q TX](ctx context.Context, db DB[Q], fn func(ctx context.Context, q Q) error) error {
|
||||
tx, err := db.Begin(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin transaction: %w", err)
|
||||
}
|
||||
|
||||
var committed bool
|
||||
defer func() {
|
||||
if !committed {
|
||||
if rbErr := tx.Rollback(ctx); rbErr != nil {
|
||||
// Log rollback error but don't override original error
|
||||
log := logger.FromContext(ctx)
|
||||
log.ErrorContext(ctx, "failed to rollback transaction", "error", rbErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err := fn(ctx, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return fmt.Errorf("failed to commit transaction: %w", err)
|
||||
}
|
||||
|
||||
committed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithReadOnlyTransaction executes a read-only function within a transaction
|
||||
// Always rolls back at the end (for consistent read isolation)
|
||||
func WithReadOnlyTransaction[Q TX](ctx context.Context, db DB[Q], fn func(ctx context.Context, q Q) error) error {
|
||||
tx, err := db.Begin(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to begin read-only transaction: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if rbErr := tx.Rollback(ctx); rbErr != nil {
|
||||
log := logger.FromContext(ctx)
|
||||
log.ErrorContext(ctx, "failed to rollback read-only transaction", "error", rbErr)
|
||||
}
|
||||
}()
|
||||
|
||||
return fn(ctx, tx)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user