KubeMQ
ConnectorsKafkaHow-to guides

Share Groups

Queue-style consumption with Kafka share groups (KIP-932) on KubeMQ, in preview — per-record acquire and acknowledge, and how it differs from consumer groups.

Kafka share groups (KIP-932) give the Kafka connector a second, queue-style way to consume a topic. Instead of assigning whole partitions to consumers, individual records are acquired, processed, and acknowledged one at a time — closer to how a KubeMQ Queue behaves than to a classic consumer group.

Share groups are supported in preview, not GA. The data plane — ShareGroupHeartbeat(76), ShareFetch(78), ShareAcknowledge(79), plus the admin/observability keys ShareGroupDescribe(77), DescribeShareGroupOffsets(90), AlterShareGroupOffsets(91), and DeleteShareGroupOffsets(92) — is implemented and advertised: acquire, Accept/Release/ Reject acknowledgements, multi-record batches, and cluster follower→leader ShareFetch forwarding are all proven against a real client. What hasn't run yet is the full multi-client share-group conformance matrix — the share-group analogue of the transactions/EOS conformance matrix — so this carries a preview verdict, not a GA guarantee. Never treat share groups as "fully supported" until that matrix lands. Track status on the fitness matrix and the capabilities reference.

How share groups differ from classic consumer groups

A classic consumer group assigns whole partitions to members via Join/Sync/Heartbeat — one partition, one owning consumer at a time, with a rebalance whenever membership changes. A share group throws that model out: every member can receive records from every partition, and the unit of ownership is a single record (or a contiguous batch), not a partition. There's no partition-assignment protocol to reason about — just heartbeat-based membership (ShareGroupHeartbeat) plus per-record acquisition on fetch.

That makes a share group behave much more like a KubeMQ Queue: multiple workers pull from a shared backlog, a record goes to exactly one worker at a time, and a worker that fails to process it releases the record back for someone else to pick up — see the acquire/acknowledge cycle below.

Acquire, deliver, acknowledge

Where a classic consumer commits offsets in bulk, a share consumer acknowledges per record (or per contiguous batch) with one of three outcomes:

AcknowledgementEffect
AcceptTerminal — the record is durably consumed; the group's start-offset advances past it.
ReleaseThe record becomes available for redelivery (to this or another member), with its delivery count incremented.
RejectTerminal, like Accept, but signals "skip this record" rather than "processed successfully" — it never redelivers.

A record the client never acknowledges is released automatically once the 30-second acquisition lock — the AcquisitionLockTimeoutMillis the connector advertises in every ShareFetch response — expires. That's the same outcome as an explicit Release, and it also counts as a delivery attempt.

Redelivery has a limit. A record redelivered (via Release or a lock timeout) more than 5 times is archived — the group's cursor advances past it permanently, so a single poison record can never wedge the partition for everyone else. The record's bytes are untouched; a plain Fetch consumer on the same topic still sees it.

Produce and share-consume

Both client libraries below drive the full flow — produce onto a plain topic, then acquire, process, and acknowledge from a share group:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/twmb/franz-go/pkg/kgo"
)

func main() {
    ctx := context.Background()

    // A share group has no partition assignment — every member shares the
    // same pool of records, each one acquired individually.
    sc, err := kgo.NewClient(
        kgo.SeedBrokers("localhost:9092"),
        kgo.ShareGroup("orders-share-group"),
        kgo.ConsumeTopics("orders"),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer sc.Close()

    for {
        fetches := sc.PollFetches(ctx)
        if errs := fetches.Errors(); len(errs) > 0 {
            log.Fatal(errs[0].Err)
        }

        var accepted []*kgo.Record
        fetches.EachRecord(func(r *kgo.Record) {
            fmt.Printf("acquired offset=%d attempt=%d: %s\n", r.Offset, r.DeliveryCount(), r.Value)
            // process the record here — on failure, MarkAcks with AckRelease
            // or AckReject instead of AckAccept below.
            accepted = append(accepted, r)
        })

        sc.MarkAcks(kgo.AckAccept, accepted...)
        if err := sc.FlushAcks(ctx); err != nil {
            log.Printf("flush acknowledgements: %v", err)
        }
    }
}
// KafkaShareConsumer is Apache Kafka's own KIP-932 preview client (early
// access) — illustrative only. Its API is still evolving, so verify
// the exact surface against the Apache Kafka client version you pin.
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "orders-share-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

try (KafkaShareConsumer<String, String> consumer = new KafkaShareConsumer<>(props)) {
    consumer.subscribe(Collections.singleton("orders"));

    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(5000));
        for (ConsumerRecord<String, String> record : records) {
            // process the record here — on failure, acknowledge RELEASE or
            // REJECT instead of ACCEPT below.
            consumer.acknowledge(record, AcknowledgeType.ACCEPT);
        }
        consumer.commitSync(); // flushes the pending acknowledgements
    }
}

Only these two clients have a share-consumer API today. kcat, confluent-kafka (Python), kafkajs, Confluent.Kafka (C#), and rdkafka (Ruby/Rust) have no KIP-932 share-consumer surface yet, so there's no gap-fallback tab to show for them. franz-go is the client the connector's own share-group support was validated against; Java's KafkaShareConsumer is Apache Kafka's own early-access preview client.

Was this page helpful?

On this page