# 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 ```go 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: ```go 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: ```go 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: ```go 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: ```go // 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