Crons
A cron runs a job on a schedule, once per scheduled time across the whole cluster, however many runners there are. Each run is a persisted message, so a run that’s due while its runner restarts still happens.
Declare and implement it
Section titled “Declare and implement it”A cron is declared by name, with its schedule and the job:
import * as Cluster from "@alchemy.run/effect-cluster";import { ClusterCron } from "effect/cluster";import * as Effect from "effect/Effect";import { Default } from "./Cluster.ts";
export default class Cleanup extends Cluster.Cron<Cleanup>()( "Cleanup", { main: import.meta.url, cron: "0 3 * * *", skipIfOlderThan: "1 hour" }, Effect.gen(function* () { return Effect.gen(function* () { const scheduled = yield* ClusterCron.CurrentScheduledTime; yield* Effect.logInfo(`cleanup for ${scheduled.epochMilliseconds}`); }); }).pipe(Effect.provide(Default)),) {}ClusterCron.CurrentScheduledTime is the time the run was scheduled for, not when it started.
Nothing calls a cron, so yield* Cleanup in the Stack to deploy it, as with a singleton.
The schedule
Section titled “The schedule”cron is a cron expression, read in UTC unless you set timeZone. A sixth, leading field schedules by the second: "*/10 * * * * *" runs every ten seconds.
skipIfOlderThan skips a run that’s due longer ago than that, as after an outage. calculateNextRunFromPrevious schedules each run from the previous run’s scheduled time rather than from when it finished.
An expression that doesn’t parse fails the deploy:
Cluster.InvalidCron: Cluster.Cron 'Cleanup': Invalid number of segments in cron expressionDeclare it on its own
Section titled “Declare it on its own”A cron can also be declared by name alone, with its schedule and job on .make, which returns a Layer:
import * as Cluster from "@alchemy.run/effect-cluster";
export class Cleanup extends Cluster.Cron<Cleanup>()("Cleanup") {}import { ClusterCron } from "effect/cluster";import * as Effect from "effect/Effect";import { Cleanup } from "./Cleanup.ts";import { Default } from "./Cluster.ts";
export default Cleanup.make( { main: import.meta.url, cron: "0 3 * * *", skipIfOlderThan: "1 hour" }, Effect.gen(function* () { return Effect.gen(function* () { const scheduled = yield* ClusterCron.CurrentScheduledTime; yield* Effect.logInfo(`cleanup for ${scheduled.epochMilliseconds}`); }); }).pipe(Effect.provide(Default)),);Provide the Layer to the Stack like any other implementation.