Skip to content

feat(dispatch): result landing complete/abort/JE convergence (slice 3/6) - #358

Open
as535364 wants to merge 3 commits into
mainfrom
feat/dispatch-result-landing
Open

feat(dispatch): result landing complete/abort/JE convergence (slice 3/6)#358
as535364 wants to merge 3 commits into
mainfrom
feat/dispatch-result-landing

Conversation

@as535364

@as535364 as535364 commented Jul 24, 2026

Copy link
Copy Markdown
Member

closes Normal-OJ/Normal-OJ#63

Slice 3/6 of the pull-based dispatch rework (spec: docs/specs/pull-based-job-dispatch.md in the meta-repo, §6/§7.4/§7.5/§9; ADR-0003). Dark PR — nothing calls these functions yet.

What

  • Four new Lua scripts, one per state transition: complete_begin (ownership → currency → mark completing), complete_finish (delete-after-write cleanup with compare-and-delete on the current_job pointer), complete_restore (undo completing after a failed Mongo write, never stomps a reclaimed-away job), abort_requeue (requeue with per-reason counting).
  • complete_job / abort_job in dispatch/job.py, returning outcome enums; HTTP status mapping is documented for slice 4.
  • Per-submission lock (redis-py Lock, non-blocking) around every landing (INV3).
  • JE convergence: attempts-exhausted jobs are swept by the orphan scan in claim_next_job; a failed JE write leaves the job in Redis for the next time-gated sweep (INV1, ADR-0003).
  • Mongo writers are injected callables (land_result(sid) / land_je(sid)); the keystone slice wires process_result and a JE marker.

Note for reviewers: drain refunds one attempt

Merged slice 2 counts attempts on every claim. If drain only skipped counting, each drain → requeue → re-claim cycle would still burn one attempt, so rolling restarts would erode a job's retry budget — against spec §7.5 ("drain must not shorten a job's life"). The drain branch therefore refunds the claim-time bump (floored at 0): a claim+drain round trip nets zero. Poison paths (prep_failed/rejected/reclaim) are unchanged, so INV5 convergence still holds.

Tests

tests/test_dispatch_landing.py (26 tests): INV1 (restore on failed landing, JE-retry after Mongo failure), INV3 (concurrent completes, reclaim-during-write, lock contention), INV4 (stale drop + log, rejudge-during-write pointer safety), abort matrix (3 reasons × attempt boundaries), JE sweep via scan, corrupted-hash reaping. Slice-2 suite untouched and green; full suite 585 passed.

Review scope

This review is scoped to dark slice Normal-OJ/Normal-OJ#63 only. Known pre-keystone landing debts are tracked by #359 and #360; both natively block keystone Normal-OJ/Normal-OJ#66.

…ce 3/6)

complete: completing state + per-submission lock + delete-after-write
(INV1/INV3/INV4). abort: requeue with per-reason counting; drain refunds
the claim-time attempts bump so a claim+drain round trip nets zero
(spec 7.5). Attempts-exhausted jobs converge to JE via the orphan scan;
a failed JE landing leaves the job in Redis for the next sweep
(ADR-0003). Mongo writers are injected callables; wiring lands in the
keystone slice.

@as535364 as535364 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

codex: review completed; actionable findings are tracked in the inline threads below.

Comment thread dispatch/job.py Outdated
Comment thread dispatch/job.py
as535364 added 2 commits July 25, 2026 02:01
…ites

A Mongo landing has no completion bound, so a fixed lock TTL cannot hold
INV3: once the lock expired mid-write, a rival complete could acquire it
and land the same result twice (PR #358 review). A watchdog thread now
extends the lock every TTL/3 for the whole landing (token lives on the
Lock instance, not thread-local, so the watchdog thread may extend); the
TTL only bounds crash recovery. Also pin the at-least-once landing
contract at the seam: land_result must tolerate full re-invocation after
a partial write (spec 17.2); mongo-side idempotency stays the keystone's
pre-wiring task.
An extend failure means the lock key itself evaporated; this layer cannot
abort a black-box landing already in flight, so the residual double-write
window closes on the mongo side (fencing token / conditional write), as a
pre-keystone task.
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.

backend: result landing — complete/abort/JE convergence (slice 3/6)

1 participant