コンテンツにスキップ
日本語

BoundedMailbox

このコンテンツはまだ日本語訳がありません。

Defined in: src/mailbox/BoundedMailbox.ts:15

Mailbox with a fixed upper bound on queued user messages. Policy for what happens when a message arrives on a full mailbox is configurable.

T = unknown

new BoundedMailbox<T>(options): BoundedMailbox<T>

Defined in: src/mailbox/BoundedMailbox.ts:19

BoundedMailboxOptions

BoundedMailbox<T>

DroppingMailbox<T>.constructor

readonly deadLetterDrops: boolean

Defined in: src/mailbox/DroppingMailbox.ts:71

See DropReportingMailbox.deadLetterDrops. Fixed at construction because the cell reads it once, when it registers its observer: a switch a running mailbox could flip would mean re-reading it per dropped message, on the path this class exists to keep cheap.

DroppingMailbox.deadLetterDrops


droppedCount: number = 0

Defined in: src/mailbox/DroppingMailbox.ts:63

Number of messages dropped by the overflow policy — useful for metrics.

DroppingMailbox.droppedCount

get size(): number

Defined in: src/internal/Mailbox.ts:406

Number of pending user messages.

number

DroppingMailbox.size


get suspended(): boolean

Defined in: src/internal/Mailbox.ts:237

boolean

DroppingMailbox.suspended

dequeueSystem(): Envelope<unknown> | undefined

Defined in: src/internal/Mailbox.ts:395

Envelope<unknown> | undefined

DroppingMailbox.dequeueSystem


dequeueUser(): Envelope<T> | undefined

Defined in: src/internal/Mailbox.ts:300

Envelope<T> | undefined

DroppingMailbox.dequeueUser


drainSystem(): Envelope<unknown>[]

Defined in: src/internal/Mailbox.ts:424

Envelope<unknown>[]

DroppingMailbox.drainSystem


drainUser(): Envelope<T>[]

Defined in: src/internal/Mailbox.ts:420

Drain all user messages; returns them so the caller can forward to dead letters.

Materialises a fresh array rather than handing out the backing store — a ring is not a dense array, so there is nothing to hand out. The allocation is real but it is on the termination path, where the caller (ActorCell) only iterates the result once.

Envelope<T>[]

DroppingMailbox.drainUser


enqueue(env): void

Defined in: src/mailbox/BoundedMailbox.ts:48

A switch rather than a match, deliberately.

The pattern-matching convention governs dispatch of an incoming message, event or command; overflow is none of those — it is a configuration value fixed at construction, and this is the shape the codebase already uses for a closed string-literal union (see decodeCrdt in crdt/DistributedData.ts, which documents itself as the reference).

It matters here because of when this branch runs. A matcher plus one closure per arm was being built for every message that arrived at a full mailbox — that is, once per shed message, at the exact moment the system is already past its capacity and least able to afford it (#974). Exhaustiveness is not lost, only moved: the never assignment below fails to compile if a fourth policy is added without an arm here.

Envelope<T>

void

DroppingMailbox.enqueue


enqueueSignal(env): void

Defined in: src/mailbox/BoundedMailbox.ts:101

A death notification is queued whatever the bound says — see Mailbox.enqueueSignal.

Straight to the base queue, past the capacity check, and that is the whole override: every one of the three policies destroyed the notification otherwise, each in its own way (#729). drop-head evicted whatever sat at the front, drop-new discarded the notification on arrival — the more likely of the two, since a Terminated arrives late relative to the flood that filled the queue — and reject was worse than either: it threw MailboxFullError synchronously on the sender’s stack, and the sender is the dying cell’s own watcher-notify loop. That throw escaped finalizeTermination mid-loop, so the remaining watchers went unnotified, the parent was never told the child had stopped, and terminate() never settled. reject is also this class’s constructor default, so the documented withMailbox(() => new BoundedMailbox({ capacity: n })) shape reached it without naming it.

Envelope<T>

void

DroppingMailbox.enqueueSignal


enqueueSystem(env): void

Defined in: src/internal/Mailbox.ts:296

Envelope<unknown>

void

DroppingMailbox.enqueueSystem


hasMessages(): boolean

Defined in: src/internal/Mailbox.ts:399

boolean

DroppingMailbox.hasMessages


hasSystemMessages(): boolean

Defined in: src/internal/Mailbox.ts:403

boolean

DroppingMailbox.hasSystemMessages


hasUserMessages(): boolean

Defined in: src/internal/Mailbox.ts:402

boolean

DroppingMailbox.hasUserMessages


observeDrops(observer): void

Defined in: src/mailbox/DroppingMailbox.ts:84

See DropReportingMailbox.observeDrops — additive.

MailboxDropObserver<T>

void

DroppingMailbox.observeDrops


prependUser(envelopes): void

Defined in: src/mailbox/BoundedMailbox.ts:152

A replay meets the bound, the same way an arrival does (#772).

Until this override existed the base prependUser wrote straight to the queue, so unstashAll() unshifted a whole stash — up to DEFAULT_STASH_CAPACITY envelopes — past the capacity check, past the switch above and past the drop accounting. A reject mailbox never threw, a drop-head / drop-new mailbox never dropped, and droppedCount / actor_mailbox_dropped_total under-reported by exactly the batch. The advertised memory ceiling was not one: capacity: 10 could become 1034.

Which end sheds. The policy is unchanged, only the geometry is: an arrival lands at the tail and drop-head makes room at the head, a replay lands at the head and so makes room at the tail. Both read as “admit the arrival, evict a queued message from the far end”, and the other choice is indefensible here — evicting the head under a prepend would discard the messages the replay just put back, which is not a bound but a way of making unstashAll() a no-op.

Which reason is reported. drop-head when a queued message was evicted, drop-new when the arrival itself was refused — the closed two-value vocabulary of MailboxDropReason, applied by what actually happened rather than by which policy is configured. So a drop-head mailbox does report drop-new for the tail of a batch bigger than its capacity: once the queue holds nothing droppable, there is no room to make and the arrival is what goes. That is where this diverges from enqueue, which admits and overshoots by one in the same situation — one envelope over a bound is the arrival rate, a whole stash over it is the defect.

What reject does. It throws MailboxFullError, and it throws before admitting anything: all-or-nothing, so a caller that catches it knows the batch is entirely still its own. The throw lands on the actor’s own stack, inside its unstashAll(), rather than on a remote sender’s — and that is the closest thing to a sender a replay has. It is also not a message lost: ActorCell.unstashAll puts the batch back into the stash before the error travels on, so the envelopes stay parked and deadLetterStash still sees them if supervision then restarts or stops the actor. Choosing reject says “refuse, do not lose”, and refusing the replay of a stash that no longer fits is what that means here.

An Envelope.undroppable envelope is admitted whatever the policy says and is never counted, exactly as enqueueSignal admits one at the tail: a Terminated that round-tripped through a stash must not become droppable on the way back in (#729).

Envelope<T>[]

void

DroppingMailbox.prependUser


resume(): void

Defined in: src/internal/Mailbox.ts:409

void

DroppingMailbox.resume


suspend(): void

Defined in: src/internal/Mailbox.ts:408

void

DroppingMailbox.suspend