Files
2025-08-12 06:37:16 -07:00
..
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00
2025-08-12 06:37:16 -07:00

Background Task Runner

A self-contained Go package for running background tasks with periodic execution and on-demand signaling capabilities.

Purpose

This package provides a robust background task runner that:

  • Executes work functions periodically (default: 60 seconds)
  • Supports on-demand execution via signals
  • Prevents concurrent executions through work coalescing
  • Provides graceful shutdown with configurable timeout
  • Tracks execution statistics for observability
  • Maintains thread-safety for concurrent access

Core Components

  • Runner: Main orchestrator that manages the background task lifecycle
  • WorkFunc: User-defined function signature for task execution
  • Stats: Thread-safe statistics tracking (cycles, timestamps, errors)
  • Options: Configuration pattern for customizing behavior

Usage

Basic Example

package main

import (
    "context"
    "log/slog"
    "time"
    "queryorchestration/internal/backgroundtask"
)

func main() {
    // Define configuration
    config := map[string]any{
        "dbConnection": db,
        "apiClient":    client,
    }
    
    // Define work function
    workFunc := func(ctx context.Context, logger *slog.Logger, cfg map[string]any) error {
        // Perform background work here
        logger.Info("executing background task")
        return nil
    }
    
    // Initialize runner
    runner, err := backgroundtask.Initialize(
        config,
        slog.Default(),
        workFunc,
        backgroundtask.WithInterval(30*time.Second), // Optional: custom interval
    )
    if err != nil {
        panic(err)
    }
    
    // Start runner
    if err := runner.Run(); err != nil {
        panic(err)
    }
    
    // Signal immediate work (optional)
    runner.Signal()
    
    // Graceful shutdown
    ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    defer cancel()
    runner.Shutdown(ctx)
}

Key Features

Work Coalescing

Multiple rapid signals are coalesced into a single execution to prevent resource exhaustion:

runner.Signal() // Triggers work
runner.Signal() // Coalesced if previous work still running
runner.Signal() // Sets pending flag for next execution

Statistics Tracking

Monitor runner performance and health:

stats := runner.Stats()
fmt.Printf("Work cycles: %d\n", stats.WorkCycles())
fmt.Printf("Last error: %v\n", stats.LastErr())
fmt.Printf("Last execution: %v\n", stats.LastStart())

Graceful Shutdown

Ensures clean termination with timeout protection:

ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
if err := runner.Shutdown(ctx); err != nil {
    log.Printf("shutdown timeout: %v", err)
}

Testing

The package includes comprehensive test helpers for unit testing:

// Use provided test configuration
config := backgroundtask.NewTestConfigMap()
testConfig, _ := backgroundtask.GetTestConfig(config)

// Use test work functions
runner, _ := backgroundtask.Initialize(
    config,
    slog.Default(),
    backgroundtask.TestWorkFunc, // Configurable test function
)

// Wait for work cycles in tests
backgroundtask.WaitForWorkCycles(runner, 3, 5*time.Second)

Design Principles

  1. No External Dependencies: Uses only standard library and slog
  2. Thread-Safe: All operations safe for concurrent access
  3. Testable: Fast intervals and test helpers for unit testing
  4. Observable: Built-in statistics for monitoring
  5. Graceful: Clean shutdown with context cancellation
  6. Defensive: Prevents concurrent executions and resource leaks

Configuration

Options

  • WithInterval(duration): Set custom execution interval (default: 60s)

WorkFunc Requirements

  • Must accept context.Context for cancellation
  • Must accept *slog.Logger for structured logging
  • Must accept map[string]any for configuration passing
  • Should respect context cancellation for graceful shutdown
  • Should return meaningful errors for observability

Error Handling

The runner continues operation even when work functions return errors:

  • Errors are logged with details
  • Last error is stored in stats for monitoring
  • Runner remains functional for next execution
  • No panic or crash on work function errors

Thread Safety

All public methods are thread-safe:

  • Signal() can be called from any goroutine
  • Stats() provides consistent snapshots
  • Shutdown() is idempotent with sync.Once protection