76 lines
1.6 KiB
Go
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)
|
|
}
|
|
}
|