STANDARDwalkthrough

Recurring Tasks: Materialize Only the Next Instance

4 of 8
3 related
"Every weekday at 9am" is not a task: it is a factory for infinite tasks, and storing infinity is a bug. The naive expansions both fail: materializing a year of instances up front floods the store with 260 rows per cron (times millions of crons) and makes editing the schedule a mass-update; expanding lazily at query time rebuilds the polling problem.
When that instance fires, the firing path computes the template's next occurrence and materializes it into its future bucket: a self-perpetuating chain with exactly one pending row per recurring task, ever. Edits become trivial: update the template, delete the one materialized instance, re-materialize.
The production pattern: store the template (cron expression, timezone, handler, payload) once, and materialize exactly one instance: the next firing: into the appropriate time bucket.
Three subtleties earn the senior nod. Timezones and DST: "9am New York" is not a fixed UTC offset: the next-occurrence computation must run in the template's zone, and the DST-gap cases (2:30am does not exist one night a year; exists twice another) need an explicit policy (skip, or fire at the adjusted instant): saying "store cron in UTC" out loud is how candidates fail this. Catch-up policy: if the system was down (or a handler was paused) across three scheduled firings, does the task fire three times, once, or zero on recovery? There is no universal answer: a billing job wants all three; a cache-warm job wants only the latest: so the template carries a misfire policy (fire-all, fire-once, skip), which is precisely what Quartz calls it. Drift semantics: for intervals ("every 4 hours"), does the next firing anchor to the schedule (fixed grid, lateness does not shift the chain) or to the completion (guaranteed gap between runs)?
Payment retries want completion-relative; report generation wants schedule-relative: another template field, another judgment call surfaced instead of buried. What if the interviewer asks: what limits does this put on scale?
One pending instance per template means 10M recurring tasks cost 10M pending rows: nothing: the design makes recurring tasks exactly as cheap as one-shot ones.
Why it matters in interviews
Materialize-next-only is the pattern that keeps infinite schedules finite, and the three policy knobs: DST handling, misfire policy, drift anchor: are where scheduling stops being an algorithm and becomes a product contract. Naming Quartz's misfire vocabulary signals you have met the real problem.
Related concepts