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.
Summary
WolverineRuntime.BatchingPendingCounts.PendingFor(uri)(added in 6.24.9, CritterWatch#942) reports 0 for members of aBatchMessagesOf<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.
PendingForis 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)
EnrollGuestmessages are published from a message handler in the same process (cascading messages), so they land on the local queuelocal://<namespace>.enrollguest/and are consumed by the batching processor. With a single element per test the batch fires onTriggerTime(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/"))→0at 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.CreateCourseBatchhandler 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
BatchingPendingCountsreflects it.Expected
Either
BatchingPendingCountscounts members regardless of whether they arrived via a transport listener or an in-process local publish (keyed by the local queue's URI), orBatchingProcessor<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 forTstill 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 intoBatchingProcessor<T>'s private block. Happy to test a build or add a reproduction if useful.