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:
| Acknowledgement | Effect |
|---|---|
| Accept | Terminal — the record is durably consumed; the group's start-offset advances past it. |
| Release | The record becomes available for redelivery (to this or another member), with its delivery count incremented. |
| Reject | Terminal, 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.
Related
Consumer Groups
The classic Join/Sync/Heartbeat protocol, durable per-group offsets, and static membership — what a share group replaces the partition-assignment model with.
Capabilities
Every implemented Kafka API and its version range, including the share-group keys and their preview status.
Fitness matrix
What's drop-in, preview, roadmap, and unsupported when running Kafka workloads on KubeMQ.
Was this page helpful?
Transactions & EOS
Exactly-once semantics on KubeMQ — the transactional producer, read_committed isolation, consume-transform-produce, and producer fencing.
Authentication
Authenticate Kafka clients to KubeMQ — SASL/PLAIN and SCRAM, OAUTHBEARER/OIDC federated tokens, mTLS client certificates, and the ACL authorization model.