mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? mutable state time 1) added time-skipping time-source wrapper 2) wrapped the time source of the mutable state/hbuilder with time-skipping features 3) exposed now() from a mutable state interface timer task time 4) added converter from virtual time to real time in task generators 5) added virtual time reference in timer task time validation ## Why? Principles of correct virtual time management - principle-1: all time in mutable state are consistent virtual - principle-2: wall clock time is used timer queues - principle-3: timer-task (re)generation - when mutable state skips time, non-stale timer tasks should be regenerated (workflow execution timeout, pending user timer) - when mutable state skips time, all future timer tasks should reflec the change - principle-4: timer-task fire time validation - when timer queues validate the fire time, the time of the mutable state should be used as the reference time ## How did you test it? - [x] built - [x] run locally and tested manually - [x] covered by existing tests - [x] added new unit test(s) - [x] added new functional test(s)
93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
package clock_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"go.temporal.io/server/common/clock"
|
|
)
|
|
|
|
func TestTimeSkippingTimeSource_Now_NilGetter(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
base := clock.NewEventTimeSource()
|
|
base.Update(time.Unix(100, 0))
|
|
ts := clock.WrapTimeSourceWithTimeSkipping(base, nil)
|
|
|
|
require.Equal(t, time.Unix(100, 0), ts.Now())
|
|
}
|
|
|
|
func TestTimeSkippingTimeSource_Now_WithZeroOffset(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
base := clock.NewEventTimeSource()
|
|
base.Update(time.Unix(100, 0))
|
|
ts := clock.WrapTimeSourceWithTimeSkipping(base, func() time.Duration { return 0 })
|
|
|
|
require.Equal(t, time.Unix(100, 0), ts.Now())
|
|
}
|
|
|
|
func TestTimeSkippingTimeSource_Now_ReadsOffsetLazily(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
base := clock.NewEventTimeSource()
|
|
base.Update(time.Unix(100, 0))
|
|
offset := 10 * time.Second
|
|
ts := clock.WrapTimeSourceWithTimeSkipping(base, func() time.Duration { return offset })
|
|
|
|
require.Equal(t, time.Unix(110, 0), ts.Now())
|
|
|
|
offset = 120 * time.Second
|
|
require.Equal(t, time.Unix(220, 0), ts.Now())
|
|
}
|
|
|
|
func TestTimeSkippingTimeSource_Since_IncludesOffset(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
base := clock.NewEventTimeSource()
|
|
base.Update(time.Unix(100, 0))
|
|
ts := clock.WrapTimeSourceWithTimeSkipping(base, func() time.Duration { return 50 * time.Second })
|
|
|
|
// Since delegates to Now(), which includes the offset: (100+50) - 90 = 60s.
|
|
require.Equal(t, 60*time.Second, ts.Since(time.Unix(90, 0)))
|
|
}
|
|
|
|
func TestTimeSkippingTimeSource_AfterFunc_DelegatesToBase(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
base := clock.NewEventTimeSource()
|
|
ts := clock.WrapTimeSourceWithTimeSkipping(base, func() time.Duration { return 50 * time.Second })
|
|
|
|
fired := false
|
|
ts.AfterFunc(time.Second, func() { fired = true })
|
|
|
|
require.False(t, fired)
|
|
base.Advance(time.Second)
|
|
require.True(t, fired)
|
|
}
|
|
|
|
func TestTimeSkippingTimeSource_NewTimer_DelegatesToBase(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
base := clock.NewEventTimeSource()
|
|
ts := clock.WrapTimeSourceWithTimeSkipping(base, func() time.Duration { return 50 * time.Second })
|
|
|
|
ch, _ := ts.NewTimer(time.Second)
|
|
|
|
select {
|
|
case <-ch:
|
|
t.Fatal("timer should not fire before deadline")
|
|
default:
|
|
}
|
|
|
|
base.Advance(time.Second)
|
|
|
|
select {
|
|
case <-ch:
|
|
// fired as expected
|
|
default:
|
|
t.Fatal("timer should have fired after deadline")
|
|
}
|
|
}
|