Skip to content

Words the parked-timer bounds as the code has them - #135

Merged
johnnyt merged 1 commit into
mainfrom
sob-em4-parked-timer-contract-wording
Sep 28, 2026
Merged

johnnyt merged 1 commit into
mainfrom
sob-em4-parked-timer-contract-wording

Conversation

@johnnyt

@johnnyt johnnyt commented Sep 28, 2026

Copy link
Copy Markdown
Member

Three wording fixes to the parked-timer contract, no behaviour change. Refs: sob-em4.

What changes

  • The exhaustion window. The StatifierOban.Timer.Delivery moduledoc section "A parked execution retries; it is never discarded" and the README's matching paragraph said Oban's default backoff spreads the default 20 attempts "over about twelve days". Both now say twelve to thirteen and a half days, and show where the range comes from: the wait after attempt n is 15 + 2^n seconds plus a random 0-10% jitter.
  • The replay condition. "Scheduling the same effect again does not revive it" is true only while the discarded job row survives, because that row holds the timer's dedup guard. Both texts now state that condition, and that once the pruner has removed the row, scheduling the same effect again inserts a fresh job.
  • The test stand-in's unpark/1. Its doc said it works "as unpark/3 does". It accepts only a parked scope, and on any other scope a match inside the store fails and the call exits. statifier_persistence's unpark/3 answers an :active execution unchanged. The doc now describes what the stand-in does and why it is stricter. The code is unchanged (test support only).

Review (in-turn, gate tier)

I read each claim against the locked dependencies. The backoff: Oban.Worker.backoff/1 in the locked Oban 2.23.1 clamps the attempt (a no-op at max_attempts 20, the Oban.Job default, which the timer worker does not override), then calls Oban.Backoff.exponential(attempt, mult: 1, max_pow: 100, min_pad: 15), which gives 15 + 2^n. After that it calls Oban.Backoff.jitter(mode: :inc), which adds trunc(rand * 0.1 * time), so 0 to under 10%. Attempts 1 to 19 each schedule a retry, and the 20th is discarded. So the floor is 19 * 15 + (2^20 - 2) = 285 + 1,048,574 = 1,048,859 s, about 12.14 days. The ceiling adds at most 10% to each wait: 1,153,735 s, about 13.35 days. The docs round these to "about 12.1" and "about 13.4", and the range to "twelve to thirteen and a half days". The replay condition: StatifierOban.Timer.Worker's unique options cover every Oban state with an infinite period, so a surviving discarded row absorbs the insert. Once the row is pruned there is nothing left to conflict with. The stand-in: I checked the exit with a throwaway test that was not committed. unpark/1 on an :active scope exits with {:badmatch, :active} from the store. statifier_persistence's unpark/3 doc says an :active execution answers {:ok, execution} with nothing written, and a terminal one answers {:discarded, execution}.

Gate

Full mix quality is green on the committed tree (305 tests, credo, docs, dialyzer). No changelog fragment: this is documentation and test support, which changelog.d/README.md excludes.

Provenance

The docs describe today's behaviour only. statifier_oban ADR-0010 (proposed) would later change what the parked paragraph says. ADR-0010's context repeats the old "about twelve days" when it quotes this section. This PR leaves the record untouched and lists that sentence as follow-up.

Three wording fixes to the parked-timer contract, no behaviour change.

The StatifierOban.Timer.Delivery moduledoc section "A parked execution
retries; it is never discarded" and the README's matching paragraph
gave the exhaustion window as "about twelve days". Oban's default
backoff waits 15 + 2^n seconds after attempt n plus a random 0-10%
jitter, so the 19 retries of the default 20 attempts sum to about 12.1
days with no jitter and about 13.4 with the most; both now say twelve
to thirteen and a half days and show the arithmetic.

"Scheduling the same effect again does not revive it" holds only while
the discarded job row survives, since that row is the dedup guard;
both now say so, and that once the pruner removes the row a repeated
schedule inserts a fresh job.

The test stand-in's unpark/1 said it works as unpark/3 does, but it
accepts only a parked scope and exits on any other, while
statifier_persistence's unpark/3 answers an active execution
unchanged. Its doc now says what it does.

No changelog fragment (documentation and test support, per
changelog.d/README.md). Full mix quality green on this tree.

Refs: sob-em4
@johnnyt
johnnyt merged commit 3768911 into main Sep 28, 2026
1 check passed
@johnnyt
johnnyt deleted the sob-em4-parked-timer-contract-wording branch September 28, 2026 06:14
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