R4.02.1 · Schedulers — dive 1
Naming "when next": cron vs interval.
A cron expression names a wall-clock calendar position; a fixed interval names a gap. Both reduce to one millisecond timestamp — the score the registry stores.
Grounding: echo/apps/echo_mq · repeat.ex (EchoMQ.Repeat, the interval form) · the
every_ms field on the emq:{q}:repeat:<name> record hash.
Static verdicts (now = 08:50) — cron 0 9 * * * → next run 09:00,
a wall-clock slot · interval every_ms 900000 → next run 09:05, the previous run plus 15
minutes. Both yield one future millisecond.
The cron expression — a wall-clock slot
delayed-queue.md.txt · applied to recurrenceA cron expression is five fields — minute, hour, day-of-month, month, day-of-week. 0 9 * * *
reads "minute 0, hour 9, any day, any month, any weekday" — 09:00 every day. The next run is the earliest
calendar slot at or after the current time that the expression matches.
Cron anchors to the wall clock, so a daily-at-09:00 schedule fires at 09:00 regardless of when it last ran or how long the last run took. The rule answers which calendar slot, not how long a gap.
- 0 9 * * *
- Minute 0, hour 9, any day — 09:00 daily.
- next run
- The earliest matching slot at or after now.
- anchor
- The wall clock; a slow run does not move the next slot.
A cron rule snaps the next run to a calendar position. The output is still a single future millisecond — the same kind of value an interval produces.
The fixed interval — a gap
repeat.ex · every_msA fixed interval is a single number of milliseconds — every_ms: 900000 is fifteen minutes.
The next run is the previous run plus the gap. An interval anchors to its own previous run rather than to the
calendar, so an interval-every-15-min schedule fires at :05, :20, :35 past whatever minute it started on,
marching forward by the gap.
EchoMQ.Repeat is exactly this form: registration takes every_ms, the record hash
stores it, and the pump advances the score by it each cycle. The two rules compute differently but produce the
same kind of value — a future millisecond, the score ZADD'd into the registry against the
registration's member. The registry does not separate a calendar match from a gap addition; it stores a sorted
set of next-run times and the pump reads the earliest.
Naming the cadence is the only place the two rules differ. Downstream, a cron schedule and an interval schedule are the same one-entry-scored-by-next-run row.
In EchoMQ — every_ms on the record hash
repeat.ex · @registerEchoMQ.Repeat.register/6 takes a period in milliseconds and a payload template, and writes both
the registry entry and a companion record hash in one inline Lua script. The script's first move is the
idempotence probe; on a fresh name it writes the record then the registry score.
# @register (EchoMQ.Repeat) — KEYS[1] = the registry ZSET, KEYS[2] = the record hash. if redis.call('EXISTS', KEYS[2]) == 1 then return 0 end -- already registered redis.call("HSET", KEYS[2], "every_ms", ARGV[2], "template", ARGV[3]) # the cadence on the hash redis.call("ZADD", KEYS[1], tonumber(ARGV[4]), ARGV[1]) # the next run on the registry # ARGV[2] every_ms · ARGV[3] template · ARGV[4] first-run millis · ARGV[1] registration name
KEYS[1] is emq:{q}:repeat (the registry sorted set), KEYS[2] is
emq:{q}:repeat:<name> (the record hash). ARGV[2] is every_ms —
the interval — and ARGV[3] is the payload template; ARGV[4] is the
first run's millisecond, the registry score. The interval lives on the record hash; the registry sorted set
stores only the computed next-run time.
Notes on ValkeyHSET writes the record fields in one command,
and the registry ZADD scores the name by a single number, so the cadence and its next-run time live
in two declared keys, both on the queue's slot —
valkey.io/commands/hset.
Where cron parsing lives
The cron-parser depth — how a calendar pattern plus a timezone resolves a slot across daylight-saving
transitions — belongs to the scheduler subsystem the EchoMQ course covers. EchoMQ.Repeat
ships the interval form; the Queue pillar teaches the controls that maintain the registry around it. See
/echomq/queue.
Name "when next" two ways — a wall-clock cron slot or a fixed gap. Both reduce to one future millisecond, the registry score.
EchoMQ.Repeat is the interval form: register/6 stores every_ms on the
record hash and the next-run time on emq:{q}:repeat.
A textbook delayed queue scores a job by one fixed fire-time. A scheduler computes that fire-time from a cadence rule each cycle; the registry stores the output, never the rule.
Recap — two rules, one timestamp
next → R4.02.2Cron names a calendar slot; an interval names a gap. Each computes a future millisecond, and that single
number is the score the registry holds against the registration's member. EchoMQ.Repeat carries
the interval rule as every_ms. The next dive keeps that single entry unique across boots —
the EXISTS-guarded register.
References
sources · relatedSources
- Cron — Wikipedia — the five-field cron expression as a calendar cadence spec.
- Redis — ZADD — writes the computed next-run millisecond as the registry score.
- Valkey — Sorted sets — the registry of next-run times both cadences feed.
- Redis — HSET — the record hash carrying
every_msand the payload template.
Related in this course
- R4.02 · Schedulersthe module hub
- R4.02.2 · The upsertthe next dive
- R4 · Time, Delay & Prioritythe chapter
- /echomq/queuecron parsing and the scheduling controls in depth
- /elixirthe functional-Elixir craft behind the bus