Major changes: - Add internal/logging package with field constants, context propagation, sensitive data auto-redaction, and per-component log levels - Add worker timeout constants (TimeoutQuickOp, TimeoutHealthCheck, etc.) - Extend SDLC with callback handlers, generate endpoints, and executor - Add new cookbook trees for aeries and slackpath progression - Add skeleton templates for queue, realtime, and microservices - Add worker component template with async job processing - Refactor services and handlers to use new logging infrastructure - Split component.go into component_infra.go and component_listing.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
126 lines
2.9 KiB
Go
126 lines
2.9 KiB
Go
package worker
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/orchard9/rdev/internal/port"
|
|
)
|
|
|
|
// OperationCleanup runs periodic cleanup of old operations.
|
|
// Operations older than the retention period (default 30 days) are deleted.
|
|
type OperationCleanup struct {
|
|
repo port.OperationRepository
|
|
logger *slog.Logger
|
|
retentionPeriod time.Duration
|
|
cleanupInterval time.Duration
|
|
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
// OperationCleanupConfig holds configuration for operation cleanup.
|
|
type OperationCleanupConfig struct {
|
|
// RetentionPeriod is how long to keep operations.
|
|
// Default: 30 days.
|
|
RetentionPeriod time.Duration
|
|
|
|
// CleanupInterval is how often to run cleanup.
|
|
// Default: 1 hour.
|
|
CleanupInterval time.Duration
|
|
|
|
Logger *slog.Logger
|
|
}
|
|
|
|
// DefaultOperationCleanupConfig returns sensible defaults.
|
|
func DefaultOperationCleanupConfig() *OperationCleanupConfig {
|
|
return &OperationCleanupConfig{
|
|
RetentionPeriod: 30 * 24 * time.Hour, // 30 days
|
|
CleanupInterval: 1 * time.Hour,
|
|
Logger: slog.Default(),
|
|
}
|
|
}
|
|
|
|
// NewOperationCleanup creates a new operation cleanup worker.
|
|
func NewOperationCleanup(repo port.OperationRepository, cfg *OperationCleanupConfig) *OperationCleanup {
|
|
if cfg == nil {
|
|
cfg = DefaultOperationCleanupConfig()
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
return &OperationCleanup{
|
|
repo: repo,
|
|
logger: cfg.Logger.With("component", "operation-cleanup"),
|
|
retentionPeriod: cfg.RetentionPeriod,
|
|
cleanupInterval: cfg.CleanupInterval,
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
}
|
|
}
|
|
|
|
// Start begins the cleanup loop.
|
|
func (c *OperationCleanup) Start() {
|
|
c.logger.Info("operation cleanup started",
|
|
"retention_period", c.retentionPeriod,
|
|
"cleanup_interval", c.cleanupInterval,
|
|
)
|
|
|
|
c.wg.Add(1)
|
|
go c.cleanupLoop()
|
|
}
|
|
|
|
// Stop gracefully shuts down the cleanup worker.
|
|
func (c *OperationCleanup) Stop() {
|
|
c.logger.Info("operation cleanup stopping")
|
|
c.cancel()
|
|
c.wg.Wait()
|
|
c.logger.Info("operation cleanup stopped")
|
|
}
|
|
|
|
// cleanupLoop runs periodic cleanup.
|
|
func (c *OperationCleanup) cleanupLoop() {
|
|
defer c.wg.Done()
|
|
|
|
// Run immediately on start
|
|
c.runCleanup()
|
|
|
|
ticker := time.NewTicker(c.cleanupInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-c.ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
c.runCleanup()
|
|
}
|
|
}
|
|
}
|
|
|
|
// runCleanup deletes operations older than the retention period.
|
|
func (c *OperationCleanup) runCleanup() {
|
|
ctx, cancel := context.WithTimeout(c.ctx, TimeoutMaintenance)
|
|
defer cancel()
|
|
|
|
cutoff := time.Now().Add(-c.retentionPeriod)
|
|
deleted, err := c.repo.DeleteOlderThan(ctx, cutoff)
|
|
if err != nil {
|
|
c.logger.Error("failed to cleanup old operations",
|
|
"error", err,
|
|
"cutoff", cutoff,
|
|
)
|
|
return
|
|
}
|
|
|
|
if deleted > 0 {
|
|
c.logger.Info("cleaned up old operations",
|
|
"deleted", deleted,
|
|
"cutoff", cutoff,
|
|
)
|
|
}
|
|
}
|