| /** | |
| * Runs recurring cron jobs through cluster sharding. | |
| * | |
| * This module turns a `Cron.Cron` schedule into a `Layer` that coordinates one | |
| * recurring job across a cluster. It registers a singleton for the initial | |
| * scheduling step and a persisted entity message for each run. This is useful | |
| * for distributed maintenance work where the job should be owned by the cluster | |
| * rather than by every runner independently. | |
| * | |
| * @since 4.0.0 | |
| */ | |
| import * as Cron from "../../Cron.js"; | |
| import * as DateTime from "../../DateTime.js"; | |
| import * as Duration from "../../Duration.js"; | |
| import * as Effect from "../../Effect.js"; | |
| import * as Exit from "../../Exit.js"; | |
| import * as Layer from "../../Layer.js"; | |
| import * as Option from "../../Option.js"; | |
| import * as PrimaryKey from "../../PrimaryKey.js"; | |
| import * as Schedule from "../../Schedule.js"; | |
| import * as Schema from "../../Schema.js"; | |
| import * as Rpc from "../rpc/Rpc.js"; | |
| import * as ClusterSchema from "./ClusterSchema.js"; | |
| import { Persisted, Uninterruptible } from "./ClusterSchema.js"; | |
| import * as DeliverAt from "./DeliverAt.js"; | |
| import * as Entity from "./Entity.js"; | |
| import * as Singleton from "./Singleton.js"; | |
| /** | |
| * Creates a layer that runs a cron job through the cluster sharding system. | |
| * | |
| * **Details** | |
| * | |
| * The job is scheduled as persisted entity messages, with an initial singleton | |
| * scheduling step and optional controls for shard group, next-run calculation, | |
| * and skipping stale scheduled runs. | |
| * | |
| * @category constructors | |
| * @since 4.0.0 | |
| */ | |
| export const make = options => { | |
| const CronEntity = Entity.make(`ClusterCron/${options.name}`, [Rpc.make("run", { | |
| payload: CronPayload | |
| }).annotate(Persisted, true).annotate(Uninterruptible, true)]).annotate(ClusterSchema.ShardGroup, () => options.shardGroup ?? "default").annotate(ClusterSchema.ClientTracingEnabled, false); | |
| const InitialRun = Singleton.make(`ClusterCron/${options.name}`, Effect.gen(function* () { | |
| const now = yield* DateTime.now; | |
| const next = DateTime.fromDateUnsafe(Cron.next(options.cron, now)); | |
| const entityId = options.calculateNextRunFromPrevious ? "initial" : DateTime.formatIso(next); | |
| const client = (yield* CronEntity.client)(entityId); | |
| yield* client.run({ | |
| dateTime: next | |
| }, { | |
| discard: true | |
| }); | |
| }), { | |
| shardGroup: options.shardGroup | |
| }); | |
| const skipIfOlderThan = Option.fromUndefinedOr(options.skipIfOlderThan).pipe(Option.map(Duration.fromInputUnsafe), Option.getOrElse(() => Duration.days(1))); | |
| const effect = Effect.fnUntraced(function* (dateTime) { | |
| const now = yield* DateTime.now; | |
| if (DateTime.isLessThan(dateTime, DateTime.subtractDuration(now, skipIfOlderThan))) { | |
| return; | |
| } | |
| return yield* options.execute; | |
| }, Effect.orDie); | |
| const EntityLayer = CronEntity.toLayer(Effect.gen(function* () { | |
| const makeClient = yield* CronEntity.client; | |
| return { | |
| run: request => Effect.onExitPrimitive(effect(request.payload.dateTime), Effect.fnUntraced(function* (exit) { | |
| if (Exit.isFailure(exit)) { | |
| yield* Effect.logWarning(exit.cause); | |
| } | |
| const now = yield* DateTime.now; | |
| const next = DateTime.fromDateUnsafe(Cron.next(options.cron, options.calculateNextRunFromPrevious ? request.payload.dateTime : now)); | |
| const client = makeClient(DateTime.formatIso(next)); | |
| return yield* client.run({ | |
| dateTime: next | |
| }, { | |
| discard: true | |
| }).pipe(Effect.tapCause(cause => Effect.logWarning("Failed to schedule next run, retrying", cause)), Effect.sandbox, Effect.retry(retryPolicy), Effect.orDie); | |
| }), true).pipe(Effect.annotateLogs({ | |
| module: "effect/cluster/ClusterCron", | |
| name: options.name, | |
| dateTime: request.payload.dateTime | |
| })) | |
| }; | |
| })); | |
| return Layer.merge(InitialRun, EntityLayer); | |
| }; | |
| const retryPolicy = /*#__PURE__*/Schedule.exponential(200, 1.5).pipe(/*#__PURE__*/Schedule.either(/*#__PURE__*/Schedule.spaced("1 minute"))); | |
| class CronPayload extends /*#__PURE__*/Schema.Class("effect/cluster/ClusterCron/CronPayload")({ | |
| dateTime: Schema.DateTimeUtc | |
| }) { | |
| [PrimaryKey.symbol]() { | |
| return ""; | |
| } | |
| [DeliverAt.symbol]() { | |
| return this.dateTime; | |
| } | |
| } | |
| //# sourceMappingURL=ClusterCron.js.map |
Xet Storage Details
- Size:
- 4.26 kB
- Xet hash:
- 471baa583f1cecbf999500a46dbe19e19843a2281b4822ca1a957f48f5e4862f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.