KubeMQ
ConnectorsKafkaConcepts

What Differs from Kafka

Understand the KubeMQ Kafka behaviors that need attention before moving a production workload from Apache Kafka, including share groups.

KubeMQ accepts standard Kafka clients, but three behaviors need an explicit check before a production migration. Workloads that use share groups (KIP-932) have their own list, in Share groups below.

Delivery acknowledgements

On a multi-node deployment, use acks=1 or acks=all. An acks=0 produce sent to a follower is not forwarded to the leader and is dropped without a response. A single-node deployment has no follower, so this case does not apply. See Durability and retention.

Retention limits

retention.ms can evict records when it is set to a finite positive value. retention.bytes is accepted and returned by the API, but it does not enforce a size limit. Set a time retention limit and monitor storage until size-based retention is available. See Durability and retention.

Keyed partitioning

The Kafka client chooses the partition for a keyed record. Java and franz-go use a murmur2-based hash by default, while librdkafka-based clients such as kcat and confluent-kafka use CRC32. The same key can therefore land on different partitions when producer libraries differ. Use one partitioning strategy for a topic that depends on per-key ordering. See Partitions and ordering.

Share groups

Share groups are generally available. These are the deliberate differences from Apache Kafka 4.3 — documented scope limits, not open defects. The first three are the ones most likely to matter. See Share Groups for how each setting named here works.

  1. A dead consumer's records come back later. When a consumer dies holding records, KubeMQ hands them out again only when their acquisition lock expires — 30 seconds by default, the group's share.record.lock.duration.ms. Kafka releases them as soon as the connection drops. Nothing is lost; redelivery is only slower.
  2. ShareFetch does not wait. It answers as soon as it has a result, instead of waiting up to the client's requested maximum wait.
  3. The in-flight limit counts stored batches. share.partition.max.record.locks (2000 by default) counts the batches a producer sent, not records, so with a batching producer more than 2000 records can be in flight.
  4. Fetch sizes count stored batches. share.acquire.mode=record_limit is honored in whole stored batches, and the default batch_optimized mode also counts stored batches, so a fetch can return more than max.poll.records records.
  5. Release and renew act on the whole stored batch. A Release, a renew, and any acknowledgement deferred behind a renew apply to every record in the same stored batch, so records you already accepted in a released record's batch come back as duplicates.
  6. share.assignment.interval.ms is refused. Every member is assigned every partition, so there is no interval to tune.
  7. Describing all group configs lists only groups with members. ListConfigResources (key 74) is not served, so kafka-configs.sh --entity-type groups --describe without --entity-name lists only groups that have members.
  8. A leader change counts a delivery attempt. Every record in flight when a partition's leader changes — a killed node, any restart including a clean one, each step of a rolling upgrade — counts one delivery attempt. A low share.delivery.count.limit can therefore archive a healthy record early; keep it at 5 or higher on groups that must survive rolling restarts. With CONNECTORS_KAFKA_SHARE_FEATURE_VERSION=1, a record released because its consumer left, was evicted, or dropped the topic from its subscription is handed out again without counting.
  9. Some late acknowledgements are not refused. An Accept, Release or Reject for a record the consumer no longer holds is refused INVALID_RECORD_STATE with nothing applied, as in Kafka, when another consumer holds the record now. When nobody holds it, KubeMQ does not refuse it: a released record takes the Accept or Reject, and a record already acknowledged or never acquired ignores it. Kafka answers INVALID_RECORD_STATE in those cases, so on KubeMQ a late acknowledgement from a consumer whose lock expired can be reported as successful.
  10. A new partition starts on the next heartbeat. A partition the group first sees after it joined starts at the group's start position on the member's next heartbeat.
  11. by_duration uses ingest time. share.auto.offset.reset=by_duration:<ISO-8601> compares KubeMQ's own ingest time, not the producer's CreateTime.
  12. Group configuration outlives the group. A group's configuration is kept when the group is deleted (not verified against Kafka's own behavior), a value stored after clamping is never re-clamped when the server's bounds later tighten, and no config synonyms are reported.
  13. An unknown group describes as Dead. ShareGroupDescribe for a group that never existed answers state Dead, not GROUP_ID_NOT_FOUND.
  14. Deleting start offsets resets to the log start. DeleteShareGroupOffsets moves the group to the start of the log, where Kafka 4.3 re-applies share.auto.offset.reset.
  15. Stricter group ids. AlterShareGroupOffsets refuses a group id no KubeMQ consumer could join (containing /, * or >, a leading or trailing dot, .., or empty) with INVALID_GROUP_ID. Every API that can create group state refuses a group id that is not valid UTF-8, where Kafka substitutes the invalid bytes and proceeds: the group APIs, ShareGroupHeartbeat and AlterShareGroupOffsets answer INVALID_GROUP_ID; ShareFetch, ShareAcknowledge and group-config requests answer INVALID_REQUEST.
  16. A read_committed edge case costs one attempt. An aborted-transaction record or transaction marker acquired just before the share coordinator stops costs one counted delivery attempt before it is discarded. The consumer never sees it.
  17. A cap on the number of share groups. Share groups count against CONNECTORS_KAFKA_MAX_GROUPS (10000 by default), separately from classic groups, and keep counting until they are deleted. Apache Kafka 4.3 has no such limit.
  18. librdkafka's share consumer stays advisory. It is an upstream preview; KubeMQ checks it but does not certify it.

Not built yet: a dead-letter queue for share groups (KIP-1191, arriving in Kafka 4.4), and acknowledgements inside transactions.

Upgrades: a cluster must follow the share-group upgrade rule when moving from a release before share groups became generally available — see Upgrading a cluster.

Was this page helpful?

On this page