Skip to main content
Timeout controls operate at three levels:
  • Bus defaults (resolved on each bus at processing time when event-level values are unset)
  • Per-event overrides (applies to one emitted event instance)
  • Per-handler overrides (applies to one handler registration)
Repository example files:

Timeout types

1) Event timeout (event_timeout)

The outer execution budget for an event. This also acts as an upper cap for each handler run for that event.

2) Handler timeout (event_handler_timeout / handler_timeout)

A handler-specific timeout budget. The effective handler timeout is resolved from handler -> event -> bus, then capped by event_timeout when both are set.

3) Slow-warning thresholds (event_slow_timeout, event_handler_slow_timeout, handler_slow_timeout)

These emit warnings when work is taking longer than expected:
  • event_slow_timeout: warns when event processing is still running past the threshold.
  • event_handler_slow_timeout / handler_slow_timeout: warns when a handler run is still running past the threshold.
Slow thresholds are warnings, not forced cancellation.

Where to set each value

LevelExecution timeout fieldsSlow-warning fields
Busevent_timeoutevent_slow_timeout, event_handler_slow_timeout
Eventevent_timeout, event_handler_timeoutevent_slow_timeout, event_handler_slow_timeout
Handlerhandler_timeouthandler_slow_timeout

Bus-level defaults

Set default budgets and warning thresholds once when creating a bus.
from abxbus import EventBus

bus = EventBus(
    'TimeoutBus',
    event_timeout=30.0,
    event_slow_timeout=10.0,
    event_handler_slow_timeout=3.0,
)

Event-level overrides

Set per-event values when emitting an event instance.
from abxbus import BaseEvent

class WorkEvent(BaseEvent):
    pass

event = bus.emit(
    WorkEvent(
        event_timeout=8.0,
        event_handler_timeout=2.0,
        event_slow_timeout=4.0,
        event_handler_slow_timeout=1.0,
    )
)

Handler-level overrides

Set per-handler timeout and slow-warning overrides at registration time (or by updating the returned handler metadata).
entry = bus.on(WorkEvent, slow_handler)
entry.handler_timeout = 1.5
entry.handler_slow_timeout = 0.5

Precedence rules

Effective handler timeout

  1. Resolve handler timeout source:
    • handler_timeout (handler level)
    • else event_handler_timeout (event level)
    • else bus event_timeout
  2. Apply event cap:
    • effective timeout is min(resolved_handler_timeout, event_timeout) when both are set
    • if one is unset, the other value is used
    • if both are unset, no timeout is enforced
Resolution happens at processing time on each bus. For forwarded events, an unset timeout/concurrency field uses the target bus defaults.

Effective handler slow-warning threshold

Resolved in this order:
  1. handler_slow_timeout
  2. event_handler_slow_timeout
  3. event_slow_timeout
  4. bus event_handler_slow_timeout
  5. bus event_slow_timeout

Effective event slow-warning threshold

Resolved in this order:
  1. event_slow_timeout
  2. bus event_slow_timeout

Execution scope ordering

Timeout and slow-monitor behavior is implemented as stacked runtime scopes. The ordering matters:
# EventBus.step(...)
async with self.locks._run_with_event_lock(self, event):
    await self._process_event(event, timeout=timeout)

# EventBus.process_event(...)
async with asyncio.timeout(resolved_event_timeout):
    async with with_slow_monitor(self._create_slow_event_warning_timer(event)):
        await event._run_handlers(eventbus=self, handlers=applicable_handlers, timeout=resolved_event_timeout)

# EventResult.run_handler(...)
async with eventbus.locks._run_with_handler_lock(eventbus, event, event_result):
    with eventbus._run_with_handler_dispatch_context(event, event_result.handler_id):
        async with event_result._run_with_timeout(event):
            async with with_slow_monitor(handler_slow_monitor):
                await event_result._call_handler(...)
Event-level timeout finalization keeps cancellation semantics explicit:
  • pending handlers -> cancelled
  • started handlers -> aborted

Note on retry

Bus/event timeouts are outer budgets. If you need per-attempt limits for retried handlers, use the retry decorator’s timeout option.