Event Lifecycle & Flow
Relevant source files
The following files were used as context for generating this wiki page:
The Event Lifecycle in the bubus project defines the transition of an event from creation and dispatch to execution and terminal completion. The system is designed to manage complex asynchronous flows, including parent-child relationships, cross-bus forwarding, and strict timeout enforcement. Events are treated as immutable records of intent, while their execution state is tracked via associated handler results.
Sources: TODO_ts.md:21-25, bubus-ts/src/base_event.ts:168-185
Event States and Transitions
Events move through a specific set of statuses: pending, started, and completed. Unlike handlers, events do not have an "error" state; an event is considered completed once all its handlers and descendants have finished, regardless of whether those handlers succeeded or failed.
Status Definitions
| Status | Description |
|---|---|
| Pending | The event has been dispatched and is sitting in the EventBus queue. |
| Started | The event has been dequeued and at least one handler has begun execution. |
| Completed | All local handlers and recursive descendant events have reached a terminal state. |
Sources: TODO_ts.md:32-34, bubus-ts/src/base_event.ts:161-165
Transition Logic
The flow of an event is orchestrated primarily by the EventBus, while the BaseEvent class maintains the metadata and relationship pointers.
The transition to completed can be forced or naturally occurring when event_pending_bus_count reaching zero and all children are complete. Sources: bubus-ts/src/base_event.ts:503-515, TODO_ts.md:94-98
Handler Execution Flow
When an event is processed, the EventBus creates EventResult objects for every registered handler. These results have their own lifecycle (pending | started | completed | error) that exists within the scope of the event's started phase.
Concurrency and Completion Modes
The flow of handler execution is governed by two main settings:
- Concurrency Mode:
serial(one at a time using semaphores) orparallel(all at once). - Completion Mode:
all(default, waits for everyone) orfirst(waits for the first non-undefined result and cancels others).
Sources: bubus-ts/src/base_event.ts:166-167, bubus-ts/src/base_event.ts:449-462
The Handler Execution Stack
The preferred execution layering for handlers follows a specific "context-manager" style to ensure safety:
- Lock Acquisition: Acquire handler-level or global-level semaphores.
- Error Normalization: Wrap execution to catch and categorize failures.
- Hard Timeout: Enforce the
event_handler_timeout. - Slow Monitor: Trigger warnings if execution exceeds
event_handler_slow_timeout. - Work: Execute the actual handler function.
Sources: TODO_ts.md:109-114, TODO_python.md:118-123
Parent-Child Linkage
Events emitted during the execution of a handler are automatically linked to the "active" event as children. This creates a tree structure accessible via event_children and event_descendants.
Automatic Context Tracking
When a handler uses event.bus.emit(), the library captures the event_id of the currently running event and assigns it as the event_parent_id of the new event. This linkage is critical for the done() and immediate() methods, which allow "queue-jumping" where a parent waits for a child to complete even if the child is behind other events in the global queue.
Sources: bubus-ts/examples/parent_child_tracking.ts:31-45, bubus-ts/src/base_event.ts:480-484
Timeout and Cancellation Flow
Timeouts can be applied at the event level (event_timeout) or the handler level (event_handler_timeout).
Cancellation Cascade
When an event times out or is explicitly cancelled, it initiates a recursive cascade:
- Abort Started Handlers: Send an abort signal to currently running handlers.
- Cancel Pending Handlers: Mark all queued handlers with
EventHandlerCancelledError. - Recurse to Descendants: Call
cancelPendingDescendants()on all child events emitted by those handlers.
Sources: bubus-ts/src/base_event.ts:583-610, bubus-ts/tests/timeout.test.ts:490-510
Error Taxonomy
The system normalizes all failures into a specific taxonomy of error classes:
EventHandlerCancelledError: Handler was removed from queue before starting.EventHandlerAbortedError: Handler was stopped mid-execution (e.g., due to parent timeout).EventHandlerTimeoutError: Handler exceeded its allocated time.EventHandlerResultSchemaError: Handler returned a value that failed Zod/Pydantic validation.
Sources: TODO_ts.md:27-31, bubus-ts/tests/timeout.test.ts:60-80
Visualizing the Flow in the UI
The event lifecycle is reflected in the Project's UI monitor, which aggregates events and results into a tree view.
| Component | UI Representation | Derived From |
|---|---|---|
| Event Summary | Status Pills (Pending, Started, Completed, Error) | event_status |
| Payload | Expandable JSON block | event_json / data |
| Results Table | Table of Handler name, Status, Start/End times, and Result/Error | event_results |
| Tree Structure | Nested list items showing parent-child links | event_parent_id |
Sources: ui/main.py:108-150, ui/main.py:270-310
Summary
The Event Lifecycle in bubus ensures that asynchronous workflows are observable and controllable. By centralizing transitions in the EventBus and enforcing a strict hierarchy through parent-child tracking, the system provides reliable execution guarantees, such as the ability to jump the queue for awaited children and the automatic cleanup of abandoned work through cascading cancellations.