hydra-node
Safe HaskellSafe-Inferred
LanguageGHC2021

Hydra.Node.Outbox

Description

An ordered hand-off for effects that must not block the thread producing them.

Synopsis

Documentation

data StallBounds Source #

Bounds beyond which an Outbox reports itself stalled.

Constructors

StallBounds 

Fields

  • noProgressFor :: DiffTime

    How long the outbox may fail to complete anything while it holds work. Also bounds how long the backlog may take to drain at the rate the consumer has recently been completing work, which catches a consumer that is moving but too slowly for what it holds, sooner than waiting for it to stop outright.

  • maxPending :: Natural

    How much work it may hold, whatever the rate it drains at: the memory backstop. Note it does not itself bound the queue: $sel:submit:Outbox never blocks, so only the producer can stop.

    Both backlog limbs also fire on a producer outrunning a consumer that is still delivering, so a caller reporting them must describe the backlog rather than blame the consumer for being unreachable.

data Outbox m Source #

A single-consumer hand-off for actions that must not block the thread submitting them.

The hydra node processes all its inputs on one thread and used to run every effect inline on it, so an effect that blocked stopped it dequeuing anything at all - including the chain observation and the client command it needs to close and contest a head. See GHSA-3mmr-q43p-g6p2 and withNetworkOutbox.

$sel:submit:Outbox therefore only appends; $sel:runOutbox:Outbox is the one thing that performs the actions, in submission order.

Constructors

Outbox 

Fields

  • submit :: m () -> m ()

    Append an action. Never blocks.

  • outboxStalled :: m (Maybe (StallReason, Natural))

    The cause, and the amount of queued work, once it exceeds the StallBounds.

    Progress-based first, on purpose: depth alone is a normal condition for a consumer that batches or rate-limits, and "non-empty for a while" would misfire on a busy producer whose queue simply never happens to be observed empty. What actually means "this is not getting through" is that nothing has completed - reported as NoProgress even when the backlog also happens to be at $sel:maxPending:StallBounds.

    The backlog itself is judged by how long it would take to drain at the recent completion rate, so a burst that a fast consumer clears within $sel:noProgressFor:StallBounds is not a stall however deep it gets, short of $sel:maxPending:StallBounds. Both are reported as BacklogFull.

  • pendingActions :: STM m Natural

    Submitted but not yet completed, including any action in flight.

  • outboxBacklog :: m (Natural, DiffTime)

    The same count, plus how long since the outbox last completed anything - zero while it holds nothing. Where $sel:outboxStalled:Outbox answers "should we cut the flow off", this is for reporting: it is the pair an operator needs to tell a backlog that is draining from one that is not, and to see which of the two StallBounds limbs is being approached.

  • runOutbox :: m ()

    Perform submitted actions forever, in submission order. Must run concurrently with the producer, or nothing is ever performed.

  • drainOutbox :: m ()

    Perform everything queued right now and return. For producers driven step by step instead of alongside $sel:runOutbox:Outbox. Must not run concurrently with it: two consumers would interleave the actions.

  • stallBounds :: StallBounds

    What this outbox was created with, so a watcher can pick a polling interval that matches.

newOutbox :: (MonadLabelledSTM m, MonadMonotonicTime m) => StallBounds -> String -> m (Outbox m) Source #

serviceWindow :: DiffTime Source #

How much busy time the service time estimate averages over. Long enough that a pause of a few hundred milliseconds barely moves it, short against any $sel:noProgressFor:StallBounds worth configuring.