Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

Consumer Groups

Consumer groups enable load-balanced event delivery across multiple consumers. Match patterns are distributed among group members, ensuring each event is processed by exactly one consumer in the group.

Consumer groups provide:

  • Load balancing: Events are distributed across group members on a first-available basis (JetStream pull consumer).
  • Durability: Group state persists across server restarts.
  • Resiliency: Rejoining a group resumes from the last acknowledged event. Note: groups start with DeliverNewPolicy, so events published before the group was first created are not delivered.
  • At-least-once delivery: Combined with Manual Acknowledgment, events are redelivered if a consumer fails.

Consumer groups must be created before they can be joined. This is an administrative task performed via the Go, Node or Python SDK, or the HTTP API.

client := ironflow.NewClient(ironflow.ClientConfig{
APIKey: os.Getenv("IRONFLOW_API_KEY"),
})
// Create a consumer group for reliable processing
group, err := client.CreateConsumerGroup(ctx, ironflow.ConsumerGroupConfig{
Name: "order-workers",
Pattern: "events:order.>",
AckMode: ironflow.AckModeManual,
MaxRedeliveries: 5,
RedeliverDelayMs: 10000,
})

Once created, multiple instances of your application can join the group to share the workload.

import { ironflow } from "@ironflow/browser";
// Standard join method
let sub: Awaited<ReturnType<typeof ironflow.joinConsumerGroup>>;
sub = await ironflow.joinConsumerGroup("order-workers", "events:order.>", {
onEvent: async (event) => {
console.log("Processing order:", event.data.orderId);
await sub.ack(event.eventId);
}
});

Joining a group that does not exist fails with NotFound on the JoinConsumerGroup RPC, in Go JoinConsumerGroup and on the stream below. The browser joinConsumerGroup helper does not fail: it creates the group (see the note above). Go and Node can also join over one ConnectRPC stream that carries the acks; see Ack over one stream.


When multiple consumers (e.g., three browser tabs or four Go microservices) join the same group, each event is delivered to whichever member is next available to receive it. The pattern is not strict round-robin — a fast consumer that completes acks quickly may receive more events than a slow one.

If a consumer disconnects, its pending (unacknowledged) events are automatically redelivered to another group member after the visibility timeout (RedeliverDelayMs).

With the default buffer backpressure, an event that reaches a member whose buffer (MaxInflight) is full goes to the first member that has room, and the group waits up to RedeliverDelayMs for room. If no member frees room in that time, the event is redelivered after RedeliverDelayMs. Each redelivery spends one of the MaxRedeliveries attempts, and an event still not placed after the last attempt is dropped. Each attempt waits up to RedeliverDelayMs for room and then RedeliverDelayMs before the next one, so with the defaults (3 redeliveries, 5 seconds) an event is dropped after about 35 seconds with no free member. Events waiting for redelivery also count toward MaxInflight, so a group that has that many waiting pauses new deliveries until they are placed. Raise MaxRedeliveries or RedeliverDelayMs for consumers that can fall further behind.

When the last member leaves, the group stops pulling. Events published in the meantime wait in the group’s durable consumer, spend no redelivery attempts, and go to the next member that joins.


FeatureRegular SubConsumer Group
DeliveryFan-out (everyone gets everything)Load-balanced (one member gets it)
PersistenceVolatile (lost on disconnect)Durable (rejoin to resume)
AcknowledgmentAutomaticManual ack for at-least-once
Use CaseReal-time dashboards, debugging.Critical workers, background jobs.