Skip to content

Derive unique periods from UTC scheduled time - #1377

Merged
bgentry merged 2 commits into
masterfrom
bg/unique-by-period-utc
Sep 24, 2026
Merged

bgentry merged 2 commits into
masterfrom
bg/unique-by-period-utc

Conversation

@bgentry

@bgentry bgentry commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

UniqueOpts.ByPeriod promises "at most one of these jobs per period", for example one daily digest per user per day. Two bugs could break that promise and let duplicates through.

Scheduled jobs were bucketed by when they were inserted, not when they run. Say you insert a daily digest at 10am today with ScheduledAt set to 9am tomorrow, and then the same digest is inserted again at 8am tomorrow, also for 9am tomorrow. Both jobs are meant to run at the same moment, but the first was keyed to today's period and the second to tomorrow's, so River saw them as different and ran the digest twice.

The period's timezone leaked into the unique key. The start of the period was written into the key using the local timezone of whichever process inserted the job. A server running in America/Chicago wrote the start of a day-long period as 2026-09-23T19:00:00-05:00, while a server running in UTC wrote the same instant as 2026-09-24T00:00:00Z. The text differed, so the keys differed, and both servers could insert the "unique" job. The same happened when a caller passed a ScheduledAt in a non-UTC zone.

This change computes the unique key only after the job's effective scheduled time is known, so a scheduled job is deduplicated against other jobs scheduled in the same period. It also always writes the period in UTC. Periods were already calculated on absolute time, so which jobs fall into which period is unchanged; only the way the period is written into the key is fixed.

Because unique keys change for scheduled ByPeriod jobs and for any ByPeriod job inserted from a process whose timezone isn't UTC, an old and a new client running side by side during a rolling upgrade may each insert one copy of such a job for the same period. Jobs that aren't scheduled and are inserted from UTC processes keep exactly the same keys. The changelog calls this out.

`UniqueOpts.ByPeriod` keys are computed before an insert's effective
`ScheduledAt` is applied, so a job scheduled for tomorrow is keyed by
today's period. Re-enqueueing "tomorrow at 9am" at different times of
day therefore inserts duplicates, and the key disagrees with the period
the job actually runs in.

The period bound is also formatted in the time's own location. A
process whose local time zone isn't UTC, or a caller passing a
`ScheduledAt` in another zone, embeds an offset like `-05:00` in the
key, so two processes in different zones compute different keys for
the same period and both insert.

Compute the unique key after the effective scheduled time is known,
and normalize the period bound to UTC before formatting it. Truncation
already operates on absolute time, so only the textual form changes.
Document both behaviors on `ByPeriod` and add a changelog entry that
notes the one-time key change for scheduled jobs and non-UTC processes
during a rolling upgrade.

Cover non-UTC clocks, non-UTC `ScheduledAt`, and zone-independent
period boundaries in key construction; scheduled jobs deduplicating by
scheduled period across insertion times while unscheduled and
next-period jobs don't conflict; and inserts in different zones
deduplicating end to end across every driver.
@bgentry
bgentry force-pushed the bg/unique-by-period-utc branch from 0416292 to 4ff1b65 Compare September 24, 2026 13:36
`JobGetAvailable_LargeMetadata` uses the same nanosecond timestamp for
insertion and fetch. PostgreSQL stores timestamps at microsecond
precision, so rounding can place the scheduled time after the fetch
cutoff and make the benchmark intermittently fetch no job.

Truncate the benchmark timestamp to microseconds before insertion so
the stored scheduled time matches the fetch cutoff exactly.
@bgentry
bgentry marked this pull request as ready for review September 24, 2026 15:57
@bgentry
bgentry merged commit 716d134 into master Sep 24, 2026
15 checks passed
@bgentry
bgentry deleted the bg/unique-by-period-utc branch September 24, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants