Effect scheduling improvements
The existing Schedule
type has been massively upgraded to support even more complex scheduling for repeating, retrying, and folding of Aff
and Eff
types.
A huge thanks to @bmazzarol who did all of the heavy lifting to make this feature a reality!
It has been refactored from the ground up, a Schedule
now is a (possibly infinite) stream of durations. Each duration indicates to the retry
, repeat
, and fold
behaviours how long to wait between each action. The generation of those streams comes from:
Schedule.Forever
- infinite stream of zero length durationsSchedule.Once
- one item stream of zero length durationSchedule.Never
- no durations (a schedule that never runs)Schedule.TimeSeries(1, 2, 3 ...)
- pass in your own durations to build a bespoke scheduleSchedule.spaced(space)
- infinite stream ofspace
length durationsSchedule.linear(seed, factor)
- schedule that recurs continuously using a linear back-offSchedule.exponential(seed, factor)
- schedule that recurs continuously using a exponential back-offSchedule.fibonacci(seed, factor)
- schedule that recurs continuously using a fibonacci based back-offSchedule.upto(max)
- schedule that runs for a given durationSchedule.fixedInterval(interval)
- if that action run between updates takes longer than the interval, then the action will run immediatelySchedule.windowed(interval)
- a schedule that divides the timeline intointerval
-long windows, and sleeps until the nearest window boundary every time it recurs.Schedule.secondOfMinute(second)
- a schedule that recurs every specified second of each minuteSchedule.minuteOfHour(minute)
- a schedule that recurs every specified minute of each hourSchedule.hourOfDay(hour)
- a schedule that recurs every specified hour of each daySchedule.dayOfWeek(day)
- a schedule that recurs every specified day of each week
These schedules are mostly infinite series, and so to control their 'length' we compose with ScheduleTransformer
values to create smaller series, or to manipulate the series in some way (jitter for example). The following functions generate ScheduleTransformer
values.
Schedule.recurs(n)
- Clamps the schedule durations to only recurn
times.Schedule.NoDelayOnFirst
- Regardless of any other settings, it makes the first duration zeroSchedule.RepeatForever
- Repeats any composed schedules foreverSchedule.maxDelay(max)
- limits the returned delays to max delay (upper clamping of durations).Schedule.maxCumulativeDelay(Duration max)
- keeps a tally of all the delays so-far, and ends the generation of the series oncemax
delay has passedSchedule.jitter(minRandom, maxRandom, seed)
- adds random jitter to the durationsSchedule.jitter(factor, seed)
- adds random jitter to the durationsSchedule.decorrelate(factor, seed)
- transforms the schedule by de-correlating each of the durations both up and down in a jittered way.Schedule.resetAfter(max)
- resets the schedule after a provided cumulative max durationSchedule.repeats(n)
- not to be confused with recurs, this repeats the schedulen
times.Schedule.intersperse(schedule)
- intersperse the provided schedule between each duration in the schedule.
Schedule
and ScheduleTransformer
can be composed using |
(union) or &
(intersection):
var schedule = Schedule.linear(1 * sec) | Schedule.recurs(3) | Schedule.repeat(3);
// [1s, 2s, 3s, 1s, 2s, 3s, 1s, 2s, 3s]
Union |
will take the minimum of the two schedules to the length of the longest, intersect &
will take the maximum of the two schedules to the length of the shortest.
One thing remaining to-do is to bring
HasTime<RT>
back into theCore
and allow these schedules to use injectable time. Some of the functions already take aFunc<DateTime>
to access 'now', this will be expanded so time can be sped up or slowed down, with the schedules 'just working'. That'll be in the next few weeks I'm sure, and is related to this issue.
Check out the API documentation to see what's what. And again, thanks to @bmazzarol for the hard work 👍