Skip to content

restayway/stx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

STX - Context-based GORM Transaction Manager

CI Go Report Card codecov GoDoc

STX is a lightweight Go package that provides context-based transaction management for GORM. It allows you to seamlessly integrate database transactions with Go's context package, making it easy to pass database connections and transactions through your application layers.

state (ctx) + transaction = stx

Features

  • Context-based transaction management: Embed GORM database instances in Go contexts
  • Thread-safe operations: Concurrent access to database connections is handled safely
  • Flexible transaction control: Support for both automatic and manual transaction management
  • Graceful error handling: Commit/Rollback operations return nil when no transaction is active
  • GORM integration: Works seamlessly with existing GORM code
  • Zero dependencies: Only depends on GORM and standard Go libraries
  • 100% test coverage: Comprehensive test suite with full coverage

Installation

go get github.com/restayway/stx

Quick Start

Basic Usage

package main

import (
    "context"
    "log"
    
    "github.com/restayway/stx"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

type User struct {
    ID   uint   `gorm:"primaryKey"`
    Name string `gorm:"not null"`
}

func main() {
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
    
    // Create context with database
    ctx := stx.New(context.Background(), db)
    
    // Use the context to get database connection
    db = stx.Current(ctx)
    
    // Auto-migrate
    db.AutoMigrate(&User{})
    
    // Create user
    user := User{Name: "John Doe"}
    db.Create(&user)
}

Transaction Management

// Automatic transaction management
err := stx.WithTransaction(ctx, func(txCtx context.Context) error {
    txDB := stx.Current(txCtx)
    
    // All operations within this function are in a transaction
    user := User{Name: "Jane Doe"}
    if err := txDB.Create(&user).Error; err != nil {
        return err // Transaction will be rolled back
    }
    
    // Transaction will be committed if no error is returned
    return nil
})

Manual Transaction Control

// Begin transaction
txCtx := stx.Begin(ctx)
txDB := stx.Current(txCtx)

// Perform operations
user := User{Name: "Bob Smith"}
if err := txDB.Create(&user).Error; err != nil {
    stx.Rollback(txCtx)
    return err
}

// Commit transaction
if err := stx.Commit(txCtx); err != nil {
    return err
}

Defer Block Pattern

// Elegant defer pattern with automatic panic recovery and transaction handling
func demo(ctx context.Context) (err error) {
    txCtx, cleanup := stx.WithDefer(ctx)
    defer cleanup(&err)
    
    db := stx.Current(txCtx)
    
    // Create user
    user := User{Name: "Alice"}
    if err := db.Create(&user).Error; err != nil {
        return err // Transaction will be rolled back
    }
    
    // Transaction will be committed automatically if no error
    return nil
}

Check Transaction Status

if stx.IsTx(ctx) {
    log.Println("Currently in a transaction")
} else {
    log.Println("Not in a transaction")
}

API Reference

Functions

New(ctx context.Context, db *gorm.DB) context.Context

Creates a new context with the given GORM database instance.

Current(ctx context.Context) *gorm.DB

Retrieves the current GORM database instance from the context. Returns nil if no database is found.

WithTransaction(ctx context.Context, fn func(context.Context) error, opts ...*sql.TxOptions) error

Executes the given function within a database transaction. The transaction is automatically committed if the function returns nil, or rolled back if it returns an error.

Begin(ctx context.Context, opts ...*sql.TxOptions) context.Context

Begins a new database transaction and returns a new context with the transaction.

Commit(ctx context.Context) error

Commits the current transaction. Returns nil if no transaction is active (operations were performed directly without transactions).

Rollback(ctx context.Context) error

Rolls back the current transaction. Returns nil if no transaction is active (operations were performed directly without transactions).

WithDefer(ctx context.Context, opts ...*sql.TxOptions) (context.Context, func(*error))

Begins a transaction and returns a context and cleanup function. The cleanup function should be called with defer and handles panic recovery and automatic commit/rollback based on the error state.

IsTx(ctx context.Context) bool

Returns true if the current context contains an active transaction.

Graceful Error Handling

STX provides graceful error handling for transaction operations:

  • Commit/Rollback without transaction: When Commit() or Rollback() is called on a context without an active transaction, they return nil instead of an error. This design acknowledges that operations were performed directly without transactions.
  • Safe operation: This behavior allows for more flexible code patterns where you can safely call commit/rollback operations without checking if a transaction is active.
// Safe to call even if no transaction is active
ctx := stx.New(context.Background(), db)
err := stx.Commit(ctx) // Returns nil, no error

Testing

Run the test suite:

# Run tests
make test

# Run tests with coverage
make cover

# Run all checks (format, lint, test)
make check

Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • GORM for the excellent ORM library
  • The Go community for inspiration and best practices