Adds an asynchronous invoke handler on Oban - #43
Merged
Conversation
The wizard's company-details step is this app's one asynchronous call now: the durable driver answers `:pending`, the work runs as an Oban job on statifier_oban's invoke-handler base, and the answer re-enters the stored run through the driver's completion door. A run therefore rests durably mid-invocation, survives a restart, and completes when the job answers. The onboarding group's abandonment deadline cancels the stored job and routes the chart's own abandon outcome, and a completion arriving after it is discarded. Pins statifier_persistence to a git ref for the ADR-0007 seam until 0.3.0 is published, and gives invoke jobs their own Oban queue. Refs: se-d74
Every test reaches the queue through `AsyncCalls.queue/0`, so a config naming it differently - or not at all - would strand every invocation in the running app while the suite stayed green. The deployment's queue list and the module's own name are now asserted against each other, as mix.exs and mix.lock are. Found by the unattended verify pass on se-d74. Refs: se-d74
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every call in this app was answered inside the step that made it.
cancel/2planned nothing, the timeout fields were decorative, and no invocation
outlived a step - which left the most production-shaped part of the invoke
contract unexercised by the reference embedder.
It has one asynchronous call now.
The beat
The signup wizard's company-details step. Collecting a company's details
is a human step that takes hours, and the chart already surrounds it with the
clock that makes waiting safe: the onboarding group arms
signup.abandonedat the head of its own body and listens for it on its interrupt rail. So a
run resting on that call has a live invocation and an armed deadline, which
is the whole shape this bead was filed for. The step also leaves its state on
the answer, which is the condition
statifier_persistence's ADR-0007 namesin its Consequences for idempotent re-entry.
Which calls a deployment defers is a host's decision, not a fact about
the document:
:pendingisStatifierPersistence.Driver's arm and aStatifier.Sessionhas no such thing, so a live session run of the samechart answers the same call inline and the chart cannot tell the difference.
This app makes that decision in one predicate,
AsyncCalls.async?/2, readper block off the
<param>the step emits.What it does
StatifierExamples.Charts.AsyncCalls- the policy, theStatifierOban.Invoke.Handlerbehaviour implementation (config/0andrun/1), and the enqueue and cancel doors, called from the durabledriver's executor because that is where the whole
%Effect.Invoke{}is inreach and
JobArgsneeds it.StatifierExamples.Charts.AsyncCalls.Delivery- theStatifierOban.Invoke.Deliveryseam. The package's default answersrun-liveness from
Statifier.Session's registry; this app has no sessionprocess, so the answer is the stored run and the persisted position.
Durablegained the:pendingdispatch arm and two cold entry points,complete_invocation/3andfail_invocation/3, shaped exactly like theexisting
deliver/2.a timer delivers an event and is over in milliseconds, an invoke job runs
the host's actual work - and sharing one queue would let a backlog of slow
calls delay every reminder behind it.
Durable.answer/3deliberately carries no:activepre-check of itsown, unlike
deliver/2. The driver reads liveness off the loaded positioninside the run's serialization strategy, so a cancel cannot land between the
read and the step; a pre-check here would answer the same question earlier
and worse. Sabotage confirmed it: with the pre-check present, the
driver-discard path was unreachable.
Evidence
11 new tests in
test/statifier_examples/charts/async_calls_test.exs, eachsabotaged red by hand and reverted from a backup (13 mutations run in all,
including the pin guard and the feed-ordering test the change moved):
:activerecord, the invocation live in acold-decoded persisted position, and the work in a stored job;
chart, position and run out of SQLite - and the run completes and
provisions;
abandon outcome;
error.communication.invoke.<id>to the step'serror outcome.
Five existing tests moved because the shipped wizard now genuinely rests
mid-call, and
docs/demo-script.mdsection 8 was rewritten around the tworeadings the beat now produces. Full gate green: 239 tests, 76.9% coverage,
dialyzer and credo clean.
The dependency pin
statifier_persistencemoves from Hex~> 0.2to an INTERIM git pin on65ef280d77b70c7560fb045ae71e1ec3bc08709d, onemix.lockline, nooverride:needed. 0.2.0 carries the driver but not ADR-0007's seam - the:pendingarm and thedone_invocation/5/failed_invocation/5doors thischange consumes.
FINAL re-pin after the operator publishes statifier_persistence 0.3.0
(
{:statifier_persistence, "~> 0.3"}, retiring@statifier_persistence_refand its guard). Tracked by se-l9s.
test/statifier_examples/dependency_pins_test.exs- a single-test fileguarding the same dependency on its own - is folded into
test/statifier_examples/mix_deps_test.exs, where the other pin guards live,rather than left to contradict it.
A finding for upstream
StatifierOban.Invoke.Handler'srun/1is handed the effect and noscope. The scope is on the job row and the worker reads it to deliver with,
but the work never sees it - so a handler whose work keys on the run cannot
be written against the base as shipped. That is why
myapp:provision, thisapp's one call that writes and keys on the run id, stays synchronous, and why
the asynchronous example is a call whose work is run-independent. Recorded in
AsyncCalls' moduledoc and queued for the operator; nothing instatifier_obanis touched here.Provenance
granted).
consumes; the pin names that repo's main tip.
mainhad not moved from2598cd83.commit and are the reason
config/config.exs,docs/demo-script.mdandtest/statifier_examples_web/live/editor_live_test.exsappear here.