Skip to content

Feature/outbox wakeup signal - #100

Merged
yilmaztayfun merged 6 commits into
masterfrom
feature/outbox-wakeup-signal
Aug 31, 2026
Merged

Feature/outbox wakeup signal#100
yilmaztayfun merged 6 commits into
masterfrom
feature/outbox-wakeup-signal

Conversation

@yilmaztayfun

@yilmaztayfun yilmaztayfun commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Enable low-latency event processing by waking outbox and inbox pollers when new work is committed while preserving adaptive polling as a fallback.

New Features:

  • Add configurable outbox wakeup notifications that prompt processors to poll immediately after successful commits.
  • Add coalescing wake signals for responsive outbox and inbox background polling, including same-process inbox delivery nudges.

Enhancements:

  • Separate outbox processing traces into independent roots while retaining causal links to the originating transition and worker loop.
  • Make wakeup delivery best-effort and non-blocking so polling remains the reliability fallback.

Tests:

  • Add coverage for wake signal behavior, coordinator deduplication and failure handling, trace isolation, and immediate wake-aware polling.

@yilmaztayfun yilmaztayfun self-assigned this Aug 31, 2026
@yilmaztayfun
yilmaztayfun requested review from a team August 31, 2026 07:11
@sourcery-ai

sourcery-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds optional, loss-tolerant outbox wakeup notifications and coalescing wake-aware polling for faster same-process processing, signals inbox polling after delivery commits, and separates outbox publish telemetry into new root spans linked to their origin and worker-loop contexts.

Sequence diagram for outbox commit wakeup and polling

sequenceDiagram
    participant UOW as UnitOfWork
    participant Store as EfCoreOutboxStore
    participant Coordinator as OutboxWakeupCoordinator
    participant Notifier as IOutboxWakeupNotifier
    participant PubSub as DaprPubSub
    participant Poller as OutboxBackgroundService
    participant Processor as OutboxProcessor

    UOW->>Store: StoreAsync(envelope)
    Store->>Coordinator: OnOutboxMessageStored()
    Coordinator->>UOW: OnCompleted(callback)
    UOW->>UOW: CommitAsync()
    UOW-->>Coordinator: callback
    Coordinator-)Notifier: NotifyAsync(cancellationToken)
    Notifier-)PubSub: PublishEventAsync(OutboxWakeupEvent)
    PubSub-)Poller: wakeup event
    Poller->>Poller: WaitAsync(timeout)
    Poller->>Processor: poll and process messages
Loading

Sequence diagram for inbox delivery wakeup

sequenceDiagram
    participant Client
    participant Controller as EventsController
    participant UOW as UnitOfWork
    participant Signal as IPollingWakeSignal_IInboxProcessor
    participant Poller as InboxBackgroundService
    participant Processor as InboxProcessor

    Client->>Controller: ProcessEventAsync()
    Controller->>UOW: CommitAsync()
    UOW-->>Controller: committed inbox row
    Controller->>Signal: Signal()
    Signal-->>Poller: wake pending wait
    Poller->>Processor: poll and process inbox
Loading

File-Level Changes

Change Details Files
Adds coalescing, DI-registered wake signals that let inbox and outbox polling loops interrupt startup and idle delays while retaining timeout-based polling.
  • Introduces a bounded SemaphoreSlim-backed generic wake signal.
  • Injects optional wake signals into both background services and registers marker-specific singleton instances.
  • Signals the inbox poller after event delivery commits.
framework/src/BBT.Aether.Core/BBT/Aether/Polling/IPollingWakeSignal.cs
framework/src/BBT.Aether.Core/BBT/Aether/Polling/PollingWakeSignal.cs
framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/Processing/InboxBackgroundService.cs
framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/Processing/OutboxBackgroundService.cs
framework/src/BBT.Aether.Infrastructure/Microsoft/Extensions/DependencyInjection/AetherOutboxServiceCollectionExtensions.cs
framework/test/BBT.Aether.Infrastructure.Tests/Polling/PollingWakeSignalTests.cs
framework/test/BBT.Aether.Infrastructure.Tests/Polling/WakeAwarePollingTests.cs
Implements an optional post-commit outbox wakeup path that publishes a loss-tolerant Dapr pub/sub nudge without adding latency or creating another outbox record.
  • Adds configurable enablement, event contract, notifier abstraction, and Dapr notifier implementation.
  • Tracks outbox storage within the ambient UoW and registers one detached, bounded notification after successful commit.
  • Deduplicates nested UoW scopes by shared root, handles no-ambient-UoW writes best-effort, and logs notification failures while polling remains the fallback.
framework/src/BBT.Aether.Core/BBT/Aether/Events/AetherOutboxOptions.cs
framework/src/BBT.Aether.Core/BBT/Aether/Events/IOutboxWakeupNotifier.cs
framework/src/BBT.Aether.Core/BBT/Aether/Events/OutboxWakeupEvent.cs
framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/DaprOutboxWakeupNotifier.cs
framework/src/BBT.Aether.Infrastructure/BBT.Aether/Events/EfCoreOutboxStore.cs
framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/OutboxWakeupCoordinator.cs
framework/src/BBT.Aether.Infrastructure/Microsoft/Extensions/DependencyInjection/AetherOutboxServiceCollectionExtensions.cs
framework/test/BBT.Aether.Infrastructure.Tests/Polling/OutboxWakeupCoordinatorTests.cs
Changes outbox processing telemetry so each publish is a new trace root with causation links rather than a child of the originating transition or polling loop.
  • Temporarily clears ambient Activity when creating the per-message producer span.
  • Adds links for the stored origin context and worker-loop context when available, preserving trace state and episode separation.
  • Updates coverage for origin, missing, malformed, and absent worker-loop trace contexts.
framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/Processing/OutboxProcessor.cs
framework/test/BBT.Aether.Infrastructure.Tests/BBT/Aether/Events/Processing/OutboxProcessorTraceTests.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b0549fd-f9e1-4400-9120-8877f2ce9f61


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yilmaztayfun
yilmaztayfun merged commit f521ed7 into master Aug 31, 2026
3 of 6 checks passed
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 10 complexity · 19 duplication

Metric Results
Complexity 10
Duplication 19

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@sonarqubecloud

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="framework/src/BBT.Aether.Core/BBT/Aether/Events/OutboxWakeupEvent.cs" line_range="9" />
<code_context>
+/// as "poll now"; the payload is deliberately empty and delivery is best-effort — the adaptive
+/// polling interval remains the safety net for lost or early signals.
+/// </summary>
+[EventName("aether.outbox.wakeup")]
+public sealed class OutboxWakeupEvent;
</code_context>
<issue_to_address>
**issue (broader_impact):** The outbox wakeup event is published and the polling signal is registered, but no `IEventHandler<OutboxWakeupEvent>` or equivalent subscription forwards deliveries to `IPollingWakeSignal<IOutboxProcessor>.Signal()`. Consequently, successful wakeup publishes are consumed by no in-process component and outbox workers continue waiting for their normal polling interval.

**Triggers:** When `WakeupSignalEnabled` is true and an outbox message is committed.

**Suggested fix:** Add and register an `IEventHandler<OutboxWakeupEvent>` that resolves `IPollingWakeSignal<IOutboxProcessor>` and calls `Signal()`.
</issue_to_address>

### Comment 2
<location path="framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/DaprOutboxWakeupNotifier.cs" line_range="19-23" />
<code_context>
+    private readonly string _topic = topicNameStrategy.GetTopicName(typeof(OutboxWakeupEvent));
+
+    public Task NotifyAsync(CancellationToken cancellationToken = default)
+        => daprClient.PublishEventAsync(
+            eventBusOptions.PubSubName,
+            _topic,
+            new OutboxWakeupEvent(),
+            cancellationToken);
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** `DaprOutboxWakeupNotifier` publishes a raw `OutboxWakeupEvent`, while the existing distributed event bus publishes serialized `CloudEventEnvelope` objects and its invokers deserialize that envelope shape. A wakeup subscriber using the framework's normal event handling path therefore cannot deserialize or dispatch this payload.

**Triggers:** When the wakeup topic is delivered through the framework's standard Dapr event-bus subscription pipeline.

**Suggested fix:** Publish the wakeup using the same CloudEvent envelope serialization and metadata path as `DaprEventBus`, or implement a dedicated raw-payload subscription that deserializes `OutboxWakeupEvent` directly.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 2 findings to address first, and if the coordinator or topic wiring is wrong, it can emit stray wakeup messages and cause workers to poll or process existing outbox rows prematurely or repeatedly. Reverting stops future nudges, but already-published wakeup messages and their operational effects cannot be recalled; the impact is bounded and the normal polling path remains available.

Blocking findings: framework/src/BBT.Aether.Core/BBT/Aether/Events/OutboxWakeupEvent.cs:9, framework/src/BBT.Aether.Infrastructure/BBT/Aether/Events/DaprOutboxWakeupNotifier.cs:23


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

/// as "poll now"; the payload is deliberately empty and delivery is best-effort — the adaptive
/// polling interval remains the safety net for lost or early signals.
/// </summary>
[EventName("aether.outbox.wakeup")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (broader_impact): The outbox wakeup event is published and the polling signal is registered, but no IEventHandler<OutboxWakeupEvent> or equivalent subscription forwards deliveries to IPollingWakeSignal<IOutboxProcessor>.Signal(). Consequently, successful wakeup publishes are consumed by no in-process component and outbox workers continue waiting for their normal polling interval.

Triggers: When WakeupSignalEnabled is true and an outbox message is committed.

Suggested fix: Add and register an IEventHandler<OutboxWakeupEvent> that resolves IPollingWakeSignal<IOutboxProcessor> and calls Signal().

Comment on lines +19 to +23
=> daprClient.PublishEventAsync(
eventBusOptions.PubSubName,
_topic,
new OutboxWakeupEvent(),
cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): DaprOutboxWakeupNotifier publishes a raw OutboxWakeupEvent, while the existing distributed event bus publishes serialized CloudEventEnvelope objects and its invokers deserialize that envelope shape. A wakeup subscriber using the framework's normal event handling path therefore cannot deserialize or dispatch this payload.

Triggers: When the wakeup topic is delivered through the framework's standard Dapr event-bus subscription pipeline.

Suggested fix: Publish the wakeup using the same CloudEvent envelope serialization and metadata path as DaprEventBus, or implement a dedicated raw-payload subscription that deserializes OutboxWakeupEvent directly.

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.

1 participant