Skip to content

Commit 8c7cf89

Browse files
authored
EFF-746 Fix Schedule.fixed immediate catch-up for long-running iterations (#1831)
1 parent 53740f4 commit 8c7cf89

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": patch
3+
---
4+
5+
Fix `Schedule.fixed` to run the next iteration immediately when the previous action takes longer than the configured interval.

packages/effect/src/Schedule.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,14 +2194,26 @@ export const fibonacci = (one: Duration.Input): Schedule<Duration.Duration> => {
21942194
*/
21952195
export const fixed = (interval: Duration.Input): Schedule<number> => {
21962196
const window = Duration.toMillis(Duration.fromInputUnsafe(interval))
2197-
return fromStepWithMetadata(effect.succeed((meta) =>
2198-
effect.succeed([
2199-
meta.attempt - 1,
2200-
window === 0
2201-
? Duration.zero
2202-
: Duration.millis(window - (meta.elapsed % window))
2203-
])
2204-
))
2197+
return fromStepWithMetadata(effect.sync(() => {
2198+
let start = 0
2199+
let lastRun = 0
2200+
return (meta) =>
2201+
effect.sync(() => {
2202+
if (window === 0) {
2203+
return [meta.attempt - 1, Duration.zero] as const
2204+
}
2205+
if (meta.attempt === 1) {
2206+
start = meta.now
2207+
lastRun = meta.now + window
2208+
return [0, Duration.millis(window)] as const
2209+
}
2210+
const runningBehind = meta.now > (lastRun + window)
2211+
const boundary = window - ((meta.now - start) % window)
2212+
const delay = runningBehind ? 0 : boundary === 0 ? window : boundary
2213+
lastRun = runningBehind ? meta.now : meta.now + delay
2214+
return [meta.attempt - 1, Duration.millis(delay)] as const
2215+
})
2216+
}))
22052217
}
22062218

22072219
/**

packages/effect/test/Schedule.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,34 @@ describe("Schedule", () => {
241241
Duration.zero
242242
])
243243
}))
244+
245+
it.effect("matches effect v3 when action duration exceeds the interval", () =>
246+
Effect.gen(function*() {
247+
const delays: Array<Duration.Duration> = []
248+
const schedule = Schedule.fixed("1 seconds").pipe(
249+
Schedule.while(({ attempt }) => Effect.succeed(attempt <= 5)),
250+
Schedule.delays,
251+
Schedule.map((delay) =>
252+
Effect.sync(() => {
253+
delays.push(delay)
254+
return delays
255+
})
256+
)
257+
)
258+
yield* Effect.sleep("1.5 seconds").pipe(
259+
Effect.schedule(schedule),
260+
Effect.forkChild
261+
)
262+
yield* TestClock.setTime(Number.POSITIVE_INFINITY)
263+
expect(delays).toEqual([
264+
Duration.millis(1000),
265+
Duration.zero,
266+
Duration.zero,
267+
Duration.zero,
268+
Duration.zero,
269+
Duration.zero
270+
])
271+
}))
244272
})
245273

246274
describe("windowed", () => {

0 commit comments

Comments
 (0)