Skip to content

Bump WolverineFx from 5.37.0 to 6.24.0 - #2265

Open
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/nuget/WolverineFx-6.24.0
Open

Bump WolverineFx from 5.37.0 to 6.24.0#2265
dependabot[bot] wants to merge 1 commit into
masterfrom
dependabot/nuget/WolverineFx-6.24.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 29, 2026

Copy link
Copy Markdown
Contributor

Updated WolverineFx from 5.37.0 to 6.24.0.

Release notes

Sourced from WolverineFx's releases.

6.24.0

Two data-loss fixes — but for unusual usages

This release closes two bugs that silently destroyed data rather than failing loudly. Both are worth reading before you skip the rest of these notes.

Durable inbox rows were orphaned when a circuit breaker tripped (#​3680). DurableReceiver checked its latched flag before calling MarkReceived. The latched path still persists each envelope to the inbox as a safety net — but on an envelope that never went through MarkReceived, Status is the enum default (Outgoing) and Destination is null. Both are filter columns for inbox recovery, so the rows were written in a state no recovery sweep on any node could ever see. The null Listener also skipped the nack back to the broker, and the broker's redelivery after restart hit DuplicateIncomingEnvelopeException — which acks and drops. Net result: genuine message loss under a durable inbox any time a circuit breaker trip latched the receiver mid-flight. Measured on the circuit-breaker suite, 9 of 1,200 messages were lost per run.

Dropping one tenant from a shared partition bucket destroyed its co-tenants' data (#​3686). Found alongside #​3683. Tenant bucketing — registering several small tenants against one partition suffix so they share a physical partition — is documented and exposed through PartitionPerTenant(p => p.AllowPartitionSharing = true), and it did not work on either engine. It had no test coverage, because the doc sample demonstrating it is compile-only and never executed.

Global partitioning

Part of the GlobalPartitioning epic (#​3482).

  • Global partitioning topologies for PostgreSQL and SQL Server queues (#​3468, #​3469)
  • End-to-end sharded-processing suites for Azure Service Bus, GCP Pub/Sub, NATS, Redis Streams and Pulsar (#​3467). The scenario is lifted into Wolverine.ComplianceTests.Partitioning.ShardedProcessing, so a new transport costs one small test class
  • Native-mode design comparison and per-transport native alternatives documented (#​3481)

The new suites immediately found two real bugs:

  • NATS global partitioning had never worked at all. The topology forces EndpointMode.Durable on every slot, and a NatsEndpoint only supports Durable when JetStream-backed — so every UseShardedNatsSubjects() call threw at configuration time. The topology now enables JetStream on its own endpoints and declares a work-queue stream per shard, without which the listener died at startup on stream not found
  • Pulsar named its companion local queues off the full topic path, producing queues like global-persistent://public/default/orders1. They now use the topic's short name, matching every other transport

Multi-tenancy and persistence

  • EF Core tenant partition back-fill (#​3496). Routine migration deltas deliberately leave Weasel-managed partitions alone, so a table joining an existing managed set — a newly deployed service, or a newly mapped ITenanted entity — had no partition for any tenant registered before that table existed. IConjoinedTenantPartitions<T>.MigrateTenantPartitionsAsync() reconciles every partitioned table against the full registered tenant set, with per-table TenantPartitionResult reporting
  • Conjoined tenant partition bucketing actually works now, on both PostgreSQL and SQL Server (#​3683, and see #​3686 above)
  • Exclusive listener inbox recovery is now covered for RavenDb (#​3595) and CosmosDb (#​3596)

Transports

  • RabbitMQ: deliveries are settled against the channel they arrived on (#​3687). Acking a delivery on a torn-down channel threw a NullReferenceException
  • NATS: auto-provisioned JetStream durable consumers are filtered to their own subject (#​3676). FilterSubject was only assigned when ConsumerName was empty, so every durable consumer on a stream received every message. The fix needs a FilterSubjects multi-filter — a single filter cannot cover both {subject} and {subject}.scheduled, and a work-queue stream discards an uncovered control message
  • MQTT: the v5 authentication method name is configurable (#​3588). It was hardcoded to "OAUTH2-JWT". Azure Event Grid's custom JWT authentication requires CUSTOM-JWT, so those brokers could not be reached through Wolverine's authentication support at all. You could already set the method by hand through MqttClientOptionsBuilder.WithAuthentication(), but that gave up Wolverine's token refresh loop — the whole reason to use MqttJwtAuthenticationOptions. You no longer have to choose
  • The HTTP transport can send to a destination nobody pre-registered (#​3681, reported as ProductSupport#​34). WolverineHttpTransportClient used the endpoint's OutboundUri purely as an IHttpClientFactory client name, then posted to that client's BaseAddress — so operator commands sent back over the HTTP transport failed with An invalid request URI was provided

Performance

  • RabbitMQ consumer dispatch concurrency is now per-endpoint (#​3492). The client default of 1 was the bottleneck. Simulated handler, 2,000 msg/s offered load, 30s measured window:

    ConsumerDispatchConcurrency Throughput Transit p50
    1 (client default) 163.7/s — (nothing from the measured window was consumed before the run ended)
    5 828/s 22,871.9 ms
    20 1,999.1/s 1.486 ms (p95 2.54, p99 3.22)

    The 5.1x and 12.2x multiples understate it — at 1 and 5 the listener never catches up at all.

  • Amazon SQS batches message deletions and chunks outgoing batches on the 256KB request size limit (#​3493)

  • Azure Service Bus session listeners are no longer quadratic — the n² session loops are now n. MaxConcurrentCalls is surfaced, and a batched defer settles the original message (#​3494)

HTTP and gRPC

... (truncated)

6.23.1

Agent distribution

TL;DR: if you pause a projection from CritterWatch on 6.23.0, restarting it appears to do nothing for a full minute. This fixes that.

  • A paused projection or subscription agent now resumes immediately when you restart it (#​3663). On 6.23.0 the restart was accepted, the pause restriction was cleared, and then nothing happened until the pending-assignment ledger's TTL expired — 2 × CheckAssignmentPeriod, so 60 seconds with the defaults. Long enough that an operator reasonably concludes the agent is never coming back.

    The ledger introduced in 6.23.0 (#​3622) only counted an assignment as confirmed if a later evaluation saw the agent running and still assigned to the same node. A pause makes those two conditions mutually exclusive: the first evaluation that can observe the delivered assignment is the same one that detaches the agent. The entry was never confirmed, nothing on the stop path cleared it, and the restart's AssignAgent was suppressed as a duplicate still in flight. Delivery alone now confirms the entry, which is the only question the ledger was ever asking.

  • Pausing an agent no longer briefly starts it first (#​3666). ApplyRestrictionsAsync kickstarted a health check before persisting the operator's restriction change, so that evaluation ran against the old restrictions and could act against the very intent being applied — for a pause, re-assigning and starting the agent one beat before the merged evaluation stopped it again. Besides the wasted daemon start/stop cycle, this is what armed the stale ledger entry behind #​3663.

PostgreSQL

  • Advisory-lock sessions stay invisible to Marten's async-daemon gap detection (#​3664). Marten 9.16.1+ will not skip a stale event-sequence gap while any session whose open transaction predates that gap is still alive (marten#​4953). A session parked in an open transaction for the life of the process therefore reads as a permanent "possible reserver" and can hold the high-water mark — and every async projection — behind a gap that is genuinely dead.

    Wolverine's long-held locks were already shaped correctly: leader election and node coordination hold session-scoped advisory locks on a dedicated connection with no transaction, so they show up as state='idle' with a NULL xact_start. Those sessions are now also tagged application_name = 'wolverine-advisory-lock:<database>', which turns a pg_stat_activity investigation from guesswork into something you can read at a glance. The constraints are pinned in tests and in the Postgres durability docs, including the trap worth knowing in your own code: never add a keepalive query inside a long-lived open transaction — it bumps state_change, makes the session look active, and re-promotes it to candidate reserver.

    Note for combined Marten + Wolverine deployments: older guidance suggested Postgres's idle_in_transaction_session_timeout as a dead-gap backstop. Prefer upgrading Marten and using SkipStaleGapsDespiteLiveTransactionsAfter instead.

6.23.0

Projection & agent distribution

TL;DR: this prevents Wolverine from going into a panic doing agent assignments and churning crazily hard during Kubernetes rollouts or cluster starts and that's a very good thing

The bulk of this release. A wave of fixes (WO-1..8) to the agent assignment plane that together remove the re-assignment churn and livelock that could leave projection agents flapping or wedged.

  • Node heartbeats no longer block behind command execution (#​3620)
  • Node resurrection restores the node's real identity rather than a skeleton record (#​3621)
  • A pending-assignment ledger suppresses duplicate AssignAgent floods (#​3622)
  • Agent batch starts are chunked and bounded-parallel, so they stay safe at scale (#​3623)
  • Ejection hysteresis plus leader protection (#​3624)
  • The assignment row is re-upserted for agents that are already running (#​3619)
  • Local agents drain with bounded parallelism on shutdown (#​3636)
  • Paused shards now say why. IEventSubscriptionAgent.Failure surfaces a ShardFailure — category, the failing event's sequence and type, and the root exception type — through health checks and a new IWolverineObserver.AgentPaused hook plus a NodeRecordType.AgentPaused record. Failures bound to a specific event (ApplyEvent, EventSerialization, UnknownEventType) or to two processes racing one shard (ProgressionOutOfOrder) are no longer auto-restarted, since they would die on the identical event every time (#​3637, #​3638)
  • Agent starts retry locally, bounded by Durability.AgentStartRetryAttempts / AgentStartRetryDelay, instead of idling a full CheckAssignmentPeriod after a startup race (#​3519)
  • Store-scoped FindAgentUriAsync overload (#​3647) and a store-aware TryRebuildRegisteredProjectionAsync overload (#​3618)

Bumps JasperFx.Events to 2.36.1, Marten to 9.20.0 and Polecat to 5.7.0.

HTTP

  • Endpoints that advertise a request body their HTTP method cannot carry now fail fast at startup instead of silently 404ing out of the route matcher (#​3648)
  • A missing Content-Type on an [AcceptsContentType] route returns 415 rather than 404 (#​3649)
  • Query-only [AsParameters] endpoints no longer advertise a form body, which had been dropping them from route matching entirely (#​3630)
  • Explicitly-routed chains — PublishMessage<T> and SendMessage<T> — now describe the message they read from the body. They had been advertising no request body at all in OpenAPI (#​3646)
  • Enum values in query strings and form posts parse case-insensitively (#​3629)
  • Compiled queries returned as a Wolverine.Http resource no longer execute twice (#​3625)

Transports

  • Conventional routing respects named brokers (#​3633)
  • Google Pub/Sub: named-broker support for sharded and partitioned topics (#​3632), and ListenToPubsubSubscriptionOnNamedBroker (#​3631)
  • Kafka: topic-already-exists error codes are handled rather than thrown (#​3640)
  • Redis: scheduled retries survive an unreadable timestamp (#​3613), and scheduled entries that repeatedly fail to deserialize are dead-lettered instead of looping (#​3644)
  • EnvelopeMapper reads both timestamp header formats (#​3645)
  • NServiceBus interop: the EnclosedMessageTypes header is split before the message type is resolved, so interop works across Azure Service Bus, SNS, SQS and the database transports (#​3628)

Persistence

  • Oracle runs the durability agent through the shared batching mechanics (#​3614). Note that ODP.NET exposes no DbBatch, so the work splits per statement.

Other

  • Resource discovery no longer eager-connects broker transports (#​3617)

Contributors

Thank you to everyone who contributed to this release:

... (truncated)

6.22.0

Wolverine 6.22.0 rolls up the claim-check backend wave, distributed-agent and durability hardening, HTTP/OpenAPI binding fixes, and the Marten 9.18 / JasperFx 2.34 critter-stack alignment.

Dependency alignment

  • JasperFx family → 2.34.0
  • Marten9.18.0 (with Marten.AspNetCore / Marten.Newtonsoft), Polecat [5.5.0,6.0.0)
  • Weasel family → 9.18.1

Claim-check offloading

  • New NATS JetStream Object Store claim-check backend (#​3506)
  • New PostgreSQL database-LOB claim-check backend (#​3507)
  • New Google Cloud Storage claim-check backend (#​3505)
  • Size-threshold auto-offload — large payloads offload to the claim-check store automatically (#​3504)
  • Per-message / per-endpoint store selection (#​3508)
  • Honor a DI-registered IClaimCheckStore (#​3564)
  • New claim-check backends are now packed for NuGet (#​3565)

Distributed agents & durability

  • Recover exclusive listener inboxes on the listening node (#​3590)
  • Reverse the reset-clears-queue-tables behavior; add IHost.ClearAllWolverineStorageAsync() (#​3592)
  • Subscription health check uses the tenant-scoped high water mark for per-tenant subscription agents (#​3582)
  • Isolate agent start failures so one wedged agent no longer skips its siblings (#​3519)
  • Restart a projection agent whose shard wedged out from under it (#​3519)
  • EventSubscriptionAgent restores continuous execution after Rebuild/Rewind (#​3520)
  • Stamp the assigned node onto owned event-store daemons (running_on_node, marten#​5001) (#​3578)
  • Solo-mode health-check loop no longer leaks an unbounded OpenTelemetry trace (#​3518)
  • Warn loudly when a host's application assembly is pinned by an earlier host (#​3521)

HTTP & OpenAPI

  • Describe chain-bound OpenAPI params from produced variables; narrow re-reflection to postprocessors (#​3601)
  • Bind [FromQuery] on arrays/collections instead of misrouting to complex-flattening (#​3602)
  • Bind scalar [FromQuery] decimal correctly + shape-test OpenAPI parameter description across type families (#​3586)
  • Don't describe a route-bound [FromQuery]/[FromHeader] parameter twice in OpenAPI (#​3586)
  • Marten 9.18 streaming typesStreamPaged, StreamPagedByCursor, ETag support (#​3593)
  • Honour opt-in discovery filters for HTTP endpoint discovery (#​3525)
  • Never infer a DbContext parameter as the HTTP request body (#​3538)

Transports

  • Add a parallel WolverineFx.Mqtt5 package (MQTTnet 5) (#​3517)
  • Pulsar: don't claim native scheduling without a retry-letter topic (#​3491)
  • Azure Service Bus: pin session listeners to specific session identifiers (#​3533)

Persistence

  • Clear transport queue tables on message-store reset — PostgreSQL (#​3529), SQLite (#​3553), MySQL (#​3551), Oracle (#​3552)
  • Oracle: durable inbox mark-as-handled binds RAW(16) Guid correctly (#​3581)
    ... (truncated)

6.21.0

Wolverine 6.21.0 is a big one: conjoined multi-tenancy for EF Core, and a measured messaging-performance wave across Kafka and RabbitMQ.

Conjoined multi-tenancy for EF Core (#​3465)

Mark an EF Core entity with ITenanted (the marker shared critter-stack-wide from JasperFx.MultiTenancy) and register your DbContext with AddDbContextWithWolverineManagedConjoinedTenancy<T>(), and Wolverine gives you what Marten users have had for years: a mapped tenant_id column, a tenant-bound global query filter you can't forget, stamp-on-insert, CrossTenantWriteException on cross-tenant writes, conjoined sagas, opt-in Weasel-managed physical tenant partitioning (PostgreSQL list partitions + SQL Server tenant-ordinal), and an authoritative wolverine_tenants registry that doubles as a dynamic tenant source and feeds CritterWatch tenant management. The behavior is checked against a port of Marten's conjoined-tenancy compliance battery. See the new ConjoinedMultiTenantedEfCore sample app and docs.

Messaging performance (GH-3490 / GH-3492)

A client-reported "Wolverine-over-Kafka is 3-12x slower than native" investigation turned into a measured optimization wave (methodology, rigs, and full ledgers are in the repo):

  • Sender batching now flushes within the batch timeout (JasperFx 2.30.1). The batch timer was a quiet-period debounce — a steady 8 msg/s stream at the default (100, 250ms) settings measured 5.8 seconds publish-to-consume p50; it now measures 136ms, bounded by the timeout. Affects every transport that sends through the batched sender (RabbitMQ routes were unaffected — they don't).
  • Durable (inbox-backed) listeners batch their inbox writes. Kafka: a 2,000 msg/s stream went from unbounded backlog to a steady 32ms delivery p50, max sustained durable throughput +83%. RabbitMQ: same load went from 14.7s-and-climbing to 0.8ms delivery p50, max sustained durable throughput +186% (1,086 → 3,101 msg/s). New MaximumMessagesToReceive listener knob on both (default 100; 1 restores strict message-at-a-time persistence).
  • Kafka hot path: incoming envelope mapping ~21% faster / ~28% less allocation per message; outgoing ~17% faster; inline senders no longer issue a blocking full-producer flush per send. RabbitMQ's mapper gets the same incoming fix.
  • Batch-arrival correctness: the Envelope[] arrival path now applies the same per-envelope guards as single-message arrival (interop serializer unwrap, dead-lettering of unidentifiable messages, expiry, drain latching), and batched inbox writes route to ancillary message stores correctly.
  • SQS: SendMessageBatch per-entry failures (throttling, oversize) are now routed to Wolverine's retry machinery instead of being silently dropped — a silent message-loss fix (GH-3493).
  • Out-of-the-box RabbitMQ consumption is Inline — one message at a time, which measures identically to an equivalent single-consumer raw-client loop. The new RabbitMQ "Performance Tuning" docs page covers scaling with BufferedInMemory() / ListenerCount() with measured numbers, and a matching page exists for Kafka.

Behavior changes to note

  • The per-message "successfully processed" log now defaults to Debug (was Information) — restore with opts.Policies.MessageSuccessLogLevel(LogLevel.Information).
  • wolverine-execution-time is now a floating-point histogram and no longer silently drops sub-millisecond executions (same name/unit; the point type changes).

Transports and messaging

  • Azure Service Bus: PrefetchCount on listeners and transport defaults (GH-3471)
  • SQS: native DelaySeconds for short scheduled sends on standard queues (GH-3472)
  • Pulsar: native scheduled delivery via DeliverAt (GH-3470)
  • Kafka: KIP-848 next-generation consumer rebalance protocol support (GH-3473)
  • Standardized diagnostic headers on dead-letter and retry moves across all transports (GH-3474)
  • SignalR cascading responses are no longer dropped when the HTTP context has already flushed (GH-3499)
  • The dead-letter metric only counts genuine moves to the error queue (#​3495)
  • Global partitioning docs now cover all eight sharded transports (GH-3466)
  • Tolerate empty agent URI lists in agent command serialization (GH-3460)

HTTP / gRPC

  • Code-first gRPC client streaming: IAsyncEnumerable<TRequest> -> Task<TResponse> handler shape (#​3500)
  • Proto-first gRPC client streaming via the new StreamAsync<TRequest, TResponse> overload (#​3459)

Dependencies

JasperFx 2.30.1 (sender-batching max-age fix), Weasel 9.18.1, Marten 9.16.1.

6.20.0

Wolverine 6.20.0

Dependency upgrades (critter stack)

  • Marten 9.16.0, Polecat 5.2.0, JasperFx 2.29.0, Weasel 9.17.0 (aligned across the stack).

Multi-tenancy & connection footprint

  • Scope tenant scheduled-job polling to the owning node (GH-3376) — under Wolverine-managed distribution, nodes no longer open a scheduled-job polling connection to every tenant database; the footprint scales with databases, not nodes × databases. Plus daemon tracker-subscription leak hygiene.
  • Polecat primary-store database-per-tenant (GH-3445) — IntegrateWithWolverine() now honors a database-per-tenant Polecat store and reads MainDatabaseConnectionString.
  • DatabaseServerId sourced from DatabaseDescriptor.Port instead of re-parsing (jasperfx#​514).

CritterWatch / connection state

  • Degrade-only connection state for Azure Service Bus, GCP Pub/Sub (GH-3237) and Kafka (GH-3454) listeners — heuristics only degrade to Reconnecting/Disconnected from real SDK callbacks; they never synthesize Connected, and resting state is Unknown.

Kafka

  • Read record timestamps into SentAt and expose record headers on raw-JSON listeners (GH-3407).
  • JsonSerializerOptions on raw-JSON endpoints now actually applies; PublishRawJson mapper registration fixed.
  • Bounded the listener shutdown drain instead of awaiting forever (GH-3434).

Sagas

  • SagaConcurrencyException now inherits JasperFx.ConcurrencyException (GH-3444) — existing OnException<ConcurrencyException>() policies now catch saga concurrency failures.
  • Lightweight RDBMS saga provider treated as a catch-all so Marten/EF Core win precedence in mixed-storage hosts (GH-3443).

Other

  • Ancillary Marten store now registers the event-subscription family interface aliases (GH-3438).
  • F# code generation improvements (#​3437); TrackedSession not-tracked vs not-routed fix (#​3435).
  • Buffered circuit-breaker tests re-scoped to the transport's non-durable contract (GH-3137); durability docs corrections.

6.19.0

CosmosDB

  • Add optimistic concurrency (ETag compare-and-swap) to saga persistence (GH-3414) @​mysticmind
  • CosmosDbConfiguration.PartitionSagasById(): opt-in, saga id becomes the document partition key (GH-3415) @​mysticmind
  • Refuse a CosmosClient whose serializer would drop a saga's id at host start; document the camelCase requirement (GH-3416) @​mysticmind

HTTP / OpenAPI

  • Describe the whole route table on a pre-start ApiExplorer read; minimal API and MVC endpoints were silently omitted (GH-3421) @​mysticmind
  • Type a Marten/Polecat aggregate-id route parameter as uuid instead of falling back to string (GH-3420) @​mysticmind

Durability / persistence

  • Dead letter recovered envelopes whose transport can't be resolved, instead of losing the rest of the batch and rethrowing forever (GH-3413) @​mysticmind
  • Measure and surface per-server database connection budgets: OTel gauges + IWolverineObserver.ConnectionBudget (GH-3397) @​jeremydmiller

Test infrastructure only

  • Stop IntegrationContext from disposing a class fixture it doesn't own; pin ApplicationAssembly in the CoreTests harness (GH-3423) @​jeremydmiller
  • Give the modular monolith fixture its own message storage schema (GH-3413) @​mysticmind
  • Stop using_dynamic_multi_tenancy from poisoning its own next run @​mysticmind

Milestone: https://github.com/JasperFx/wolverine/issues?q=is%3Aissue%20state%3Aclosed%20milestone%3A6.19.0
Full Changelog: JasperFx/wolverine@V6.18.0...V6.19.0

6.18.0

Wolverine 6.18.0

A security-relevant serialization fix, a startup-fatal codegen fix, a silently-dead-listener fix in RabbitMQ, the first F# saga codegen support of any persistence provider, and the CI split that makes "merge when green" mean something again.

If you use MassTransit interop over a durable listener, take this release. See the first section.

⚠️ Security-relevant: reserved envelope headers could be spoofed through the durable inbox

#​3408fixed in #​3411

EnvelopeSerializer wrote the typed envelope properties to the wire format and then appended every Envelope.Headers entry verbatim, with no reserved-key filter — and the appended entries came last. Because the reader parses reserved keys straight back into typed properties, a Headers entry under a reserved key silently overwrote the real property on the next read.

A value in envelope.Headers["tenant-id"] is inert while the envelope is in memory. It stops being inert the moment the envelope crosses the serializer — any durable listener, the inbox/outbox, or the scheduled-message store:

  1. Something puts tenant-id into envelope.Headers.
  2. The durable inbox persists the envelope; the header is appended after the (null) typed property.
  3. On read back, env.TenantId is set from it.

saga-id reaches another saga's state, and id rewrites Envelope.Id — the inbox's dedupe identity.

This was live, not theoretical. MassTransitEnvelope.TransferData already copies every incoming MassTransit header into envelope.Headers unfiltered (and by assignment, not TryAdd). Any Wolverine app doing MassTransit interop over a durable listener has had this path open. If that describes you, this release is the one to take.

The fix filters reserved keys on the write side, so the typed property stays authoritative and a reserved key sitting in Headers becomes a no-op. causation-id is deliberately not filtered — DeliveryOptions intentionally carries it as a loose header for Wolverine.Marten's OutboxedSessionFactory, and it is never promoted by the reader.

Startup-fatal codegen fix

#​3399fixed in #​3406 — invalid generated class name for batched (array) message types. This one prevents the application from starting.

Fixes

  • #​3388 (#​3400) — refuse a competing Marten daemon under Wolverine-managed event subscription distribution. A DaemonMode.Solo/HotCold daemon alongside managed distribution is now an actionable startup exception instead of two schedulers quietly fighting over the same shards.
  • CritterWatch #​698 (#​3396) — IAgentRuntime.ApplyRestrictionsAsync persisted the restriction and then never dispatched the commands it computed, so pausing an agent had no immediate effect. Reported by @​erdtsieck against a live cluster.
  • #​3385 (#​3403) — a header-identified saga invoked over a gRPC hop failed with an opaque Internal status. It now returns an actionable diagnostic telling you to put the saga identity on the request DTO.
  • #​3398 (#​3404) — [AsParameters] now rejects unparseable values in collection query parameters, closing the gap left by the scalar fix in #​3372.
  • #​3365 (#​3412) — the Polecat primary IEventStore bridge registered twice, so GetServices<IEventStore>() returned the same store instance two times and anything iterating it double-counted. Polecat's own AddPolecat() had started registering IEventStore and Wolverine was still bridging it as well.
  • #​3391 (#​3419) — RabbitMQ: a successful eager channel restart never re-consumed. A callback-exception restart could leave an open channel with zero consumers while reporting State = Connected — a silently dead listener. The listener now defers to ReconnectedAsync(), which re-declares and re-consumes. Also pins the ConnectionMonitor tracking invariant that #​3370 fixed but nothing guarded.

OpenAPI

#​3380 (#​3418) — OpenAPI parameters are now derived from the full binding chain rather than the handler signature alone. Two real defects closed:

  • Query/header values bound only by an After/Finally postprocessor were omitted from the operation entirely.
  • Route parameter types were read off resolved binding variables, so they degraded to the route constraint (or string) whenever the description was assembled before those frames resolved — which is exactly the build-time OpenAPI / openapi CLI path, because ASP.NET caches the first ApiExplorer read.

More importantly, this ships the OpenAPI shape-test harness that was missing. Adding a shape assertion is now one endpoint plus one [Fact], which is why this class of omission kept shipping unnoticed.

New: Azure Service Bus emulator support

#​3366 (#​3409) — the docs told you to call UseAzureServiceBusTesting(), which only ever existed in Wolverine's own test suite. It is now a real, shipping API:

... (truncated)

6.17.3

Bug-fix and scale release, following the 6.17.2 community sweep. Every item below came from a community report or a review finding — thank you all.

Closed issues

  • #​3375 — durability metrics polling pinned a connection per database per node (PR #​3384 by @​erdtsieck). Each durability agent ran its own in-phase PeriodicTimer, so at high database counts the metrics polling itself became significant connection pressure. Agents now register their store with a node-wide sequential sweeper that walks the node's databases one at a time across the UpdateMetricsPeriod window — at most one metrics connection in flight per node, regardless of database count. The registration set is re-read every pass, so databases join and leave the sweep as agents start and stop without a restart.
  • #​3332 — CIAWS was disabled by a broken SNS per-tenant LocalStack setup (PR #​3364 by @​Steve-XYZ). Test-only; re-enables the AWS CI job. Partially addresses #​3350 (the CIPolecat half remains open).
  • RabbitMQ listeners ghosted after a broker restart on a channel callback exception (PR #​3370 by @​kconfesor). The agent stayed latched after a channel-only shutdown and was never rebuilt, so a listener came back "connected" but dead. Reviewed specifically against the #​3171/#​3187 channel-only-shutdown work to confirm it does not reintroduce the latched-Disconnected state that #​3187 fixed. Two follow-ups are tracked in #​3391.
  • ProductSupport#​33 — CritterWatch telemetry was caught by tracked-session waits (PR #​3390, reported by @​uniquelau). A monitored host publishes telemetry that a TrackedSession would pick up and then sit waiting on messages the test never sent. The default ignore rule now covers all of INotToBeRouted (agent commands and framework telemetry), with a deliberate carve-out for Acknowledgement / FailureAcknowledgement, which the session's own acknowledgement APIs depend on. If you are on an older version, IgnoreMessagesMatchingType(t => t.CanBeCastTo<INotToBeRouted>()) is the workaround.

Fixes from review

  • Metrics sweeper: unregistration race and a hot-spin guard (PR #​3393, follow-ups from the #​3384 post-merge review). The sweeper removed a registration by URI alone, so when an agent for a database stopped after a replacement agent for the same database had registered — exactly what agent redistribution does — the stopping agent evicted the live registration, and that database silently stopped being polled until the node restarted. Unregistration now removes only the exact registration instance it created. Separately, UpdateMetricsPeriod = TimeSpan.Zero would hot-spin the sweep loop (the pre-#​3384 PeriodicTimer threw); it is now rejected at configuration time, with DurabilityMetricsEnabled = false as the way to turn polling off.

Marten test-helper: PauseThenCatchUpOnMartenDaemonActivity

  • #​3388 — cold first catch-up appeared to stall (PR #​3394, reported by @​uniquelau). Investigated in depth. The reported mechanism — that coordinator.ResumeAsync() does not start never-started shards — does not hold: under Wolverine-managed distribution the coordinator is WolverineProjectionCoordinator, whose ResumeAsync builds the daemon lazily and starts every shard, bypassing agent assignment entirely. The cold path works, and there are now four tests proving it (including with a second subscription-agent consumer sharing the agent family).

    The real defect was a timeout mismatch, and it explains the reported symptom exactly. The stage runs inside a child TrackedSession whose token cancels at TrackedSession.Timeout5 seconds by default — while the catch-up ignored that token and waited on an internal 60-second budget. The session gave up first and left the catch-up envelope started-but-never-finished, which reads as a hang. This is a genuine 6.16 → 6.17 behavior change: the old active ForceAll finished inside 5 seconds; resume-and-wait on a cold daemon or a busy machine does not. The catch-up now honors the session's token and raises an actionable TimeoutException naming the store and pointing at TrackActivity().Timeout(...).

    If you hit this on 6.17.0–6.17.2, raising the tracked-session timeout is the fix.

Docs

  • New page: gRPC + Sagas (PR #​3389, following @​erikshafer's coverage in PR #​3386). gRPC services can start and continue sagas with no gRPC-specific code — the saga identity must be on the message body, because a gRPC method is a thin shim in front of IMessageBus.InvokeAsync<T> and the chain that runs is the handler's. The header-identified gap is tracked as #​3385, with a clear diagnostic planned.
  • Testing guide (PR #​3395): tracked sessions ignore framework telemetry by default as of this release, and — the trap behind #​3388 — Timeout() bounds the whole session including its stages, so a slow stage like PauseThenCatchUpOnMartenDaemonActivity() is capped by the session's 5-second default, not by any budget internal to the stage.

Full changelog: JasperFx/wolverine@V6.17.2...V6.17.3

6.17.2

Community-issue sweep release. Every fix below shipped same-day from issues filed by the community — thank you all.

Closed issues

  • #​3371 — any ApiExplorer read before server start permanently emptied every OpenAPI document (PR #​3373 by @​uniquelau). WolverineApiDescriptionProvider now enumerates the HttpGraph (complete when MapWolverineEndpoints() returns) instead of the start-time EndpointDataSource, so ASP.NET's version-keyed cache can never freeze an empty first read. If you monitor Wolverine hosts with CritterWatch and expose OpenAPI, upgrade to this release (see JasperFx/CritterWatch#​689).
  • #​3374 — [AsParameters] + compound-handler LoadAsync binding the same route variable generated uncompilable code (CS0136/CS0841, host failed at startup) (PR #​3381). Binding frames are now emitted once per chain and re-homed so any second consumer reuses them; both the [FromRoute] and [AsParameters]-parameter variants are covered. The related OpenAPI gap (route params bound only by LoadAsync missing from the operation) is tracked as #​3380.
  • #​3372 — [AsParameters] query binder silently ignored unparseable values (PR #​3379). New opt-in WolverineHttpOptions.RejectUnparseableQueryValues: a present-but-unparseable query value short-circuits with a 400 ProblemDetails naming the parameter, matching ASP.NET minimal APIs; missing values keep their initializer in both modes. The default flips to strict in Wolverine 7.0.
  • #​3368 — gRPC server-side tenant-id detection (PRs #​3369 by @​erikshafer + #​3382). The server now reads back what the client interceptor stamps: envelope propagation onto the scoped IMessageContext, plus a full ITenantDetectionPolicies-style mirror (opts.TenantId.IsRequestHeaderValue(...), IsClaimTypeNamed(...), DetectWith<T>()) that sets the codegen tenant variable Marten/Polecat session frames consume — with a zero-config default when the client stamps tenant-id. New docs page: gRPC multi-tenancy.
  • #​3375 (docs half) — documented DurabilityMetricsEnabled = false and raising UpdateMetricsPeriod as mitigations for metrics-polling connection load at high tenant-database counts (PR #​3378). The per-node sweeper implementation is in progress on the issue.

Dependency bumps

  • Marten 9.15.0 (sharded-tenancy provisioning repair, marten#​4942) and JasperFx 2.27.0 (daemon block observability, jasperfx#​506/#​507) — the fixes from the marten#​4941 silent-outage incident (PR #​3383).

6.17.1

Wolverine 6.17.1 is a bug-fix release covering EF Core outbox enlistment gaps in Wolverine.Http, persistence provider resolution, HTTP route parameter binding, multi-tenancy message store roles, and a RavenDB startup race. It also upgrades the Marten dependency to 9.14.1.

EF Core & persistence

  • HTTP endpoints that inject a DbContext and cascade messages only through a tuple return are now enlisted in the EF Core outbox, so cascaded messages are no longer sent before the transaction commits when using Lightweight mode (#​3358, #​3362)
  • Wolverine.Http endpoints that persist entities through storage actions (IStorageAction<T> / storage side effects) are likewise enlisted in the EF Core outbox in Lightweight mode (#​3353, #​3357)
  • When both EF Core and Marten (or another catch-all provider like RavenDb) are registered, the selective EF Core persistence provider is now evaluated first regardless of registration order, so DbContext-based handlers get the correct transactional middleware (#​3359, #​3361)
  • MessageStoreRole.Ancillary is now honored for tenanted message stores (static tenants and master-table tenancy) instead of silently reporting Main (#​3351), with the registration behavior now covered by tests across PostgreSQL, SQL Server, SQLite, MySQL, and Oracle

HTTP

  • [FromRoute(Name = "...")] is now honored on plain endpoint method parameters (previously only inside [AsParameters] types), enabling route segments like {journey-id} that aren't valid C# identifiers (#​3356 — thanks to @​outofrange-consulting!)

RavenDB

  • Fixed a node sequence startup race that could cause duplicate node assignments when multiple nodes started concurrently (#​3352)

Dependencies

  • Marten upgraded to 9.14.1, which brings a substantial round of LINQ query-translation improvements plus event-store partitioning, high-water, and AoT fixes (#​3363)

Documentation

  • Corrected the HTTP QUERY verb documentation: transactional middleware is applied based on a chain's dependencies (e.g. taking an IDocumentSession or DbContext), not the HTTP verb (#​3355, #​3360)

6.17.0

Why is this such a big release? Because @​jeremydmiller went on a 3 night vacation and the community decided to throw in issues and pull requests left and right!

A big theme was filling in the remaining gaps of "Name Broker" and "Broker per Tenant" support in every external messaging transport where it made sense to add that rather than just being Rabbit MQ, Azure Service Bus, and hit and miss everywhere else. We also added HTTP QUERY support.

What's Changed

6.16.0

Lot of CritterWatch stuff, optimized SQL Server transport, new options for NServiceBus interop using SQL Server, bug fixes

What's Changed

Full Changelog: JasperFx/wolverine@V6.15.0...V6.16.0

6.15.0

Wolverine 6.15.0 aligns the critter-stack dependencies with the latest stable releases and brings observability, transport, and persistence improvements.

Dependency updates

  • Marten 9.11.0, Polecat 4.6.0, JasperFx / JasperFx.Events 2.16.0, Weasel 9.3.0

GCP Pub/Sub

  • Leader-pinned (ListenOnlyAtLeader()) listeners now use a single shared subscription instead of a per-node subscription, restoring single-consumer semantics (#​3258)
  • Configurable client builders + credential injection (#​3172); Pub/Sub added to CI (#​3191)

Observability & health

  • Shared BackgroundReceiveLoop with receive-loop health reporting, adopted across SQS, Redis, PostgreSQL queue, SQL Server queue, and Kafka (#​3236)
  • Transport connection state surfaced in EndpointHealthSnapshot; IReportConnectionState for NATS, MQTT, Pulsar, Redis (#​3231)
  • Force-restart path for stuck listeners (#​3232)
  • Metrics: every instrument tagged with source (service name) (#​3221); dimensional inbox/outbox/scheduled gauges (source + database); configurable millisecond histogram buckets (#​3224)
  • User-defined service Tags on WolverineOptions, surfaced on ServiceCapabilities (#​3240)
  • Discovered gRPC endpoint → message-type mapping exposed via IGrpcEndpointManifest (#​3235)

Persistence & fixes

  • Reconcile competing "Main" message stores via opt-in policy (#​3226)
  • DB transport binds a same-engine Ancillary store when Main is a different engine (#​3248)
  • EF Core: only call DbContext.Update() for untracked entities in Storage.Update (#​3229)
  • Register IEventStore for Polecat stores so they're discoverable (#​3219)
  • Fix flaky multi-node Polecat event-subscription agent distribution (#​3216)
  • NullMessageStore never throws — no-ops every member for storeless observers
  • Agent restrictions: PersistAgentRestrictionsAsync no-ops on empty list (#​3252); AssignmentGrid.ApplyRestrictions tolerates non-grid paused-agent URIs

Full changelog: JasperFx/wolverine@V6.14.0...V6.15.0

6.14.0

The big ticket item is new interop options for Wolverine to/from MassTransit or NServiceBus using each's SQL Server or PostgreSQL queueing. Also quite a few Pulsar improvements. And community additions too!

What's Changed

Full Changelog: JasperFx/wolverine@V6.13.1...V6.14.0

6.13.1

Patch release on the 6.x line — a Critter Stack dependency refresh plus one targeted fix. No breaking changes.

🐛 Fixes

  • DLQ admin readers tolerate a NULL received_at (#​3165) — the dead-letter explorer could report 0 messages even when dead letters existed; the RDBMS DLQ readers now handle a null received_at column.

⬆️ Dependencies

  • Weasel 9.1.5 → 9.2.3 (#​3166) — refreshes all seven Weasel packages (Core, EntityFrameworkCore, MySql, Oracle, Postgresql, SqlServer, Sqlite). Clean restore + Release build against the current Marten/JasperFx pins.

Full Changelog: JasperFx/wolverine@V6.13.0...V6.13.1

6.13.0

Wolverine 6.13.0 on the 6.0 line (JasperFx 2.x, net9.0/net10.0). The headline is a top-to-bottom Kafka integration re-evaluation (epic #​3134) that makes the transport idiomatic and high-throughput, plus [AsParameters] HTTP fidelity fixes and event-subscription/projection-distribution hardening. No breaking changes.

🚀 Kafka integration re-evaluation (#​3134)

  • Commit-strategy overhaul with CommitMode (#​3152) — StoreThenAutoFlush (default, non-blocking idiomatic throughput), PerMessage, and CommitOffsetsAfterCount/AfterInterval batch modes.
  • In-flight-safe offset watermark across all commit strategies (#​3162) — under concurrent out-of-order completion the committed/stored position never advances past a still-in-flight message; tolerates compacted/read_committed offset gaps.
  • Scale-out & concurrency — cooperative-sticky rebalancing + static membership (#​3154), and opt-in intra-partition concurrency by key with ordered-per-key guarantees via the durable inbox (#​3158).
  • Cold start vs. live tail — first-class AutoOffsetReset and ephemeral hot-tail / broadcast consume (#​3155).
  • Bounded one-shot topic replay by offset/timestamp via Assign (#​3156).
  • Idempotency & exactly-once — idempotent producer + read_committed isolation, with EOS guidance (#​3157).
  • Non-blocking tiered retry topics via the OnException<T>().MoveToKafkaRetryTopic(...) DSL (#​3160).
  • Fix: ExtendConsumerConfiguration now preserves parent/global consumer settings (#​3151).

🌐 HTTP — [AsParameters] (#​3135)

  • OpenAPI route-type fidelity + multiple-body guard (#​3141)
  • Optional [FromBody] in [AsParameters]: binds null with required:false (#​3142)
  • Fix [AsParameters] + [FromBody] + [WriteAggregate] codegen 500 (#​3143/#​3144)
  • Docs: [AsParameters] as the idiomatic route/body split (#​3145)

🗄️ Event subscriptions, projections & distribution

  • Lift event-subscription distribution into core; fix Polecat managed distribution (#​3136, closes #​3133)
  • Rebuild a registered projection with no live agent — Inline/Live/undistributed (#​3163)
  • Fix: SQL Server node-capabilities delimiter must be newline, not comma — Polecat managed-distribution startup crash (#​3164)

🐛 Other fixes

  • Fix circuit breaker (#​3132) and RabbitMQ post-#​3132 cleanup (#​3138)

Full Changelog: JasperFx/wolverine@V6.12.0...V6.13.0

6.12.0

What's Changed

Full Changelog: JasperFx/wolverine@V6.11.0...V6.12.0

6.11.0

The Polecat change was necessary for CritterWatch persistence with SQL Server. The inbox cleanup should help with very busy Wolverine systems be a bit easier on databases.

What's Changed

Full Changelog: JasperFx/wolverine@V6.10.0...V6.11.0

6.10.0

New Polecat integration for ancillary store support within Wolverine that folks doing modular monoliths will want -- and we needed in CritterWatch post haste. Also new options for configuring Redis.

What's Changed

Full Changelog: JasperFx/wolverine@V6.9.0...V6.10.0

6.9.0

This release was mostly about CritterWatch, but does have some new DLQ functionality, which was meant to complement CritterWatch. Couple bug fixes too though.

What's Changed

Full Changelog: JasperFx/wolverine@V6.8.0...V6.9.0

6.8.0

What's Changed

New Contributors

Full Changelog: JasperFx/wolverine@V6.7.0...V6.8.0

6.7.0

What's Changed

...

Description has been truncated

---
updated-dependencies:
- dependency-name: WolverineFx
  dependency-version: 6.24.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Pull requests that update a dependency file labels Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants