Skip to main content

Lanes

One subscription hands its messages over one at a time; workers(n, by_key) spreads them over lanes keyed by the nats-partition-key header.

One subscription's dispatch: one stream of messages and two lanes keyed by the partition-key headerThe subscriptiona Core subject or a pull consumerNatsSubscriber streamone message at a timeworkers(2, by_key)lane = hash of nats-partition-keyLane 1key a, in orderLane 2key b, in orderA message without the header has no key:such messages rotate over the lanesAcross processes: a queue group or a shared durablehands each message to one member
The diagram as text
  1. The subscription a Core subject or a pull consumer → NatsSubscriber stream one message at a time: delivered
  2. NatsSubscriber stream one message at a time → workers(2, by_key) lane = hash of nats-partition-key: one at a time
  3. workers(2, by_key) lane = hash of nats-partition-key → Lane 1 key a, in order
  4. workers(2, by_key) lane = hash of nats-partition-key → Lane 2 key b, in order
  5. A message without the header has no key: such messages rotate over the lanes
  6. Across processes: a queue group or a shared durable hands each message to one member

Without workers, one loop runs the handler for each message in turn. The subscription's order is kept, and nothing runs side by side.

workers(n, by_key) runs n lanes. A message goes to the lane its key hashes to, and a lane runs its messages in order. NATS has no partition of its own, so the key is the nats-partition-key header, which the sender sets. Messages of one key keep their order, and different keys run side by side.

Across processes the server shares the work. A queue group hands each Core message to one member, in the order the server picks. Subscriptions that bind one durable consumer take each JetStream message in turn. max_ack_pending bounds how many messages of a consumer wait for an answer at once, across all its members.

A batch handler takes up to the size the mount site names. On JetStream one batch is one fetch, closed early by pull_expires. On Core the batch is assembled on the client and closes at the size or 10 milliseconds after its first message.