From 10bf6213f36d8ddb00f058a4609b85220f3d8334 Mon Sep 17 00:00:00 2001 From: Sebastian Lorenz Date: Tue, 17 Sep 2024 23:12:04 +0200 Subject: [PATCH] fix double firing of schedules at initial interval (#3624) --- .changeset/blue-comics-prove.md | 5 +++++ packages/effect/src/internal/schedule.ts | 17 ++++++++++------- packages/effect/test/Schedule.test.ts | 24 ++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 .changeset/blue-comics-prove.md diff --git a/.changeset/blue-comics-prove.md b/.changeset/blue-comics-prove.md new file mode 100644 index 0000000000..ff5628c965 --- /dev/null +++ b/.changeset/blue-comics-prove.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Fixed double firing of cron schedules in cases where the current time matched the initial interval. diff --git a/packages/effect/src/internal/schedule.ts b/packages/effect/src/internal/schedule.ts index 5e3adeeaeb..5b60f5c3fe 100644 --- a/packages/effect/src/internal/schedule.ts +++ b/packages/effect/src/internal/schedule.ts @@ -433,21 +433,24 @@ export const cron = (expression: string | Cron.Cron): Schedule.Schedule<[number, const cron = parsed.right const date = new Date(now) - let next: number if (initial && Cron.match(cron, date)) { - next = now - } else { - const result = Cron.next(cron, date) - next = result.getTime() + const next = now + const start = beginningOfMinute(next) + const end = endOfMinute(next) + return core.succeed([ + [false, [next, start, end]], + [start, end], + ScheduleDecision.continueWith(Interval.make(start + 60000, end + 60000)) + ]) } + const next = Cron.next(cron, date).getTime() const start = beginningOfMinute(next) const end = endOfMinute(next) - const interval = Interval.make(start, end) return core.succeed([ [false, [next, start, end]], [start, end], - ScheduleDecision.continueWith(interval) + ScheduleDecision.continueWith(Interval.make(start, end)) ]) } ) diff --git a/packages/effect/test/Schedule.test.ts b/packages/effect/test/Schedule.test.ts index c7647ab039..0b128f24ac 100644 --- a/packages/effect/test/Schedule.test.ts +++ b/packages/effect/test/Schedule.test.ts @@ -572,6 +572,30 @@ describe("Schedule", () => { })) }) describe("cron-like scheduling - repeats at point of time (minute of hour, day of week, ...)", () => { + it.effect("recur every minute after initial interval using cron", () => + Effect.gen(function*($) { + const ref = yield* $(Ref.make>([])) + yield* $(TestClock.setTime(new Date(2024, 0, 1, 0, 0, 35).getTime())) + const schedule = Schedule.cron("* * * * *") + yield* $( + TestClock.currentTimeMillis, + Effect.tap((instant) => Ref.update(ref, Array.append(format(instant)))), + Effect.repeat(schedule), + Effect.fork + ) + yield* $(TestClock.adjust("5 minutes")) + const result = yield* $(Ref.get(ref)) + const expected = [ + "Mon Jan 01 2024 00:00:35", + "Mon Jan 01 2024 00:01:00", + "Mon Jan 01 2024 00:02:00", + "Mon Jan 01 2024 00:03:00", + "Mon Jan 01 2024 00:04:00", + "Mon Jan 01 2024 00:05:00" + ] + assert.deepStrictEqual(result, expected) + })) + it.effect("recur at time matching cron expression", () => Effect.gen(function*($) { const ref = yield* $(Ref.make>([]))