Interface ICommandIntentStore
- Namespace
- Stratara.Abstractions.Outbox
- Assembly
- Stratara.Abstractions.dll
Where an execution model that records every accepted command keeps the bookkeeping of its resume: the command with the aggregate it names and whether it is heavy, how often it has been handed over, how many of its attempts ended in a concurrency conflict, when it was last handed over, the failure of its last attempt, and whether it has been kept for an operator. The resume is bounded only as far as an implementation persists these; an execution model that requires this port fails at start when none is registered, rather than resuming without a bound.
public interface ICommandIntentStore
- Extension Methods
Methods
ClaimAsync(IReadOnlyList<RecordedIntent>, DateTimeOffset, CancellationToken)
Claims the hand-over of several due commands read together: for each, what TryClaimAsync(Guid, DateTimeOffset?, DateTimeOffset, CancellationToken) does, and returns the ones this call claimed.
Task<IReadOnlyList<Guid>> ClaimAsync(IReadOnlyList<RecordedIntent> due, DateTimeOffset now, CancellationToken cancellationToken)
Parameters
dueIReadOnlyList<RecordedIntent>The due commands, as GetDueAsync(DateTimeOffset, int, CancellationToken) returned them.
nowDateTimeOffsetThe time of this hand-over.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task<IReadOnlyList<Guid>>
The identities of the commands this call claimed.
Remarks
The default claims one command at a time through TryClaimAsync(Guid, DateTimeOffset?, DateTimeOffset, CancellationToken), one round trip each. A store that overrides it claims the batch in a number of round trips that does not grow with the batch, so that a drain resuming a backlog pays per pass, not per command.
GetDueAsync(DateTimeOffset, int, CancellationToken)
Returns the recorded commands due for resumption, oldest first, commands recorded at the same time in the order
of their identity: not kept, recorded at or before
handedOverBefore, and not handed over since then.
Task<IReadOnlyList<RecordedIntent>> GetDueAsync(DateTimeOffset handedOverBefore, int batchSize, CancellationToken cancellationToken)
Parameters
handedOverBeforeDateTimeOffsetThe moment a command's last hand-over must precede for it to be due.
batchSizeintThe maximum number of commands to return.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task<IReadOnlyList<RecordedIntent>>
The due commands, or an empty list.
KeepAsync(Guid, DateTimeOffset, CancellationToken)
Keeps the command for an operator; a kept command is not due until an operator returns it.
Task KeepAsync(Guid intentId, DateTimeOffset now, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
nowDateTimeOffsetThe time it was kept.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the command is kept.
RecordAsync(Guid, CommandEnvelope, Guid?, bool, DateTimeOffset, CancellationToken)
Records an accepted command durably, with the aggregate it names, whether it is heavy, and the time its dispatch began, which orders it among the commands due with it.
Task RecordAsync(Guid intentId, CommandEnvelope envelope, Guid? aggregateId, bool heavy, DateTimeOffset recordedAt, CancellationToken cancellationToken)
Parameters
intentIdGuidThe identity of the recorded command.
envelopeCommandEnvelopeThe serialised command and the session it was dispatched under.
aggregateIdGuid?The aggregate the command names, or null for a command that names none.
heavyboolWhether the command declared itself long-running.
recordedAtDateTimeOffsetWhen the dispatch began, taken before anything was awaited; a later dispatch of the same scope to the same aggregate carries a later time, however long either takes to be recorded.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the command is durable.
Remarks
A store that overrides it counts the hand-over of the dispatch that follows the record as the command's first
attempt, as a bus message's first delivery counts, so a record whose host died before that hand-over has used one
attempt. The default records through the overload without recordedAt, so a store that does not
override it orders commands by the time it stamps them itself and counts from zero, as before.
RecordAsync(Guid, CommandEnvelope, Guid?, bool, CancellationToken)
Records an accepted command durably, with the aggregate it names and whether it is heavy.
Task RecordAsync(Guid intentId, CommandEnvelope envelope, Guid? aggregateId, bool heavy, CancellationToken cancellationToken)
Parameters
intentIdGuidThe identity of the recorded command.
envelopeCommandEnvelopeThe serialised command and the session it was dispatched under.
aggregateIdGuid?The aggregate the command names, or null for a command that names none.
heavyboolWhether the command declared itself long-running.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the command is durable.
RecordConflictAsync(Guid, string, CancellationToken)
Records that the command's latest attempt ended in a concurrency conflict: records the failure, counts one conflict, and gives back the attempt the hand-over counted, never below zero — so that the delivery bound measures failures other than conflicts, and the conflict bound measures conflicts.
Task RecordConflictAsync(Guid intentId, string failure, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
failurestringA description of the conflict.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the conflict is durable.
Remarks
The default records the conflict through RecordFailureAsync(Guid, string, CancellationToken), so a store that does not override it counts a conflict against the delivery bound like any other failure.
Exceptions
- ArgumentNullException
failureis null.
RecordFailureAsync(Guid, string, CancellationToken)
Records the failure of the command's latest attempt.
Task RecordFailureAsync(Guid intentId, string failure, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
failurestringA description of the failure.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the failure is durable.
RenewAsync(Guid, DateTimeOffset, CancellationToken)
Renews the hand-over of a command whose handler is running, so it does not become due again while it runs.
Task RenewAsync(Guid intentId, DateTimeOffset now, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
nowDateTimeOffsetThe time of the renewal.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the renewal is durable.
ReturnAttemptAsync(Guid, CancellationToken)
Gives back the attempt the command's latest hand-over counted, never below zero, because its handler was stopped with its silo rather than failing.
Task ReturnAttemptAsync(Guid intentId, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task
A task that completes when the attempt is given back.
Remarks
The default gives nothing back, so a store that does not override it counts the stop as an attempt.
TryClaimAsync(Guid, DateTimeOffset?, DateTimeOffset, CancellationToken)
Claims a hand-over of a due command: stamps now as its last hand-over and
increments its attempt count, only if it is not kept and its last hand-over is still
expectedLastHandedOverAt.
Task<bool> TryClaimAsync(Guid intentId, DateTimeOffset? expectedLastHandedOverAt, DateTimeOffset now, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
expectedLastHandedOverAtDateTimeOffset?The last hand-over read with the command, or null for one never handed over.
nowDateTimeOffsetThe time of this hand-over.
cancellationTokenCancellationTokenCancels the operation.
Returns
TryRenewAsync(Guid, DateTimeOffset, CancellationToken)
Renews the hand-over of a command that is still recorded and not kept, and reports whether there was one: a late hand-over that finds the command gone — it completed through another run — or kept is not run.
Task<bool> TryRenewAsync(Guid intentId, DateTimeOffset now, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
nowDateTimeOffsetThe time of the renewal.
cancellationTokenCancellationTokenCancels the operation.
Returns
Remarks
The default renews through RenewAsync(Guid, DateTimeOffset, CancellationToken) and returns true, so a store that does not override it runs every such hand-over.
TryRenewFromAsync(Guid, DateTimeOffset, DateTimeOffset, CancellationToken)
Takes over a resumed hand-over: renews it only if the command is not kept and its last hand-over is still
claimedAt, the stamp of the claim that handed it over, and moves that stamp to
now or, where now is not later, one millisecond past
claimedAt. A second hand-over of the same claim therefore finds the stamp moved, and a
hand-over of a command that completed finds no record.
Task<bool> TryRenewFromAsync(Guid intentId, DateTimeOffset claimedAt, DateTimeOffset now, CancellationToken cancellationToken)
Parameters
intentIdGuidThe recorded command.
claimedAtDateTimeOffsetThe last hand-over the claim stamped, as the hand-over carries it.
nowDateTimeOffsetThe time of the renewal.
cancellationTokenCancellationTokenCancels the operation.
Returns
- Task<bool>
true when this call took the hand-over over; false when another runner already did or the command is gone, and the hand-over is to be dropped without running the handler.
Remarks
The default renews through RenewAsync(Guid, DateTimeOffset, CancellationToken) and returns true, so a store that does not override it runs every hand-over it receives.