Manager: fix error in sleep scheduler when shutting down

When the Manager was shutting down while the sleep scheduler was running, it
could cause a null pointer dereference. This is now doubly solved:

- `worker.Identifier()` is now nil-safe, as in, `worker` can be `nil` and
  it will still return a sensible string.
- failure to apply the sleep schedule due to the context closing is not
  logged as error any more.
This commit is contained in:
Sybren A. Stüvel 2022-09-27 12:26:20 +02:00
parent 50ec5f4f36
commit 59655ea770
3 changed files with 35 additions and 3 deletions

@ -33,6 +33,10 @@ type Worker struct {
}
func (w *Worker) Identifier() string {
// Avoid a panic when worker.Identifier() is called on a nil pointer.
if w == nil {
return "-nil worker-"
}
return fmt.Sprintf("%s (%s)", w.Name, w.UUID)
}

@ -4,6 +4,7 @@ package sleep_scheduler
import (
"context"
"errors"
"fmt"
"time"
@ -200,7 +201,12 @@ func (ss *SleepScheduler) CheckSchedules(ctx context.Context) {
func (ss *SleepScheduler) checkSchedule(ctx context.Context, schedule *persistence.SleepSchedule) {
// Compute the next time to check.
schedule.NextCheck = ss.calculateNextCheck(schedule)
if err := ss.persist.SetWorkerSleepScheduleNextCheck(ctx, schedule); err != nil {
err := ss.persist.SetWorkerSleepScheduleNextCheck(ctx, schedule)
switch {
case errors.Is(ctx.Err(), context.Canceled):
// Manager is shutting down, this is fine.
return
case err != nil:
log.Error().
Err(err).
Str("worker", schedule.Worker.Identifier()).
@ -209,12 +215,15 @@ func (ss *SleepScheduler) checkSchedule(ctx context.Context, schedule *persisten
}
// Apply the schedule to the worker.
if err := ss.ApplySleepSchedule(ctx, schedule); err != nil {
err = ss.ApplySleepSchedule(ctx, schedule)
switch {
case errors.Is(ctx.Err(), context.Canceled):
// Manager is shutting down, this is fine.
case err != nil:
log.Error().
Err(err).
Str("worker", schedule.Worker.Identifier()).
Msg("sleep scheduler: error applying worker's sleep schedule")
return
}
}

@ -95,6 +95,25 @@ func TestSetScheduleSwappedStartEnd(t *testing.T) {
assert.NoError(t, err)
}
// Test that a sleep check that happens at shutdown of the Manager doesn't cause any panics.
func TestCheckSleepScheduleAtShutdown(t *testing.T) {
ss, mocks, _ := testFixtures(t)
sched := persistence.SleepSchedule{
IsActive: true,
DaysOfWeek: "mo tu we",
StartTime: mkToD(18, 0),
EndTime: mkToD(9, 0),
Worker: nil,
}
// Cancel the context to mimick the Manager shutting down.
ctx, cancel := context.WithCancel(context.Background())
cancel()
mocks.persist.EXPECT().SetWorkerSleepScheduleNextCheck(ctx, &sched).Return(context.Canceled)
ss.checkSchedule(ctx, &sched)
}
func TestApplySleepSchedule(t *testing.T) {
ss, mocks, ctx := testFixtures(t)