Skip to content

Messaging delivery models

Forze separates its messaging ports by delivery model, not by backend. The model decides what a consumer is promised — whether a message survives a crash, whether it can be replayed, how a stalled consumer recovers — so choosing the model is the real decision; the backend is an implementation detail behind it. There are four.

The four models

Model Guarantee Recovery unit Replayable Fits
Queue at-least-once per-message ack / nack(requeue) no work items, jobs, commands
Ack-stream at-least-once, per group per-message ack + explicit claim from group creation ordered events, competing consumers
Commit-stream at-least-once, per group per-partition offset commit; broker rebalance any offset / timestamp high-throughput logs, event sourcing
Pub/sub at-most-once none no lossy broadcast (presence, cache-bust)

Three of the four — queue, ack-stream, commit-stream — are at-least-once at the transport, and those three reach exactly-once effect when paired with the inbox: the consumer dedups on a stable message id inside the same transaction as its writes, so a redelivery is a no-op. The inbox only turns at-least -once into exactly-once — it cannot recover a message the transport never delivered, so it does not apply to pub/sub, which is at-most-once and can drop a message before the inbox ever sees it. Choose an at-least-once model whenever a consumer must eventually see every message.

Queue

QueueSpec[M] — a competing-consumers work queue. Each message is delivered to one consumer, which acks on success or nacks (with or without requeue) on failure; the broker redelivers after a visibility timeout. No offsets, no replay — once acked, a message is gone. Reach for it for jobs and commands where order across items does not matter and each item is handled once.

Ack-stream (Redis-class)

AckStreamGroupQueryPort — an ordered, replayable log consumed by a consumer group with a Pending Entries List. Each entry is delivered to exactly one consumer in the group and stays pending until that consumer acks it; a consumer that crashes strands its pending entries, and any worker recovers them with claim (transferring ownership of entries idle past a threshold). Use it for ordered events with competing consumers on a Redis-class backend, where you want per-message acks and explicit stranded-work recovery.

Commit-stream (Kafka-class)

CommitStreamGroupQueryPort — a partitioned, offset-committed log. Instead of acking each message, a consumer commits a StreamPosition, and that single offset acknowledges every message up to it on that partition (a high-water mark). Partitions are assigned across live group members by the broker, so recovery from a crash is a rebalance, not a per-message claim. The control plane (CommitStreamGroupAdminPort) provisions topics, positions a group, inspects lag, and reset_offsets to replay from any offset or timestamp.

Consume it with the CommitStreamGroupConsumer runner, which commits the offset only after process_with_inbox succeeds — never auto-commit, which would decouple the commit from processing and silently drop the guarantee. On a poison message it either produces to a dead-letter stream and commits past it (freeing the partition) or, with no dead-letter route, pauses and alerts — never a silent skip. Reach for it for high-throughput logs and event sourcing where partitioned ordering and replay matter. The production backend is Kafka (or any Kafka-protocol broker).

Ack vs commit

Both are consumer groups over an ordered log; the difference is the acknowledgment unit — per-message id (ack) versus per-partition offset (commit). That one axis is why the ports carry the Ack / Commit prefix.

Pub/sub

PubSubSpec[M] — fire-and-forget fan-out to whoever is listening right now. A subscriber offline at publish time misses the message; there is no ack and no replay. It is at-most-once past the broker — legitimate for lossy broadcast (cache invalidation, presence, live notifications), wrong for anything that must eventually be seen. When in doubt, pick a queue or a stream.

Choosing

  • Must every item be handled, order-independent?Queue.
  • Ordered events, competing consumers, per-message acks?Ack-stream.
  • Partitioned high-throughput log, replay, offset commits?Commit-stream.
  • Lossy broadcast to live listeners only?Pub/sub.

The contract surface for each lives in Streaming & pub/sub and Messaging & the outbox.