mirror of
https://github.com/hanzoui/registry-backend.git
synced 2026-10-30 20:30:45 +00:00
96 lines
2.5 KiB
Go
96 lines
2.5 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"registry-backend/ent"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
// WithTxResult wraps the given function with a transaction.
|
|
// If the function returns an error, the transaction is rolled back.
|
|
func WithTxResult[T any](ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) (T, error)) (T, error) {
|
|
var zero T
|
|
|
|
// Start a new transaction
|
|
log.Ctx(ctx).Info().Msg("Starting transaction")
|
|
tx, err := client.Tx(ctx)
|
|
if err != nil {
|
|
return zero, err
|
|
}
|
|
|
|
// Flag to keep track of transaction finalization
|
|
transactionCompleted := false
|
|
|
|
defer func() {
|
|
if transactionCompleted {
|
|
return
|
|
}
|
|
|
|
log.Ctx(ctx).Info().Msg("Transaction not completed, attempting to rollback")
|
|
if v := recover(); v != nil {
|
|
// Attempt to rollback on panic
|
|
err := tx.Rollback() // Ignore rollback error here as panic takes precedence
|
|
log.Ctx(ctx).Info().Msgf("Rollback failed: %v", err)
|
|
panic(v)
|
|
}
|
|
}()
|
|
|
|
// Execute the function within the transaction
|
|
log.Ctx(ctx).Info().Msg("Executing function within transaction")
|
|
result, err := fn(tx)
|
|
if err != nil {
|
|
// Rollback transaction on error
|
|
log.Ctx(ctx).Info().Msgf("Rolling back transaction on error: %v", err)
|
|
if rerr := tx.Rollback(); rerr != nil {
|
|
err = fmt.Errorf("%w: rolling back transaction: %v", err, rerr)
|
|
}
|
|
return zero, err
|
|
}
|
|
|
|
// Commit the transaction
|
|
log.Ctx(ctx).Info().Msg("Committing transaction")
|
|
if err := tx.Commit(); err != nil {
|
|
log.Ctx(ctx).Info().Msgf("Error committing transaction: %v", err)
|
|
return zero, fmt.Errorf("committing transaction: %w", err)
|
|
}
|
|
|
|
// Mark the transaction as completed to prevent deferred rollback
|
|
log.Ctx(ctx).Info().Msg("Transaction completed successfully")
|
|
transactionCompleted = true
|
|
return result, nil
|
|
}
|
|
|
|
func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
|
|
tx, err := client.Tx(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if v := recover(); v != nil {
|
|
tx.Rollback()
|
|
panic(v)
|
|
}
|
|
}()
|
|
if err := fn(tx); err != nil {
|
|
if rerr := tx.Rollback(); rerr != nil {
|
|
err = fmt.Errorf("%w: rolling back transaction: %v", err, rerr)
|
|
}
|
|
return err
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
return fmt.Errorf("committing transaction: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// rollback calls to tx.Rollback and wraps the given error
|
|
// with the rollback error if occurred.
|
|
func rollback(tx *ent.Tx, err error) error {
|
|
if rerr := tx.Rollback(); rerr != nil {
|
|
err = fmt.Errorf("%w: %v", err, rerr)
|
|
}
|
|
return err
|
|
}
|