Files
RenewCMS/internal/infrastructure/scheduler/runner.go
T

76 lines
1.6 KiB
Go

package scheduler
import (
"RenewCMS/internal/domain/purge"
"context"
"errors"
"log"
"time"
)
const (
TickInterval = time.Hour
StartupDelay = time.Minute
)
type Job interface {
Tick(now time.Time) (purge.RunResult, bool, error)
}
type Runner struct {
job Job
now func() time.Time
ticks <-chan time.Time
startup <-chan time.Time
logger *log.Logger
release func()
}
func NewRunner(job Job, now func() time.Time, ticks <-chan time.Time, startup <-chan time.Time, logger *log.Logger) *Runner {
return &Runner{job: job, now: now, ticks: ticks, startup: startup, logger: logger, release: func() {}}
}
func NewSystemRunner(job Job, logger *log.Logger) *Runner {
ticker := time.NewTicker(TickInterval)
startup := time.NewTimer(StartupDelay)
runner := NewRunner(job, time.Now, ticker.C, startup.C, logger)
runner.release = func() {
ticker.Stop()
startup.Stop()
}
return runner
}
func (r *Runner) Run(ctx context.Context) {
defer r.release()
startup := r.startup
for ctx.Err() == nil {
select {
case <-ctx.Done():
return
case <-startup:
startup = nil
r.tick(ctx)
case <-r.ticks:
r.tick(ctx)
}
}
}
func (r *Runner) tick(ctx context.Context) {
if ctx.Err() != nil {
return
}
result, ran, err := r.job.Tick(r.now())
if errors.Is(err, purge.ErrPurgeBusy) {
return
}
if err != nil {
r.logger.Printf("scheduler: automatic purge failed: %v", err)
return
}
if ran {
r.logger.Printf("scheduler: automatic purge completed: groups_purged=%d merged=%d articles_updated=%d failed_groups=%d", result.GroupsPurged, result.Merged, result.ArticlesUpdated, result.FailedGroups)
}
}