DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
...
The TopologyDescription common struct mirrors the struct used by StreamsGroupTopologyDescriptionUpdateRequest (same field names and shape). Because Kafka RPC schemas do not share common structs across message files, the struct is duplicated in StreamsGroupDescribeResponse.json. Setting these fields does not change the ErrorCode on the DescribedGroup: a group with a successful describe but a missing or failed topology fetch still returns ErrorCode=NONE. The TopologyDescriptionStatus field tells the caller why TopologyDescription is null, so that "waiting for first push" (NOT_STORED) can be distinguished from "broker-side fetch failed" (ERROR) without an error-level change to the describe result.
DeleteGroupsResponse Change
No schema change. A new error code is added to the per-group ErrorCode slot of the existing DeleteGroupsResponse:
...
The sequence below traces the four user-visible interactions — a successful push, a describe, an explicit DeleteGroups, and broker-driven cleanup of a naturally-expired group. Plugin calls return CompletableFutures; the broker awaits them asynchronously.
| View file | ||||
|---|---|---|---|---|
|
Broker Side
The plugin is instantiated at broker startup if
group.streams.topology.description.plugin.classis configured. A broker without a plugin returnsUNSUPPORTED_VERSIONforStreamsGroupTopologyDescriptionUpdateand never setsTopologyDescriptionRequired, so the RPC is only sent against a plugin-configured broker.After a successful
StreamsGroupHeartbeat, the broker decides whether to setTopologyDescriptionRequired=truepurely from the group's persisted state — no plugin RPC is involved. Members withSTALE_TOPOLOGYstatus are skipped. For all other members the broker sets the flag iffStoredTopologyEpoch != currentTopologyEpochANDLastFailedTopologyEpoch != currentTopologyEpochAND no per-group back-off is in its window. The back-off is in-memory state (keyed bygroupId, carryingtopologyEpoch+nextAttemptMs) that arms or extends every time the flag is set and additionally on a transientsetTopologyfailure; consecutive arms double the window from 30 s up to 1 h. It clears on a successful push, on a permanent failure (whereLastFailedTopologyEpochratchets), and implicitly on any topology-epoch advance. The same mechanism covers unresponsive plugins and clients that never push (for example, withtopology.description.push.enabled=false).On
StreamsGroupTopologyDescriptionUpdate, the broker checks theREADACL on the group and that a plugin is configured, then validates theMemberId: an emptyMemberIdis rejected withINVALID_REQUEST, a non-existing streams group withGROUP_ID_NOT_FOUND, and aMemberIdnot matching any current member withUNKNOWN_MEMBER_ID. The broker enforces no size limit; the plugin decides what it is willing to store. The broker then callssetTopologyon the plugin. On success it writes a metadata record settingStoredTopologyEpoch = pushedEpochand the response carriesNONE. OnInvalidRequestExceptionorStreamsTopologyDescriptionTooLargeExceptionit writesLastFailedTopologyEpoch = pushedEpochso subsequent heartbeats at the same epoch do not re-solicit. Any other exception maps toSTREAMS_TOPOLOGY_DESCRIPTION_UPDATE_FAILED, is logged at WARN, and is treated as transient — no metadata record is written and the next heartbeat re-solicits. If the plugin call succeeds but the metadata-record write fails, the next heartbeat sees the drift, re-solicits, and the idempotent re-push closes the gap.On
DeleteGroups, the broker callsdeleteTopologyon the plugin before writing the group tombstone, for each requested streams group withStoredTopologyEpoch != -1. On plugin success the group is tombstoned and the per-groupErrorCodeisNONE. On plugin failure the group is not tombstoned and the per-groupErrorCodeis set toSTREAMS_TOPOLOGY_DESCRIPTION_DELETE_FAILED(the plugin's exception is logged at WARN on the broker); the operator retries the request once the plugin recovers, or unsets the plugin config to bypass it. Other groups in the same batch are unaffected — the failure is reported per group. This ordering matches the natural-expiration cleanup below, which also defers tombstoning untilplugin.deleteTopologysucceeds.- On
StreamsGroupDescribewithIncludeTopologyDescription=true, the broker callsgetTopologyon the plugin only whenStoredTopologyEpoch == currentTopologyEpochfor that group; otherwise it reportsNOT_STOREDwithout making a plugin call. Calls across groups run in parallel. Authorization is unchanged — the existingDESCRIBEACL on the GROUP resource covers the topology description.DescribedGroup.ErrorCodeis never modified by topology-related outcomes; theTopologyDescriptionStatusfield carries the reason whenTopologyDescriptionis null (NOT_REQUESTED,NOT_STORED, orERROR; the last is also logged at WARN). With no plugin configured the broker returnsNOT_STORED. AgetTopologycall returningnull(plugin-side data loss) surfaces asNOT_STOREDand is logged at WARN; subsequent describes keep returningNOT_STOREDuntil the topology epoch advances or an operator clears plugin state. - When a plugin is configured, the broker runs a periodic topology-description cleanup every
offsets.retention.check.interval.ms. Each fire fans out a read-only query across the broker's hosted__consumer_offsetspartitions to identify streams groups eligible for cleanup:isEmpty && allOffsetsExpired && StoredTopologyEpoch != -1. This is the same eligibility predicate the shard's offset-expiration sweep uses to delete consumer/share groups, with the additionalStoredTopologyEpoch != -1filter. For each eligible group, the broker callsplugin.deleteTopology(groupId)and, on success, writes a metadata record settingStoredTopologyEpoch = -1. Plugin failures leave the field set; the same group is retried on the next cycle. OnceStoredTopologyEpoch = -1, the shard's offset-expiration sweep tombstones the (now flag-cleared) group on a subsequent cycle. When no plugin is configured, the periodic cleanup does not run and the shard's offset-expiration sweep expires streams groups normally, ignoringStoredTopologyEpoch; operators that disable a previously-configured plugin are responsible for cleaning up plugin-side state out-of-band.
...
The following broker-side metrics are added under the existing group-coordinator-metrics group (JMX type kafka.server:type=group-coordinator-metrics). Each sensor produces both a rate and a cumulative count.
| MBean | Type | Description |
|---|---|---|
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-set-success-{rate,count} | Meter | Successful plugin.setTopology calls. |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-set-error-{rate,count} | Meter | Failed plugin.setTopology calls. An error increments this sensor regardless of whether it was StreamsTopologyDescriptionTooLargeException, InvalidRequestException, or any other exception. |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-delete-success-{rate,count} | Meter | Successful plugin.deleteTopology calls (covers both the explicit DeleteGroups and periodic-cleanup paths). |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-delete-error-{rate,count} | Meter | Failed plugin.deleteTopology calls. |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-get-success-{rate,count} | Meter | Successful plugin.getTopology calls. |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-get-error-{rate,count} | Meter | Failed plugin.getTopology calls. |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-cleanup-cycle-{rate,count} | Meter | Periodic topology-description cleanup cycles that actually ran. |
kafka.server:type=group-coordinator-metrics,name=streams-group-topology-description-cleanup-eligible-{rate,count} | Meter | Streams group IDs identified as eligible for topology-description cleanup, summed across partitions. |
No client-side metrics are introduced.
...
Test Plan
Integration Tests
Broker
...
StreamsGroupTopologyDescriptionUpdateRequestTest(new) — push happy path; permanent and transient plugin failures; heartbeat-flag gating; member/group/MemberIdfencing; explicitDeleteGroupsplugin-success path (tombstone written) and plugin-failure path (STREAMS_TOPOLOGY_DESCRIPTION_DELETE_FAILEDreturned, group not tombstoned, retry converges after plugin recovery).StreamsGroupTopologyDescriptionUpdateNoPluginRequestTest(new) — broker without plugin: pushes rejected, flag never set, describe returnsNOT_STORED.AuthorizerIntegrationTest—READACL on the GROUP resource for the new RPC.
Streams client
...
TopologyDescriptionPluginIntegrationTest(new) — end-to-end push against an in-memory plugin; describe status mapping;topology.description.push.enabled=falseopt-out.
CLI
...
TopologyDescriptionFormatterTest(new) — eachTopologyDescriptionStatus, the pretty-print format, and the exit-code mapping.StreamsGroupCommandTest— the--topologysub-action against a populated group.
...
