Skip to content

BatchingPendingCounts.PendingFor is 0 for in-process local-queue batch members while a batch is pending (6.28.2) #4397

Description

@carcer

Summary

WolverineRuntime.BatchingPendingCounts.PendingFor(uri) (added in 6.24.9, CritterWatch#942) reports 0 for members of a BatchMessagesOf<T> pipeline when the elements were published in-process to a local queue, even while a batch is demonstrably pending in the batching block. Its internal dictionary stays empty for the whole trigger window. It looks like members are only counted when they arrive through a transport listener.

The use case is a test harness: at the end of each integration test, wait until every batch the test caused has been triggered and handled, so a batch cannot fire after the next test has reset the database. PendingFor is documented as counting members "posted into a BatchingProcessor's batching channel, waiting in a grouped batch on the local execution queue, or executing in a batch handler", which is exactly the condition needed — but not for local-queue members.

Setup (Wolverine 6.28.2, .NET 10, Marten 9.23.0)

opts.BatchMessagesOf<EnrollGuest>(x =>
{
    x.BatchSize = 2;
    x.Batcher = new EnrollGuestBatcher();   // IMessageBatcher, groups into CreateCourseBatch
}).Sequential();
opts.Policies.UseDurableLocalQueues();

EnrollGuest messages are published from a message handler in the same process (cascading messages), so they land on the local queue local://<namespace>.enrollguest/ and are consumed by the batching processor. With a single element per test the batch fires on TriggerTime (default 250 ms).

What I measured

At the end of a test that published one EnrollGuest, sampling every 10 ms for 400 ms:

  • runtime.BatchingPendingCounts.PendingFor(new Uri("local://<namespace>.enrollguest/")) → 0 at every sample. The endpoint exists (runtime.Endpoints.EndpointFor(uri) returns it) and the address is the one the batch's own envelopes report in the logs.
  • The counter's internal dictionary (read by reflection for the diagnostic) has 0 entries at every sample.
  • The batch was pending: without the 400 ms of sampling, the CreateCourseBatch handler runs after the next test's database reset and throws (the data it needs is gone); with the sampling in place, the handler runs against the original data and succeeds — every time.

So the batch is in the pipeline for those 250+ ms and nothing in BatchingPendingCounts reflects it.

Expected

Either BatchingPendingCounts counts members regardless of whether they arrived via a transport listener or an in-process local publish (keyed by the local queue's URI), or BatchingProcessor<T> exposes a pending count / a way to await "no batch in flight" that test code can use. Anything that lets a caller ask "is a batch for T still pending or executing?" without reflection into the batching block would do.

Why it matters

Without it, the only end-of-test options are a timed wait for TriggerTime (a wait that can be wrong under load) or reflection into BatchingProcessor<T>'s private block. Happy to test a build or add a reproduction if useful.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions