Skip to content

Single-DbContext Eager path still has the GH-3744 defect: scraped domain-event envelopes are tracked after the only SaveChanges and never persisted #4628

Closed

Description

@jeremydmiller

Summary

GH-3744 (#3744, fixed in 71133e473) found that on a Wolverine-mapped DbContext, a durable route persists its envelope by adding an OutgoingMessage/IncomingMessage entity to the change tracker (EfCoreEnvelopeTransaction.PersistOutgoingAsync, lines 54/81; PersistIncomingAsync, line 111), that domain-event scraping runs after the SaveChangesAsync the middleware emits, and that committing straight after the scrape therefore committed the aggregate and dropped every envelope the scrape had just produced — while the event was still published in memory, so tracked-session tests stayed green. The fix added a second SaveChangesAsync after the scrape.

That fix lives only in the tenanted frame, StartDatabaseTransactionForDbContext.cs:124-133 (CommitTenantedDbContextTransaction). The single, non-tenanted Eager path still has the original ordering. EfCoreEnvelopeTransaction.CommitAsync (Internals/EfCoreEnvelopeTransaction.cs:181-223) is:

foreach (var scraper in _scrapers) await scraper.ScrapeEvents(DbContext, _messaging);   // tracks OutgoingMessage rows on durable routes
... MarkIncomingEnvelopeAsHandledInTransactionAsync / PersistIncomingAsync(handled row)  // the latter also only Adds to the tracker
if (DbContext.Database.CurrentTransaction != null) await ...CommitAsync(cancellation);  // no SaveChangesAsync between scrape and commit
await _messaging.FlushOutgoingMessagesAsync();

So on AddDbContextWithWolverineIntegration<T>() + UseEntityFrameworkCoreTransactions() (Eager, the default) with PublishDomainEventsFromEntityFrameworkCore() and a durable route (durable local queue, durable RabbitMQ endpoint), the scraped envelopes are tracked and never written. They are still sent in memory, so the only symptom is a missing durability row — a crash between commit and flush loses them, and a durable local queue's inbox row for the domain event never exists (mark-handled then updates zero rows, silently). The PersistIncomingAsync(handled row) branch for non-inbox envelopes has the same shape.

DomainEvents/configuration_of_domain_events_scrapers.cs (8 tests) asserts tracked sessions only; the #3744 commit message itself says "No automated regression test yet". This is read from source and matched against #3744's own fix comment; it has not yet been reproduced by a test, which is the first step.

Ask

  1. Red test first: scraped_domain_events_on_a_durable_route_persist_a_row_before_commit — single mapped DbContext, Eager, durable local queue whose handler blocks on a gate; while it blocks, query wolverine_incoming_envelopes for the domain event's envelope. Expect a row; today expect none. A twin through IDbContextOutboxFactory (it passes [] scrapers — see the Lightweight issue).
  2. Fix: a SaveChangesAsync after the scrape (and after the handled-row Add) in EfCoreEnvelopeTransaction.CommitAsync when the context is Wolverine-mapped, mirroring the tenanted frame. A no-op when the scrape produced nothing.

🤖 Generated with Claude Code

Activity

  1. added
    bugSomething isn't working
    durabilityAnything related to durable inbox/outbox functionality
    on Sep 26, 2026
  2. added a commit that references this issue on Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdurabilityAnything related to durable inbox/outbox functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions