feat: implement cron window spread backend (#4566)

- New DB fields on Schedule and ScheduleInstance
- Use `queueTimestamp` for the "effectiveAt" delayed start time,
propagate it to Clickhouse TaskRun table
- Disable fastpath for delayed jobs
- Add schedule timing logic, API endpoints with windows, persistence
- Calculate phase for every schedule, only persist when window is
non-null
- Additional o11y for phased rollout
This commit is contained in:
Chris Arderne
2026-08-12 12:24:32 +01:00
committed by GitHub
parent 3c5bbc1607
commit ed1bb72fb8
43 changed files with 2137 additions and 89 deletions
+9
View File
@@ -10,6 +10,7 @@ import {
import { BackgroundWorkerMetadata } from "./resources.js";
import { DequeuedMessage, MachineResources } from "./runEngine.js";
import { QueueTypeName } from "./queues.js";
import { ScheduleWindow } from "./schemas.js";
export const RunEngineVersion = z.union([z.literal("V1"), z.literal("V2")]);
@@ -1045,6 +1046,13 @@ export const CreateScheduleOptions = z.object({
*
*/
timezone: z.string().optional(),
/** Optionally delay each occurrence by a stable amount within this window.
* Absolute windows use whole minutes or hours up to 24 hours and are capped at the next
* nominal interval. Percentages are relative to each nominal interval.
*
* @example "30m", "2h", "24h", "30%", "100%"
*/
window: ScheduleWindow.optional(),
});
export type CreateScheduleOptions = z.infer<typeof CreateScheduleOptions>;
@@ -1070,6 +1078,7 @@ export const ScheduleObject = z.object({
externalId: z.string().nullish(),
generator: ScheduleGenerator,
timezone: z.string(),
window: ScheduleWindow.optional(),
nextRun: z.coerce.date().nullish(),
environments: z.array(
z.object({
+10
View File
@@ -174,10 +174,20 @@ export const QueueManifest = z.object({
export type QueueManifest = z.infer<typeof QueueManifest>;
/**
* A delay window after a nominal cron tick.
*
* The server's schedule timing domain validates and normalizes the public syntax.
*/
export const ScheduleWindow = z.string().min(1);
export type ScheduleWindow = z.infer<typeof ScheduleWindow>;
export const ScheduleMetadata = z.object({
cron: z.string(),
timezone: z.string(),
environments: z.array(EnvironmentType).optional(),
window: ScheduleWindow.optional(),
});
const AgentConfig = z.object({