TimerRunner
Fires the timers of one log group of one bounded context in the partitions this node owns: the node's TimerLoop reads them from store and sends them, the runner holds what it loaded and what is being sent per partition (startPartition, stopPartition, driven by the partition leases, me.ahoo.wow.runtime.cluster.PartitionOwnership), so a timer is sent by the owner of its owner's partition. A new owner reads the store again: nothing is handed over.
Sending one: from the row as loaded, with no read of it again. One due longer ago than TimerOptions.expiresAfter is cancelled as EXPIRED. Otherwise the runner first claims it with one conditional write from the loaded row (PENDING to FIRING, due again at the claim's deadline, TimerOptions.claimTimeout): a timer cancelled or rescheduled after it was loaded fails the claim and is not sent. Claimed, firer sends it, its request id timer#slot, and its outcome becomes a conditional change of the claimed row (TimerChange), written with the others of the round in one batch (flush). A claim left by a node that died lapses at its deadline, and the next owner of the partition claims and sends the timer again, deduplicated by its request id:
committed, already committed (a duplicate: sent before, whose outcome was not recorded), unchanged or rejected: its slot fired; a rule due once is then FIRED, a recurring one PENDING at its next slot, back in the wheel. A rejection is the target's state guard doing its work, not a failure: it is neither retried nor handed to
@OnRejected;sent to another context's command topic: FIRED with the outcome
SENTonce the broker acknowledged it;a technical failure (a contended or unknown outcome, a failing store, no transport, a target whose clock is too far behind, me.ahoo.wow.runtime.wait.ResultCodes.SCHEDULE_NOT_DUE): the timer stays PENDING, due again after TimerOptions.retry's backoff, until its attempts are spent; its target is then recorded in the failure ledger under TimerOptions.PARKED_SUBSCRIPTION and the timer PARKED, in that order, for an operator to retry or skip. A ledger that fails to record it leaves the timer PENDING, its last attempt sent again after the backoff;
refused as busy (me.ahoo.wow.runtime.wait.ResultCodes.BUSY): backpressure, not a failure: the timer stays PENDING, due again after TimerOptions.retry's first backoff, its attempts as they were, up to TimerOptions.maxBusyRetries times in a row on this node; one refusal more counts as a failed attempt, so a target that stays busy parks the timer in the end, as a subscription's backpressure does.
A claim, a send's retry or an operator's retry moves only the timer's Timer.dueAt: every send of the slot carries the slot's own due time (Timer.slotDueAt), so its target never decides it before then. A recurring timer whose slot is past due by more than one time of its rule when it is claimed is caught up first (ScheduleSlots.caughtUp): a coalescing rule sends only the latest slot due, the others counted as skipped; an expired slot of a recurring rule moves on to its next slot instead of cancelling the timer.
At least once, applied once: a crash after sending and before writing sends the timer again, and its target finds the request id committed already.
Constructors
Properties
What the ledger parks this context's targets under: its TimerOptions.PARKED_SUBSCRIPTION.
The partitions whose timers this runner sends now.
Functions
Sends the PARKED timers of target again, due now with their attempts reset, and resolves its ledger entry: an operator's retry. Any node may call it; the owner of the timers' partition sends them.
Cancels the PARKED timers of target with the outcome SKIPPED and resolves its ledger entry: an operator's skip.
Starts sending the timers of partition, which the node has just acquired; nothing when it does already.
Stops sending the timers of partition: the node hands it off. A command being sent is cancelled; if it reached its target, the new owner sends it again and the target finds it committed. What was sent is written first.