Skip to content

Commit 699577d

Browse files
fix(scheduler): preserve trigger location
This problem arisen from the fact that we rely on the timezone of the time passed to cron trigger for correct calculation of next activation. We might want to improve that by switching to set timezone as a part of cron specification, but this is also confusing when we take into consideration our separate timezone flag (#3818). In this case, it wasn't done for the time of initial activation kept always in UTC+0 timezone. This resulted in switching from correct timezone to UTC+0 timezone on task reschedule. Refs #3818 Fixes #4743
1 parent a46b933 commit 699577d

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

pkg/scheduler/scheduler.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2017 ScyllaDB
1+
// Copyright (C) 2026 ScyllaDB
22

33
package scheduler
44

@@ -140,6 +140,7 @@ func (s *Scheduler[K]) reschedule(ctx *RunContext[K], initialActivation time.Tim
140140
now := s.now()
141141
if d.Location != nil {
142142
now = now.In(d.Location)
143+
initialActivation = initialActivation.In(d.Location)
143144
}
144145
next := d.Trigger.Next(now)
145146

pkg/scheduler/scheduler_test.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2017 ScyllaDB
1+
// Copyright (C) 2026 ScyllaDB
22

33
package scheduler
44

@@ -855,4 +855,58 @@ func TestReschedule(t *testing.T) {
855855
}
856856
}
857857
})
858+
859+
t.Run("preserve trigger timezone on reschedule", func(t *testing.T) {
860+
ctx, cancel := context.WithCancel(t.Context())
861+
defer cancel()
862+
// Use different timezones in time generator and trigger location
863+
zone0 := time.FixedZone("UTC", 0)
864+
zone5 := time.FixedZone("UTC+5", 5*60*60)
865+
// Trigger at midnight UTC+5
866+
d := Details{
867+
Trigger: schedules.MustCron("0 0 * * *", time.Time{}),
868+
Location: zone5,
869+
}
870+
// Returns noon in UTC+0
871+
timeGen := func() time.Time {
872+
return time.Date(2024, 1, 1, 12, 0, 0, 0, zone0)
873+
}
874+
// Since trigger timezone takes precedence over time generator timezone,
875+
// expect next activation in 7 hours.
876+
expectedNext := timeGen().Add(7 * time.Hour)
877+
878+
f := newFakeRunner()
879+
s := NewScheduler[testKey](timeGen, f.Run, ll)
880+
k := randomKey()
881+
882+
s.Schedule(ctx, k, d)
883+
// Verify initial activation
884+
a := s.Activations(k)
885+
if len(a) != 1 {
886+
t.Fatalf("expected 1 activation in queue, got %d", len(a))
887+
}
888+
if !expectedNext.Equal(a[0].Time) {
889+
t.Fatalf("expected initial activation to be %v, got %v", expectedNext, a[0].Time)
890+
}
891+
// Start task in the background so that it's rescheduled when it finishes
892+
time.AfterFunc(StartOffset, func() {
893+
s.Trigger(ctx, k)
894+
})
895+
// Verify that task finished before timeout or closing scheduler
896+
select {
897+
case <-startAndWait(ctx, s):
898+
t.Fatal("expected a run, scheduler exit")
899+
case <-time.After(Timeout):
900+
t.Fatal("expected a run, timeout")
901+
case <-f.WaitKeys(k):
902+
}
903+
// Verify rescheduled activation
904+
a = s.Activations(k)
905+
if len(a) != 1 {
906+
t.Fatalf("expected 1 activation in queue, got %d", len(a))
907+
}
908+
if !expectedNext.Equal(a[0].Time) {
909+
t.Fatalf("expected rescheduled activation to be %v, got %v", expectedNext, a[0].Time)
910+
}
911+
})
858912
}

0 commit comments

Comments
 (0)