Verglas schedules can start in the past. The scheduler gives every invocation
its logical job time, runs historical instances with bounded concurrency, and
keeps the current cron schedule moving while catch-up is still in progress.
This daily Worker starts with January 1, 2024, permits four live and historical
instances at a time, and sends its results into the market_prices Stream:
start_date is an inclusive UTC RFC 3339 timestamp. It enables catch-up for
cron occurrences before deployment. max_concurrent is an integer from 1 to
32 and limits the number of instances dispatched together. A plain cron string
such as "0 0 * * *" retains normal forward-only scheduling.
Use logical job time
Always partition scheduled work with controller.scheduledTime, not the wall
clock. During catch-up it is the historical deadline being processed; for a
current run it is the ordinary cron deadline.
The Pipeline binding receives the historical job_time just like any other
record field. Use a deterministic identity such as (symbol, job_time) in the
downstream model: a Worker can be retried after it durably appends a record but
before it acknowledges completion.
How live and catch-up runs interact
Each schedule has two durable cursors:
- The live cursor starts at the first cron deadline after deployment and always
gets the first available concurrency slot.
- The catch-up cursor advances from
start_date toward the live cursor’s
initial boundary in bounded batches.
The cursors share an idempotent execution ledger but advance independently. A
failed historical date is retried without moving the live cursor backward, so
today’s run does not wait for an older backlog to finish.
max_concurrent controls pressure, not ordering. Historical invocations in
one batch may finish in any order. Do not make one partition depend on side
effects from the previous partition.