db

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2025 License: MIT Imports: 10 Imported by: 0

README

Database Package

The db package provides utilities for database connection management and operations in Go applications. It supports multiple database types and provides features for connection pooling, transaction management, and query execution.

Features

  • Connection Management:

    • Connection pooling
    • Automatic reconnection
    • Health checks
  • Supported Databases:

    • PostgreSQL (via pgx)
    • SQLite
    • MongoDB
  • Features:

    • Transaction management
    • Query execution with retries
    • Result mapping
    • Migrations

Installation

go get github.com/abitofhelp/servicelib/db

Usage

Connecting to a Database
package main

import (
    "context"
    "log"
    "github.com/abitofhelp/servicelib/db"
    "github.com/abitofhelp/servicelib/config"
)

func main() {
    // Load configuration
    cfg, err := config.New("config.yaml")
    if err != nil {
        log.Fatalf("Failed to load configuration: %v", err)
    }

    // Create a database connection
    ctx := context.Background()
    database, err := db.New(ctx, cfg)
    if err != nil {
        log.Fatalf("Failed to connect to database: %v", err)
    }
    defer database.Close()

    // Configure connection pool
    database.SetMaxOpenConns(25)
    database.SetMaxIdleConns(10)
    database.SetConnMaxLifetime(5 * time.Minute)

    // Check connection
    if err := database.Ping(ctx); err != nil {
        log.Fatalf("Failed to ping database: %v", err)
    }

    log.Println("Successfully connected to database")
}
Executing Queries
package main

import (
    "context"
    "log"
    "github.com/abitofhelp/servicelib/db"
)

type User struct {
    ID       string
    Username string
    Email    string
    Active   bool
}

func main() {
    // Assume database is already connected
    ctx := context.Background()
    
    // Execute a query
    rows, err := database.Query(ctx, "SELECT id, username, email, active FROM users WHERE active = $1", true)
    if err != nil {
        log.Fatalf("Failed to execute query: %v", err)
    }
    defer rows.Close()

    // Process results
    var users []User
    for rows.Next() {
        var user User
        if err := rows.Scan(&user.ID, &user.Username, &user.Email, &user.Active); err != nil {
            log.Fatalf("Failed to scan row: %v", err)
        }
        users = append(users, user)
    }

    if err := rows.Err(); err != nil {
        log.Fatalf("Error iterating rows: %v", err)
    }

    log.Printf("Found %d active users", len(users))
}
Transaction Management
package main

import (
    "context"
    "log"
    "github.com/abitofhelp/servicelib/db"
)

func main() {
    // Assume database is already connected
    ctx := context.Background()
    
    // Execute a transaction
    err := database.Transaction(ctx, func(tx *sql.Tx) error {
        // Execute first query
        _, err := tx.Exec("INSERT INTO users (id, username, email, active) VALUES ($1, $2, $3, $4)",
            "user123", "johndoe", "john.doe@example.com", true)
        if err != nil {
            return err
        }

        // Execute second query
        _, err = tx.Exec("INSERT INTO user_roles (user_id, role) VALUES ($1, $2)",
            "user123", "admin")
        if err != nil {
            return err
        }

        return nil
    })

    if err != nil {
        log.Fatalf("Transaction failed: %v", err)
    }

    log.Println("Transaction completed successfully")
}
Query with Retries
package main

import (
    "context"
    "log"
    "time"
    "github.com/abitofhelp/servicelib/db"
)

func main() {
    // Assume database is already connected
    ctx := context.Background()
    
    // Configure retry options
    options := db.RetryOptions{
        MaxRetries:  3,
        InitialDelay: 100 * time.Millisecond,
        MaxDelay:     1 * time.Second,
        Multiplier:   2.0,
    }

    // Execute query with retries
    var count int
    err := db.QueryWithRetries(ctx, database, options, func(ctx context.Context, db *sql.DB) error {
        return db.QueryRowContext(ctx, "SELECT COUNT(*) FROM users").Scan(&count)
    })

    if err != nil {
        log.Fatalf("Query failed after retries: %v", err)
    }

    log.Printf("User count: %d", count)
}
Using the Repository Pattern
package main

import (
    "context"
    "database/sql"
    "errors"
    "log"
    "github.com/abitofhelp/servicelib/db"
)

// User model
type User struct {
    ID       string
    Username string
    Email    string
    Active   bool
}

// UserRepository interface
type UserRepository interface {
    GetByID(ctx context.Context, id string) (*User, error)
    Create(ctx context.Context, user *User) error
    Update(ctx context.Context, user *User) error
    Delete(ctx context.Context, id string) error
}

// SQLUserRepository implementation
type SQLUserRepository struct {
    db *sql.DB
}

func NewSQLUserRepository(db *sql.DB) UserRepository {
    return &SQLUserRepository{db: db}
}

func (r *SQLUserRepository) GetByID(ctx context.Context, id string) (*User, error) {
    var user User
    err := r.db.QueryRowContext(ctx, 
        "SELECT id, username, email, active FROM users WHERE id = $1", 
        id,
    ).Scan(&user.ID, &user.Username, &user.Email, &user.Active)

    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, nil // Not found
        }
        return nil, err
    }

    return &user, nil
}

func (r *SQLUserRepository) Create(ctx context.Context, user *User) error {
    _, err := r.db.ExecContext(ctx,
        "INSERT INTO users (id, username, email, active) VALUES ($1, $2, $3, $4)",
        user.ID, user.Username, user.Email, user.Active,
    )
    return err
}

func (r *SQLUserRepository) Update(ctx context.Context, user *User) error {
    _, err := r.db.ExecContext(ctx,
        "UPDATE users SET username = $1, email = $2, active = $3 WHERE id = $4",
        user.Username, user.Email, user.Active, user.ID,
    )
    return err
}

func (r *SQLUserRepository) Delete(ctx context.Context, id string) error {
    _, err := r.db.ExecContext(ctx, "DELETE FROM users WHERE id = $1", id)
    return err
}

func main() {
    // Assume database is already connected
    ctx := context.Background()
    
    // Create repository
    userRepo := NewSQLUserRepository(database)
    
    // Use repository
    user, err := userRepo.GetByID(ctx, "user123")
    if err != nil {
        log.Fatalf("Failed to get user: %v", err)
    }
    
    if user != nil {
        log.Printf("Found user: %s (%s)", user.Username, user.Email)
    } else {
        log.Println("User not found")
    }
}

Configuration

Example database configuration in YAML:

database:
  driver: "postgres"
  url: "postgres://user:password@localhost:5432/mydb?sslmode=disable"
  max_open_conns: 25
  max_idle_conns: 10
  conn_max_lifetime_seconds: 300
  retry:
    max_retries: 3
    initial_delay_ms: 100
    max_delay_ms: 1000
    multiplier: 2.0

Best Practices

  1. Connection Pooling: Configure connection pools based on your application's needs and the database's capacity.

  2. Transactions: Use transactions for operations that need to be atomic.

  3. Prepared Statements: Use prepared statements to prevent SQL injection and improve performance.

  4. Context: Always pass a context to database operations to enable cancellation and timeouts.

  5. Error Handling: Handle database errors appropriately, distinguishing between different types of errors.

  6. Repository Pattern: Use the repository pattern to abstract database access and make testing easier.

  7. Migrations: Use database migrations to manage schema changes.

License

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

Documentation

Overview

Package db provides utilities for working with databases.

Index

Constants

View Source
const DefaultTimeout = 30 * time.Second

DefaultTimeout is the default timeout for database operations

Variables

This section is empty.

Functions

func CheckMongoHealth

func CheckMongoHealth(ctx context.Context, client *mongo.Client) error

CheckMongoHealth checks if a MongoDB connection is healthy. Parameters:

  • ctx: The context for the operation
  • client: The MongoDB client

Returns:

  • error: An error if the health check fails

func CheckPostgresHealth

func CheckPostgresHealth(ctx context.Context, pool *pgxpool.Pool) error

CheckPostgresHealth checks if a PostgreSQL connection is healthy. Parameters:

  • ctx: The context for the operation
  • pool: The PostgreSQL connection pool

Returns:

  • error: An error if the health check fails

func CheckSQLiteHealth

func CheckSQLiteHealth(ctx context.Context, db *sql.DB) error

CheckSQLiteHealth checks if a SQLite connection is healthy. Parameters:

  • ctx: The context for the operation
  • db: The SQLite database connection

Returns:

  • error: An error if the health check fails

func ExecutePostgresTransaction

func ExecutePostgresTransaction(ctx context.Context, pool *pgxpool.Pool, fn func(tx pgx.Tx) error) error

ExecutePostgresTransaction executes a function within a PostgreSQL transaction. Parameters:

  • ctx: The context for the operation
  • pool: The PostgreSQL connection pool
  • fn: The function to execute within the transaction

Returns:

  • error: An error if the transaction fails

func ExecuteSQLTransaction

func ExecuteSQLTransaction(ctx context.Context, db *sql.DB, fn func(tx *sql.Tx) error) error

ExecuteSQLTransaction executes a function within a SQL transaction. Parameters:

  • ctx: The context for the operation
  • db: The SQL database connection
  • fn: The function to execute within the transaction

Returns:

  • error: An error if the transaction fails

func InitMongoClient

func InitMongoClient(ctx context.Context, uri string, timeout time.Duration) (*mongo.Client, error)

InitMongoClient initializes a MongoDB client. Parameters:

  • ctx: The context for the operation
  • uri: The MongoDB connection URI
  • timeout: The timeout for the connection operation

Returns:

  • *mongo.Client: The initialized MongoDB client
  • error: An error if initialization fails

func InitPostgresPool

func InitPostgresPool(ctx context.Context, uri string, timeout time.Duration) (*pgxpool.Pool, error)

InitPostgresPool initializes a PostgreSQL connection pool. Parameters:

  • ctx: The context for the operation
  • uri: The PostgreSQL connection URI
  • timeout: The timeout for the connection operation

Returns:

  • *pgxpool.Pool: The initialized PostgreSQL connection pool
  • error: An error if initialization fails

func InitSQLiteDB

func InitSQLiteDB(ctx context.Context, uri string, timeout, connMaxLifetime time.Duration, maxOpenConns, maxIdleConns int) (*sql.DB, error)

InitSQLiteDB initializes a SQLite database connection. Parameters:

  • ctx: The context for the operation
  • uri: The SQLite connection URI
  • timeout: The timeout for the connection operation
  • maxOpenConns: The maximum number of open connections
  • maxIdleConns: The maximum number of idle connections
  • connMaxLifetime: The maximum lifetime of a connection

Returns:

  • *sql.DB: The initialized SQLite database connection
  • error: An error if initialization fails

func LogDatabaseConnection

func LogDatabaseConnection(ctx context.Context, logger *logging.ContextLogger, dbType string)

LogDatabaseConnection logs a successful database connection. Parameters:

  • ctx: The context for the operation
  • logger: The logger to use
  • dbType: The type of database (e.g., "MongoDB", "PostgreSQL", "SQLite")

Types

type MongoClientInterface

type MongoClientInterface interface {
	Ping(ctx context.Context, rp *readpref.ReadPref) error
	Connect(ctx context.Context) error
	Database(name string, opts ...*options.DatabaseOptions) *mongo.Database
}

MongoClientInterface defines the interface for mongo.Client operations

type PgxPoolInterface

type PgxPoolInterface interface {
	Ping(ctx context.Context) error
	Begin(ctx context.Context) (pgx.Tx, error)
	Close()
}

PgxPoolInterface defines the interface for pgxpool.Pool operations

type PgxTxInterface

type PgxTxInterface interface {
	Commit(ctx context.Context) error
	Rollback(ctx context.Context) error
}

PgxTxInterface defines the interface for pgx.Tx operations

type SQLDBInterface

type SQLDBInterface interface {
	PingContext(ctx context.Context) error
	BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
	SetMaxOpenConns(n int)
	SetMaxIdleConns(n int)
	SetConnMaxLifetime(d time.Duration)
}

SQLDBInterface defines the interface for sql.DB operations

type SQLTxInterface

type SQLTxInterface interface {
	Commit() error
	Rollback() error
}

SQLTxInterface defines the interface for sql.Tx operations

Directories

Path Synopsis
Package mocks is a generated GoMock package.
Package mocks is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL