Skip to content

Wolverine 6.30.0

Choose a tag to compare

@jeremydmiller jeremydmiller released this 25 Aug 00:13

Wolverine 6.30.0 is a large release built around one headline feature — a new endpoint mode — plus the usual crop of transport fixes, and a couple of long-standing multi-tenancy and HTTP gaps closed.

EndpointMode.NativeAck

The main event. Buffered's throughput and partitioning with Inline's no-loss guarantee, and no database required.

A broker delivery is held unacknowledged while the envelope flows through an in-memory, optionally group-partitioned execution block, and is settled natively when the handler pipeline terminates. Nothing is acknowledged ahead of its handler, so work parked in a lane when a node goes away comes back rather than vanishing.

opts.ListenToRabbitQueue("orders")
    .ProcessInParallelWithNativeAcks();

The guarantee, stated exactly: no two messages sharing a group id execute concurrently. Ordering is per-slot best-effort, not per-group guaranteed; redelivery may reorder. Anything needing strict order under failure keeps the durable inbox.

Transport support is opt-in and default-closed — a transport must explicitly claim the mode, because most settlement models cannot express out-of-order completion. Adopted by RabbitMQ, Amazon SQS, Azure Service Bus, NATS JetStream, Redis Streams, Pulsar and GCP Pub/Sub (#3708, #4046, #4047, #4050, #4051, #4052, #4053).

Supporting work in the same wave:

  • Lease renewal for queued envelopes on clocked transports — SQS, ASB, JetStream and Pub/Sub run a clock on an unsettled delivery, and the risk window is lane queue time plus handler time (#4048).
  • In-memory idempotency guard, an opt-in duplicate filter for a mode with no inbox row to deduplicate against (#3710).
  • Global partitioning across sharded queues (#3709).
  • Listener mode coherence validation, which caught that RabbitMQ queues default to Inline — so sharded topologies were silently unpartitioned without an explicit BufferedInMemory() (#3712, #4022).
  • A five-node chaos reproduction under webhook flood, measuring the real duplicate rate on abrupt node loss (#3713).

Multi-tenancy

  • Conjoined EF Core tenancy now works when Marten owns the message store via IntegrateWithWolverine(). Marten hands Wolverine an NpgsqlDataSource rather than a connection string, and NpgsqlDataSource.ConnectionString deliberately omits the password — so there is a new DbDataSource overload of AddDbContextWithWolverineManagedConjoinedTenancy that carries credentials through intact. A second defect on the same path is fixed too: IntegrateWithWolverine() never registered the tenant partitioning provider, so PartitionPerTenant() failed (#4044).

HTTP and event sourcing

  • [StreamState] and [StreamEvents] — new parameter attributes for handlers whose read is the raw stream rather than the folded aggregate, for timeline and audit shaped endpoints that [ReadModel] cannot express. Store-agnostic across Marten, Polecat and Fisher; Marten batches both fetches into a single round trip (#3627).
  • Marten concurrency conflicts as 409 — a documented, tested recipe for mapping optimistic-concurrency failures on [WriteAggregate] endpoints to ProblemDetails instead of an unhandled 500. Note that StreamLockedException derives from MartenException, not ConcurrencyException, so catching only the latter silently leaves FetchForExclusiveWriting returning 500s (#3764).
  • Event Model slices per routeHttpChainDescriptor and GrpcRpcDescriptor now carry the slice the route is, so a consumer walking endpoint by endpoint sees it next to the route rather than only through the assembled model (#4000).

Transport fixes

  • Pulsar: requeue, scheduled retry and dead-letter routing implemented (#3797). A global failure rule was silently disabling every user error policy application-wide (#4075). Hot-tail listeners silently dropped deferred messages in every mode (#4060).
  • GCP Pub/Sub: listener shutdown could hang on in-flight callbacks (#4065); exhausting MaxTotalAckExtension silently delivered a concurrent duplicate rather than reporting anything (#4066); effective listener concurrency was not what the configuration implied, and the flow-control bound is global per SubscriberClient rather than per inner client (#4067). PubsubTopicOptions.OrderBy gained a configuration surface (#4087).
  • Redis: DeleteStreamEntryOnAck silently never acked on Redis < 8.2, where XACKDEL is unsupported (#4058).
  • Ack reliability: a shared ack-attempt budget across stacked retry blocks, and terminal-failure classification for Azure Service Bus and SQS so a permanent settle failure stops rather than burning the whole budget (#4012).

Upgrading

Additive. EndpointMode.NativeAck is opt-in per endpoint and default-closed per transport, and MaximumBrokerRedeliveries defaults to off. Requires JasperFx 2.55.0.