Skip to content

Runs the corpus timers on an Oban queue - #154

Merged
johnnyt merged 1 commit into
mainfrom
sr-uckn-corpus-timers-on-oban
Oct 2, 2026
Merged

johnnyt merged 1 commit into
mainfrom
sr-uckn-corpus-timers-on-oban

Conversation

@johnnyt

@johnnyt johnnyt commented Oct 2, 2026

Copy link
Copy Markdown
Member

What

The routing corpus now runs every delivery case a second time with its timers on a real timer queue. CorpusRunner.run/2 takes timers: :oban: each delayed send is scheduled with StatifierOban.Timer.schedule/3 under its execution's id, each cancel is StatifierOban.Timer.cancel/3, and each due job is fired by draining the Oban queue, reaching its execution through a process-less host's StatifierOban.Timer.Delivery (StatifierRouter.CorpusTimerDelivery). The Oban instance runs in manual testing mode on the suite's own repo and Postgres database. Both runs compare against the same expected.

Two choices in the runner, both test-only:

  • statifier_oban times a job off the wall clock; the runner moves each job's scheduled_at onto its own clock (the time the send was handed over on that clock, plus its delay), so a job falls due when the case's clock says so.
  • The queue is drained one job at a time (with_limit: 1), earliest due first, so a job that a fired send cancels is off the queue before the queue is read again. with_safety: false raises a failing delivery in the test rather than leaving it as a retry.

The delivery cases, enumerated by CorpusRunner.delivery_case_paths/0 (every case under corpus/cases/ but the publish cases): click-then-impression, click-then-orphan-timeout, grace-click-after-expiry, impression-then-click, impression-then-expiry, reaped-address-drop, redelivered-impression, two-clicks-for-one-impression. All eight are green in both run modes.

Dependency

statifier_oban enters ONLY as a test dependency (only: :test); no runtime dependency is added, and nothing under lib/ changes. Ruled by the operator, 2026-10-01.

Before adding it, statifier_oban 0.17.0's Hex requirements were read against this lock: statifier ~> 2.5 (lock: 2.10.0), statifier_persistence ~> 0.13, optional (lock: 0.18.0), telemetry ~> 1.3 (lock: 1.4.2), oban ~> 2.19 (new). git diff origin/main -- mix.lock adds two lines and changes no existing line:

+  "oban": {:hex, :oban, "2.24.1", ...
+  "statifier_oban": {:hex, :statifier_oban, "0.17.0", ...

Oban's job table is one more row in the suite's bootstrap migrations (StatifierRouter.BootstrapMigrations), pinned at Oban's migration V14. The live migration tests own their own schema and do not see it; the isolated stage stays green.

Sabotage

Each mutation was run against the corpus test module, restored from a copy (byte-equal), recompiled, and re-run green:

  1. A dropped cancel: the Oban run mode's cancel made to answer the state without calling Timer.cancel/3. Red on an assertion: impression-then-click, click-then-impression, redelivered-impression, two-clicks-for-one-impression (Oban run mode).
  2. run/2 made to ignore timers:. Red on an assertion: "the Oban run mode fires its timers as jobs on the queue" (no job on the queue); every case green, which is why that test exists.
  3. delivery_case_paths/0 made to keep the publish cases. Red on an assertion: "the Oban run mode runs every case but a publish case".
  4. The job's fire time left on the wall clock. Red on an assertion: seven of the eight delivery cases (click-then-impression stays green) and the job test.

Provenance

  • corpus/README.md (a gated path the file map did not name) gains one paragraph saying the runner plays the host twice, so its "The runner plays the host" section stays a complete description.
  • The fired-step code moved from the runner's private fire/2 into CorpusRunner.step_fired/4, shared by both run modes.

No changelog fragment: a test harness change with no public surface (changelog.d/README.md).

Gate

Full mix quality green on the rebased head (446 of 446 tests, isolated stage green).

Every delivery case now runs a second time with its timers on a real
timer queue: each delayed send is scheduled, each cancel taken and each
due job fired through statifier_oban, on an Oban instance in the
suite's Postgres database. The runner moves each job's fire time onto
its own clock and drains the queue one job at a time, so a job a fired
send cancels is off the queue before it is read again. Both runs
compare against the same expected.

statifier_oban enters as a test-only dependency; mix.lock gains oban
and statifier_oban and moves no existing version. Oban's job table is
one more bootstrap migration, pinned at V14.

Refs: sr-uckn
@johnnyt
johnnyt force-pushed the sr-uckn-corpus-timers-on-oban branch from ad619e2 to 499ae87 Compare October 2, 2026 10:31
@johnnyt
johnnyt merged commit 184d96d into main Oct 2, 2026
1 check passed
@johnnyt
johnnyt deleted the sr-uckn-corpus-timers-on-oban branch October 2, 2026 10:32
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.

1 participant