Skip to main content
Backpressure in abxbus is history-policy based, not queue-capacity based.
  • emit() enqueues synchronously and returns immediately.
  • Pending queues are unbounded in each runtime.
  • Overload behavior is controlled by max_history_size + max_history_drop.

1) If I emit 1,000,000 events, will errors be raised?

Error conditions

In normal operation, queue-capacity errors are not the backpressure mechanism. max_history_size=0 / MaxHistorySize=0 is a special case in all runtimes: it does not trigger history-limit rejection, and instead keeps only in-flight visibility. With max_history_drop=true, emit() does not reject on history size. Under sustained overload, old uncompleted entries can be dropped and a warning is logged.

Reject vs drop behavior

2) If 1,000,000 events complete, how many are kept?

Let N = max_history_size. Python nuance: in heavy bursts with max_history_drop=True, cleanup is amortized, so history can temporarily exceed N before converging back to <= N. For the broader retention model, see Event History Store.

3) How RAM usage scales

At a high level, memory grows with:
  • pending queue depth,
  • retained history size,
  • per-event handler/result payload size.
A practical model is: RAM ~= O(pending_event_queue) + O(event_history) + O(event_results and payloads)

Measured slopes from perf suites

  • Python README matrix reports scenario-dependent peak RSS slopes between about 0.025kb/event and 8.024kb/event.
  • TypeScript README matrix reports scenario/runtime-dependent peak RSS slopes between about 0.1kb/event and 7.9kb/event.
  • TypeScript README notes those kb/event values are measured during active processing with history aggressively bounded (max_history_size=1 in perf harnesses).
Use those numbers as throughput-era slope indicators, not exact long-term retention multipliers for your payloads. Operationally:
  • bounded history (N finite) keeps steady-state memory bounded by queue depth + N,
  • unbounded history (N=None/null) makes retained RAM grow roughly linearly with total completed events.

4) Queue vs history lifecycle (exact behavior)

Events do not “move from queue to history.” They are added to history at emit() time, and can exist in both structures while pending.

Python timeline (emit)

  1. Validate pressure policy.
  2. Enqueue into pending_event_queue.
  3. Add same event object to event_history.
  4. Runloop dequeues event (queue.get()), then executes handlers.
  5. Event remains in event_history as pending -> started -> completed unless trimmed/removed by history policy.

TypeScript timeline (emit)

  1. Validate pressure policy.
  2. Add event to event_history.
  3. Apply trimHistory().
  4. Push event into pending_event_queue.
  5. Runloop shifts from queue and executes handlers.
  6. Event remains in event_history unless trimmed/removed by policy.

Go timeline (Emit)

  1. Validate pressure policy against EventHistory.
  2. Add event to EventHistory.
  3. Apply history trimming according to MaxHistorySize + MaxHistoryDrop.
  4. Push event into the pending queue.
  5. Runloop dequeues the event and executes handlers.
  6. Event remains in EventHistory unless trimmed/removed by policy.

Rust timeline (emit)

  1. Validate pressure policy against the runtime history store.
  2. Add event to history and append its id to history order.
  3. Apply history trimming according to max_history_size + max_history_drop.
  4. Push event into the pending queue.
  5. Runloop dequeues the event and executes handlers.
  6. Event remains in history unless trimmed/removed by policy.
So yes:
  • an event can be in both pending_event_queue and event_history at the same time,
  • event_history can contain pending events (not only started/completed events).

Observe both structures directly