Transport semantics
This document mirrors the implemented TransportSemantics profiles. A true capability describes framework-observable behavior; it is not a claim about application-level exactly-once processing, remote cancellation, or external security.
Authoritative profiles
| Transport | Delivery | Ordering | Durability | Pub/sub | Competing | Native req/reply | Native stream | Partitioning | Backpressure | Cancellation |
|---|---|---|---|---|---|---|---|---|---|---|
| InMemory | At most once | Per channel | Volatile | Yes | Yes | No | No | No | Yes | Yes |
| REST client/server | At most once | None | Volatile | No | No | Yes | No | No | Yes | Yes |
| gRPC client/server | At most once | None | Volatile | No | No | Yes | Yes | No | Yes | Yes |
| Kafka | At least once | Per partition | Durable | Yes | Yes | No | No | Yes | Yes | Yes |
| RabbitMQ | At least once | Per channel | Durable | Yes | Yes | No | No | No | Yes | Yes |
| ZeroMQ | At most once | Per channel | Volatile | Yes | Yes | No | No | No | Yes | Yes |
All unspecified TransportSemantics Boolean properties are false.
Delivery, ordering, and durability
- At most once means AnyProtocol does not redeliver after a failed accepted send. Loss remains possible on process, connection, or peer failure.
- Kafka at least once uses producer idempotence and
Acks.All, disables consumer auto-commit/offset-store, and stores offsets after successful handling. Handler re-execution remains possible. - RabbitMQ at least once uses persistent mandatory confirmed publishing, manual acknowledgements, confirmed retry republishing, and a durable dead-letter exchange. Handler re-execution remains possible.
- Per channel applies to the transport's send path; concurrent handlers (
MaxConcurrency > 1) can complete out of order. - Per partition is Kafka's boundary.
[PartitionKey]is supported only by Kafka and is required on every operation whenRequireOrdering(PerPartition)is configured. - Volatile transports do not survive process restart. Kafka durability still depends on broker replication, retention, acknowledgements, and topic configuration.
There is no exactly-once application guarantee.
Fan-out and competing consumers
InMemory and ZeroMQ deliver ungrouped subscriptions to every subscriber. Subscribers with the same non-null ConsumerGroup compete, with one group member selected per message.
Kafka creates a unique group when ConsumerGroup is null, producing fan-out across subscriptions. An explicit shared group delegates competition and partition assignment to Kafka. MaxConcurrency defaults to one and creates multiple workers for a subscription.
RabbitMQ uses a server-named exclusive auto-delete queue for a null group, so each subscription receives a copy. A named group maps to one stable durable queue, so its consumers compete. Prefetch bounds unacknowledged delivery; MaxConcurrency > 1 can complete messages out of order.
REST and gRPC server transports dispatch mapped requests and do not expose subscriptions, fan-out, or consumer groups.
Request/reply
REST and gRPC implement IRequestReplyTransport; gRPC also implements IStreamingTransport. InMemory, Kafka, RabbitMQ, and ZeroMQ emulate request/reply using a unique _anyprotocol.reply.* subscription and correlation headers. A positive client timeout defaults to 30 seconds.
Kafka reply topics default to five-minute retention. The other emulated transports have no durable reply storage.
Streaming and backpressure
gRPC uses native server streaming. InMemory, Kafka, and ZeroMQ emulate streams over publish/subscribe. The emulated StreamEngine uses a bounded channel of 32 envelopes by default with BoundedChannelFullMode.Wait, validates sequence numbers, and propagates stream completion/fault envelopes.
REST does not support streaming contracts. Backpressure means local async writes wait when bounded transport/stream buffers are full; it does not guarantee bounded memory in every internal or broker queue.
Cancellation
All built-in profiles set SupportsCancellation = true: public async operations honor their cancellation token while sending, receiving, waiting, or dispatching. REST and gRPC pass cancellation to their client calls. Kafka and ZeroMQ cancellation cannot recall a message already accepted by a broker/socket. Emulated cancellation does not create a transport-level remote cancel message.
Readiness
Readiness is separate from TransportSemantics:
| Transport | Readiness behavior |
|---|---|
| InMemory | Ready until disposed |
| REST server | Ready only after MapAnyProtocol marks endpoints mapped |
| REST client | Not assessed (does not implement ITransportReadiness) |
| gRPC server | Ready only after MapAnyProtocolGrpc marks the service mapped |
gRPC client with GrpcChannel |
Connects and checks ConnectivityState.Ready |
gRPC client with CallInvoker |
Unknown |
| Kafka | Ready when broker metadata returns at least one broker; fixed 2-second metadata timeout |
| RabbitMQ | Ready when the connection, channel, and durable exchange are available; bounded by ReadinessTimeout |
| ZeroMQ server | Ready while the socket loop runs and bound successfully |
| ZeroMQ client | Unknown because no non-destructive peer probe exists |
Unknown does not fail the aggregate AnyProtocol readiness check; NotReady does.
Known limitations
- InMemory is process-local and intended for local execution/testing.
- REST has no subscriptions or streams.
- gRPC exposes unary and server-streaming dispatch, not publish/subscribe.
- Kafka topic auto-creation defaults are development-oriented; at-least-once delivery requires idempotent handlers.
- RabbitMQ ordering requires a single active consumer/handler per queue; retries and recovery require idempotent handlers.
- ZeroMQ has no broker persistence, delivery acknowledgements, built-in reconnect state store, or non-destructive client peer readiness.
- Consumer groups, retries, and
[Idempotent]do not provide deduplication. - Ordering requirements are exact matches: requesting
PerChanneldoes not accept aPerPartitionprofile, or vice versa.