Documentation
¶
Overview ¶
Package mtx provides a flexible transaction management system with support for nested transactions, panic recovery, and context-based transaction propagation. It allows working with different database/sql implementations through interfaces
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNilTxBeginner indicates that the provided TxBeginner is nil. // This error is returned when trying to use a Transactor with an uninitialized beginner. ErrNilTxBeginner = fmt.Errorf("tx beginner is nil") // ErrNilTxOperator indicates that the provided CtxOperator is nil. // This error is returned when trying to use a Transactor with an uninitialized operator. ErrNilTxOperator = fmt.Errorf("tx operator is nil") // ErrBeginTx indicates that starting a new transaction has failed. // This error wraps the underlying error from the database driver. ErrBeginTx = fmt.Errorf("begin tx") // ErrCommitFailed indicates that committing a transaction has failed. // This error wraps the underlying error from the database driver during commit ErrCommitFailed = fmt.Errorf("commit failed") // ErrRollbackFailed indicates that rolling back a transaction has failed. // This error wraps the underlying error from the database driver during rollback. ErrRollbackFailed = fmt.Errorf("rollback failed") // ErrRollbackSuccess indicates that a transaction was successfully rolled back. // Despite being an error type, it signals a successful rollback operation. ErrRollbackSuccess = fmt.Errorf("rollback tx") // ErrPanicRecovered indicates that a panic was recovered and converted to an error. // It wraps the original panic value to provide context about what caused the panic. ErrPanicRecovered = fmt.Errorf("panic recovered") )
Functions ¶
This section is empty.
Types ¶
type ContextOperator ¶
type ContextOperator[K comparable, T Tx] struct { // contains filtered or unexported fields }
ContextOperator implements transaction injection and extraction from context.Context. It uses a type-safe approach with comparable keys to store and retrieve transactions from context, avoiding global string keys and providing compile-time type safety.
The type parameters:
- K: a comparable type used as the context key (typically a custom type or iota)
- T: the transaction type that satisfies the Tx interface
func NewContextOperator ¶
func NewContextOperator[K comparable, T Tx](key K) *ContextOperator[K, T]
NewContextOperator returns a pointer to a new ContextOperator instance.
It accepts a key of a comparable type that will be used for both injecting and extracting transactions from context.Context.
The key should be unique to avoid collisions.
func (*ContextOperator[K, T]) Extract ¶
func (o *ContextOperator[K, T]) Extract(ctx context.Context) (T, bool)
Extract retrieves a transaction from the context if one exists. It performs a type assertion to convert the context value to the expected transaction type. The boolean return value indicates whether a transaction was found and successfully type-asserted.
This method is used by Transactor to check if a transaction already exists in the context, enabling nested transaction support.
Parameters:
- ctx: the context to extract the transaction from
Returns:
- T: the extracted transaction (zero value if not found)
- bool: true if a transaction was found and is of the correct type
Example:
tx, ok := operator.Extract(ctx)
if ok {
// Use existing transaction
result := tx.QueryRow(...)
} else {
// No transaction in context, create new one
}
func (*ContextOperator[K, T]) Inject ¶
func (o *ContextOperator[K, T]) Inject(ctx context.Context, tx T) context.Context
Inject stores a transaction in the context and returns a new context containing it. It uses context.WithValue internally, associating the transaction with the operator's key. The original context remains unchanged.
This method is typically used by Transactor when creating a new transaction to make it available to nested function calls.
Parameters:
- ctx: the parent context
- tx: the transaction to store
type Transactor ¶
type Transactor[B TxBeginner[T], T Tx] struct { // contains filtered or unexported fields }
Transactor manages transactions for a single TxBeginner instance. It provides a high-level API for executing functions within a transaction context, with support for nested transactions, automatic rollback on error/panic, and proper transaction propagation through context.
The type parameters B and T allow working with any transaction implementation that satisfies the TxBeginner and Tx interfaces respectively.
func NewTransactor ¶
func NewTransactor[B TxBeginner[T], T Tx]( beginner B, operator СtxOperator[T]) *Transactor[B, T]
NewTransactor returns new Transactor.
func (*Transactor[B, T]) TryGetTx ¶
func (t *Transactor[B, T]) TryGetTx(ctx context.Context) (T, bool)
TryGetTx attempts to retrieve a transaction from the given context. It returns the transaction and true if found, or a zero value and false otherwise.
func (*Transactor[B, T]) TxBeginner ¶
func (t *Transactor[B, T]) TxBeginner() B
TxBeginner returns the underlying TxBeginner used by this Transactor. This can be useful for creating transactions manually.
func (*Transactor[B, T]) WithinTx ¶
func (t *Transactor[B, T]) WithinTx(ctx context.Context, fn func(ctx context.Context) error) (err error)
WithinTx executes the provided function within a transaction context. It handles transaction creation, propagation, and automatic cleanup (commit/rollback).
Key features:
- Nested transaction support: When called recursively, only the top-level call creates and manages the actual transaction. Inner calls reuse the existing transaction from the context.
- Automatic rollback: If the function returns an error or panics, the transaction is automatically rolled back.
- Automatic commit: If the function completes without error, the transaction is automatically committed (only at the top level).
- Panic recovery: Panics are recovered and converted to errors with ErrPanicRecovered. Higher-level panics override lower-level ones.
- Context propagation: The transaction is injected into the context for inner function calls.
The function follows these rules:
- If a transaction exists in the context, it is reused (nested call)
- Otherwise, a new transaction is created (top-level call)
- Errors from the function or from commit/rollback are properly wrapped
- Panics are handled gracefully without crashing the application
Example:
// Top-level transaction
err := transactor.WithinTx(ctx, func(ctx context.Context) error {
// This operation runs in a transaction
if err := someOperation(ctx); err != nil {
return err // Will trigger rollback
}
// Nested call - reuses the same transaction
err := transactor.WithinTx(ctx, func(ctx context.Context) error {
return anotherOperation(ctx) // Same transaction
})
return err
}) // Auto-commits on success, rolls back on error
Note:
- A processed error returns to the highest level for commit or rollback
- Panics are transformed to errors with the same message
- Higher level panics override lower level panics or errors
Examples:
type TxBeginner ¶
type TxBeginner[T Tx] interface {
comparable
BeginTx(ctx context.Context) (T, error)
}
TxBeginner is responsible for creating new Tx.
type СtxOperator ¶
type СtxOperator[T Tx] interface { Inject(ctx context.Context, tx T) context.Context Extract(ctx context.Context) (T, bool) }
СtxOperator is responsible for transaction propagation through context.Context. It provides methods to inject a transaction into context and extract it back.