Skip to main content
Version: 0.7.0

Transactions API Reference

Complete reference for ACID transactions, savepoints, and nested transactions.

Automatic Transaction Management

Tx(ctx context.Context, fn func(tx *Tx) error) error

Executes a function within a transaction. Commits on nil return, rolls back on error or panic.

err := client.Tx(ctx, func(tx *quark.Tx) error {
// All operations use the same transaction
if err := quark.ForTx[User](ctx, tx).Create(&user); err != nil {
return err // Triggers rollback
}
if err := quark.ForTx[Order](ctx, tx).Create(&order); err != nil {
return err // Triggers rollback
}
return nil // Triggers commit
})

Guarantees:

  • Automatic rollback on error return
  • Automatic rollback on panic (re-panics after rollback)
  • Uses default isolation level (driver-specific)

Manual Transaction Control

BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)

Starts a new transaction with explicit options.

tx, err := client.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelSerializable,
ReadOnly: false,
})
if err != nil {
log.Fatal(err)
}
defer tx.Rollback() // Safe cleanup

// Perform operations
quark.ForTx[User](ctx, tx).Create(&user)
quark.ForTx[Account](ctx, tx).Update(&account)

// Commit manually
if err := tx.Commit(); err != nil {
log.Fatal(err)
}

Isolation Levels:

LevelDescription
sql.LevelDefaultDriver default
sql.LevelReadUncommittedDirty reads possible
sql.LevelReadCommittedNo dirty reads
sql.LevelWriteCommittedPostgreSQL-specific
sql.LevelRepeatableReadConsistent snapshot
sql.LevelSerializableFull isolation

Transaction Methods

Commit() error

Commits the transaction.

if err := tx.Commit(); err != nil {
return fmt.Errorf("commit failed: %w", err)
}

Rollback() error

Rolls back the transaction.

if err := tx.Rollback(); err != nil {
return fmt.Errorf("rollback failed: %w", err)
}

Savepoints (Nested Transactions)

Savepoint(name string) error

Creates a named savepoint.

if err := tx.Savepoint("before_payment"); err != nil {
return err
}

RollbackTo(name string) error

Rolls back to a named savepoint.

if err := processPayment(); err != nil {
_ = tx.RollbackTo("before_payment")
return fmt.Errorf("payment failed, rolled back: %w", err)
}

ReleaseSavepoint(name string) error

Releases a named savepoint.

if err := tx.ReleaseSavepoint("before_payment"); err != nil {
return err
}

Nested Transaction Helper

Tx.Tx(ctx context.Context, fn func(tx *Tx) error) error

Nested transaction using savepoints.

err := client.Tx(ctx, func(tx *quark.Tx) error {
// Create order
if err := quark.ForTx[Order](ctx, tx).Create(&order); err != nil {
return err
}

// Nested: payment processing
if err := tx.Tx(ctx, func(nested *quark.Tx) error {
return processPayment(nested, order)
}); err != nil {
// Payment failed but order creation is preserved
// Can implement retry logic here
log.Printf("Payment failed: %v", err)
}

return nil
})

Complete Example

func TransferFunds(ctx context.Context, client *quark.Client, fromID, toID int64, amount float64) error {
return client.Tx(ctx, func(tx *quark.Tx) error {
// Verify sender has sufficient funds
fromAccount, err := quark.ForTx[Account](ctx, tx).Find(fromID)
if err != nil {
return fmt.Errorf("sender account not found: %w", err)
}
if fromAccount.Balance < amount {
return fmt.Errorf("insufficient funds")
}

// Create savepoint before critical operations
if err := tx.Savepoint("funds_check_passed"); err != nil {
return err
}

// Deduct from sender
fromAccount.Balance -= amount
if _, err := quark.ForTx[Account](ctx, tx).Update(&fromAccount); err != nil {
_ = tx.RollbackTo("funds_check_passed")
return fmt.Errorf("failed to deduct from sender: %w", err)
}

// Add to recipient
toAccount, err := quark.ForTx[Account](ctx, tx).Find(toID)
if err != nil {
_ = tx.RollbackTo("funds_check_passed")
return fmt.Errorf("recipient account not found: %w", err)
}

toAccount.Balance += amount
if _, err := quark.ForTx[Account](ctx, tx).Update(&toAccount); err != nil {
_ = tx.RollbackTo("funds_check_passed")
return fmt.Errorf("failed to add to recipient: %w", err)
}

// Release savepoint on success
if err := tx.ReleaseSavepoint("funds_check_passed"); err != nil {
return err
}

// Record transaction
txn := &Transaction{
FromAccountID: fromID,
ToAccountID: toID,
Amount: amount,
Status: "completed",
}
return quark.ForTx[Transaction](ctx, tx).Create(txn)
})
}

ForTx Helper

ForTx[T any](ctx context.Context, tx *Tx) *Query[T]

Creates a Query bound to a transaction.

tx, _ := client.BeginTx(ctx, nil)
defer tx.Rollback()

users, _ := quark.ForTx[User](ctx, tx).
Where("active", "=", true).
List()

// ... more operations ...

tx.Commit()