AddDecisionRecording
Records every decision a train makes against the run that made it, in
trax.decision, as each decision is made, and makes a re-queued run replay the decisions of the
run it repeats. Each decision is also logged. See
Decision Recording and Models.
Signature
public static TraxEffectBuilderWithData AddDecisionRecording(
this TraxEffectBuilderWithData configurationBuilder
)Defined on TraxEffectBuilderWithData, so it comes after a data provider. Called before one, it
is a compile error that says so.
Returns
TraxEffectBuilderWithData, for continued chaining.
Example
services.AddTrax(trax => trax
.AddEffects(effects => effects
.UsePostgres(connectionString)
.AddDecisionRecording()
)
);What it registers
| Service | Lifetime | Role |
|---|---|---|
DecisionJournal | Singleton | Writes each decision as it is made, and serves a replaying run its original's answers |
IDecisionObserver | Singleton | The journal, which Trax.Core awaits for every decision and routing. It is Required, so a failed write fails the step. |
IDecisionReplay | Singleton | The journal, which Trax.Core asks before it asks a decider |
Calling AddDecisionRecording() more than once registers these once.
Remarks
- Each decision is written before the train acts on it, through a short-lived data context from
IDataContextProviderFactoryrather than the run's own, so it never flushes what the run's junctions have tracked. Each track a routing step takes is added, in order, to theroutesof the latest row for that question when it is taken. A run killed mid-way keeps every decision it acted on. - Each decision is stored with the fingerprint Trax.Core reports for it, and a replay hands the fingerprint back with the answer, so an answer given to a different asking of the question is not replayed.
- A decision that cannot be written fails its step before any track is taken, classified
Transient, orPermanentwhen the store refuses the value. A live answer of a type the journal has no stored form for fails its step,Permanent, because it could never be read back. - A shadow's answer cannot cost the live record: a number JSON cannot hold is written as the
string
"NaN","Infinity"or"-Infinity", and an answer of a type with no stored form is recorded with the reason in the shadow's error. - A decider's missing or unfit live answer, which fails its step, is recorded too, with the answer
(null when there was none), the decider, the model and the reason in
refused. A refused row is never replayed, so a requeue of that run asks the question afresh. - Each run's metadata row is marked
DecisionsRecordedon its first write, before any junction. - A decision, refusal, routing or replay lookup that arrives without the run on its async flow,
because code in the run suppressed
ExecutionContextflow, is matched to the run by external id among the runs of that train in progress on this host. Exactly one match is recorded against; otherwise it is logged only. - A run that is not persisted (no metadata row) has its decisions logged and not written. A
decision the run's own train reports under an external id other than the run's, because the
train changed its
ExternalIdwhile running, fails its step,Permanent. - A run whose metadata names an earlier run in
ReplayDecisionsOfloads, before its first junction, the answers of that run and, for questions it never reached, of the runs it replayed in turn, nearest first, up to 32 runs back. It fails there, classifiedPermanent, rather than asking afresh, when a run in that chain does not exist, is a run of another train, or ran without recording its decisions, when the chain loops or goes back further, or when a recorded answer cannot be read; and classifiedTransientwhen the database fails. A question no run in the chain reached is asked afresh. See A requeue of a requeue. - On a host without
AddDecisionRecording(), a run that names a run to replay fails before its first junction, classifiedPermanent, because it has nothing to replay from. - Runs that share an external id, as a retried dispatch's rows can, each write under their own row and replay their own answers.
- A host with no logging registered still records.
HasDecisionsToReplay
// Trax.Effect.Data.Decisions
public static Task<bool> HasDecisionsToReplay(
this IDataContext context,
long metadataId,
CancellationToken cancellationToken
)Whether a requeue of the run metadataId should replay its decisions rather than be queued as an
ordinary run that asks afresh. True when the run recorded a decision it acted on (a refused answer
does not count, since it is never replayed), or was itself queued to
replay another run's: a requeue of a requeue that failed before reaching a question recorded
nothing, but the answers of the run it replayed are still the ones to repeat. False for a run of a
train that never decides, a run on a host that records nothing, and a run that does not exist.
IOperationsService.RequeueExecutionAsync
asks it; a host that requeues runs its own way should too.
Package
dotnet add package Trax.Effect.Data