issue-742: bug in `NextRun` (#743)

* issue-742: bug in `NextRun`

* issue-742: bug in `NextRun` correction
This commit is contained in:
Rodrigo Broggi 2024-06-21 16:17:34 +02:00 committed by GitHub
parent fd18ca7b52
commit 7c391d4326
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 96 additions and 16 deletions

View File

@ -357,18 +357,16 @@ func (s *scheduler) selectExecJobsOutCompleted(id uuid.UUID) {
return
}
// if the job has more than one nextScheduled time,
// if the job has nextScheduled time in the past,
// we need to remove any that are in the past.
if len(j.nextScheduled) > 1 {
var newNextScheduled []time.Time
for _, t := range j.nextScheduled {
if t.Before(s.now()) {
continue
}
newNextScheduled = append(newNextScheduled, t)
var newNextScheduled []time.Time
for _, t := range j.nextScheduled {
if t.Before(s.now()) {
continue
}
j.nextScheduled = newNextScheduled
newNextScheduled = append(newNextScheduled, t)
}
j.nextScheduled = newNextScheduled
// if the job has a limited number of runs set, we need to
// check how many runs have occurred and stop running this

View File

@ -6,10 +6,12 @@ import (
"fmt"
"os"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/google/uuid"
"github.com/jonboulle/clockwork"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
@ -1185,6 +1187,92 @@ func TestScheduler_LimitModeAndSingleton(t *testing.T) {
}
}
func TestScheduler_OneTimeJob_DoesNotCleanupNext(t *testing.T) {
defer verifyNoGoroutineLeaks(t)
schedulerStartTime := time.Date(2024, time.April, 3, 4, 5, 0, 0, time.UTC)
tests := []struct {
name string
runAt time.Time
fakeClock clockwork.FakeClock
assertErr require.ErrorAssertionFunc
// asserts things about schedules, advance time and perform new assertions
advanceAndAsserts []func(
t *testing.T,
j Job,
clock clockwork.FakeClock,
runs *atomic.Uint32,
)
}{
{
name: "exhausted run do does not cleanup next item",
runAt: time.Date(2024, time.April, 22, 4, 5, 0, 0, time.UTC),
fakeClock: clockwork.NewFakeClockAt(schedulerStartTime),
advanceAndAsserts: []func(t *testing.T, j Job, clock clockwork.FakeClock, runs *atomic.Uint32){
func(t *testing.T, j Job, clock clockwork.FakeClock, runs *atomic.Uint32) {
require.Equal(t, uint32(0), runs.Load())
// last not initialized
lastRunAt, err := j.LastRun()
require.NoError(t, err)
require.Equal(t, time.Time{}, lastRunAt)
// next is now
expected := time.Date(2024, time.April, 22, 4, 5, 0, 0, time.UTC)
nextRunAt, err := j.NextRun()
require.NoError(t, err)
require.Equal(t, expected, nextRunAt.UTC())
// advance and eventually run
oneSecondAfterNextRun := expected.Add(1 * time.Second)
clock.Advance(oneSecondAfterNextRun.Sub(schedulerStartTime))
require.Eventually(t, func() bool {
return assert.Equal(t, uint32(1), runs.Load())
}, 3*time.Second, 100*time.Millisecond)
// last was run
lastRunAt, err = j.LastRun()
require.NoError(t, err)
require.WithinDuration(t, expected, lastRunAt, 1*time.Second)
nextRunAt, err = j.NextRun()
require.NoError(t, err)
require.Equal(t, time.Time{}, nextRunAt)
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := newTestScheduler(t, WithClock(tt.fakeClock), WithLocation(time.UTC))
t.Cleanup(func() {
require.NoError(t, s.Shutdown())
})
runs := atomic.Uint32{}
j, err := s.NewJob(
OneTimeJob(OneTimeJobStartDateTime(tt.runAt)),
NewTask(func() {
runs.Add(1)
}),
)
if tt.assertErr != nil {
tt.assertErr(t, err)
} else {
require.NoError(t, err)
s.Start()
for _, advanceAndAssert := range tt.advanceAndAsserts {
advanceAndAssert(t, j, tt.fakeClock, &runs)
}
}
})
}
}
var _ Elector = (*testElector)(nil)
type testElector struct {
@ -1980,7 +2068,7 @@ func TestScheduler_OneTimeJob(t *testing.T) {
s := newTestScheduler(t)
j, err := s.NewJob(
_, err := s.NewJob(
OneTimeJob(tt.startAt()),
NewTask(func() {
jobRan <- struct{}{}
@ -1996,12 +2084,6 @@ func TestScheduler_OneTimeJob(t *testing.T) {
t.Fatal("timed out waiting for job to run")
}
var nextRun time.Time
for ; nextRun.IsZero(); nextRun, err = j.NextRun() { //nolint:revive
}
assert.NoError(t, err)
assert.True(t, nextRun.Before(time.Now()))
assert.NoError(t, s.Shutdown())
})
}