Skip to content

fix: async @EventHandler detached unit publishes lifecycle events under aggregation (#433)#436

Merged
tomas-samek merged 2 commits into
mainfrom
fix/issue-433-aggregated-async-lifecycle
Jul 20, 2026
Merged

fix: async @EventHandler detached unit publishes lifecycle events under aggregation (#433)#436
tomas-samek merged 2 commits into
mainfrom
fix/issue-433-aggregated-async-lifecycle

Conversation

@tomas-samek

Copy link
Copy Markdown
Owner

Fixes #433.

Problem

In aggregated (multi-module) containers, async @EventHandler units published no lifecycle events. Async dispatch runs through the module container's runInDetachedEventScoperunInEventScope, and per-module containers are constructed with publishLifecycleEvents=false (#339). The aggregator publishes unit lifecycle only around its own runInEventScope/supplyInEventScope (the sync path) — which async dispatch never traverses — so async units were invisible to EventStartedEvent/EventEndingEvent metrics.

Fix

The gate was conflating two distinct questions. publishLifecycleEvents=false means "the aggregator brackets my sync units, so I stay silent" — but that reasoning does not hold for async detached units, which the aggregator never brackets. The module container is their sole publisher.

The generated scope methods are refactored around a private __runInEventScope(Runnable, boolean __publishLifecycle) core:

  • public runInEventScope delegates with the standing publishLifecycleEvents flag → sync units in a module stay silent (aggregator publishes the one pair). Unchanged behavior.
  • runInDetachedEventScope (async path) forces true → the detached unit always publishes its own pair.

Single-module containers are built with publishLifecycleEvents=true, so both paths publish exactly as before — no behavior change there.

Tests

  • New behavioral acceptance test in the real 2-module 05_multi_module example (MultiModuleLifecycleEventsTest): a genuine AggregatingContainer over module-a+module-b, an async @EventHandler, asserts exactly one EventStartedEvent/EventEndingEvent pair (one shared eventId) reaches the shared bus. Verified red-before-green (times out with no pair when the force-true is reverted).
  • Updated the white-box codegen tests (DetachedEventScopeTest, ContainerGeneratorLifecycleEventGateTest) to pin the new delegate/core structure and both gate expressions.
  • Regression-checked green: single-module 16_async_scope (unchanged), tiko-runtime (155), tiko-test (27), tiko-processor (241), and both multi-module example reactors.
  • docs/events.md multi-module note updated to describe the unified behavior.

🤖 Generated with Claude Code

@sonarqubecloud

Copy link
Copy Markdown

@tomas-samek
tomas-samek merged commit 20025b6 into main Jul 20, 2026
6 checks passed
@tomas-samek
tomas-samek deleted the fix/issue-433-aggregated-async-lifecycle branch July 20, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AggregatingContainer: async EVENT units publish no lifecycle events (publisher plumbing)

1 participant