0.2.71
This commit is contained in:
@@ -93,6 +93,7 @@ type Scheduler struct {
|
||||
store *store.Store
|
||||
log *slog.Logger
|
||||
events *adminevents.Bus
|
||||
paused func() bool
|
||||
|
||||
mu sync.RWMutex
|
||||
tasks map[string]*registered
|
||||
@@ -101,6 +102,21 @@ type Scheduler struct {
|
||||
started bool
|
||||
}
|
||||
|
||||
// SetPaused installs the server-wide activity gate. The function is intentionally read at
|
||||
// execution time so an admin change takes effect without rebuilding the task registry.
|
||||
func (s *Scheduler) SetPaused(paused func() bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.paused = paused
|
||||
}
|
||||
|
||||
func (s *Scheduler) isPaused() bool {
|
||||
s.mu.RLock()
|
||||
paused := s.paused
|
||||
s.mu.RUnlock()
|
||||
return paused != nil && paused()
|
||||
}
|
||||
|
||||
func New(st *store.Store, log *slog.Logger, events *adminevents.Bus) *Scheduler {
|
||||
return &Scheduler{
|
||||
store: st, log: log.With("component", "scheduler"), events: events,
|
||||
@@ -219,6 +235,9 @@ func (s *Scheduler) loop(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (s *Scheduler) runDue(ctx context.Context) {
|
||||
if s.isPaused() {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
s.mu.RLock()
|
||||
entries := make([]*registered, 0, len(s.tasks))
|
||||
@@ -248,6 +267,9 @@ func (s *Scheduler) RunNow(ctx context.Context, id string) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("scheduler: no task %q", id)
|
||||
}
|
||||
if s.isPaused() {
|
||||
return fmt.Errorf("scheduler: server activity is paused for quiet time")
|
||||
}
|
||||
entry.mu.Lock()
|
||||
if entry.running {
|
||||
entry.mu.Unlock()
|
||||
@@ -261,6 +283,9 @@ func (s *Scheduler) RunNow(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
func (s *Scheduler) execute(ctx context.Context, entry *registered, trigger string) {
|
||||
if s.isPaused() {
|
||||
return
|
||||
}
|
||||
entry.mu.Lock()
|
||||
if entry.running {
|
||||
entry.mu.Unlock()
|
||||
|
||||
@@ -40,6 +40,22 @@ func TestATaskNeedsAnIDAndAFunction(t *testing.T) {
|
||||
sched.Register(Task{ID: "broken"})
|
||||
}
|
||||
|
||||
func TestQuietTimePausesManualTasks(t *testing.T) {
|
||||
sched := quietScheduler()
|
||||
ran := false
|
||||
sched.Register(Task{ID: "quiet", Name: "Quiet", Run: func(context.Context) (string, error) {
|
||||
ran = true
|
||||
return "", nil
|
||||
}})
|
||||
sched.SetPaused(func() bool { return true })
|
||||
if err := sched.RunNow(context.Background(), "quiet"); err == nil {
|
||||
t.Fatal("manual task started during quiet time")
|
||||
}
|
||||
if ran {
|
||||
t.Fatal("quiet-time task function ran")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPanickingTaskBecomesAFailedRun(t *testing.T) {
|
||||
// A background job is the one place a panic takes the whole process down for a reason
|
||||
// nobody is watching for. One housekeeping job with a nil map must not be able to stop
|
||||
|
||||
Reference in New Issue
Block a user