Files
query-orchestration/internal/backgroundtask/README.md
T

159 lines
4.4 KiB
Markdown
Raw Normal View History

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
```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