Skip to content

Commit ac1a2a0

Browse files
committed
Add group coordinator lookup
We need a way to send a request to the group coordinator. I spent a day and a half trying to implement a `_send_request_to_group_coordinator()` that included: 1. caching the value of the group coordinator so that it wouldn't have to be repeatedly looked up on every call. This is particularly important because the `list_consumer_groups()`, `list_consumer_group_offsets()`, and `describe_consumer_groups()` will frequently be used by monitoring scripts. I know across the production clusters that I support, using a cached value will save ~1M calls per day. 2. clean and consistent error handling. This is difficult because the responses are inconsistent about error codes. Some have a top-level error code, some bury it within the description of the actual item. 3. Avoiding tight coupling between this method and the request/response classes... the custom parsing logic for errors etc, given that it's non-standard, should live in the callers, not here. So finally I gave up and just went with this simpler solution and made it so the callers can optionally bypass this if they somehow already know the group coordinator.
1 parent d67157c commit ac1a2a0

File tree

1 file changed

+40
-0
lines changed

1 file changed

+40
-0
lines changed

kafka/admin/kafka.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import logging
55
import socket
66
from kafka.client_async import KafkaClient, selectors
7+
import kafka.errors as Errors
78
from kafka.errors import (
89
IncompatibleBrokerVersion, KafkaConfigurationError, KafkaConnectionError,
910
NodeNotReadyError, NotControllerError)
1011
from kafka.metrics import MetricConfig, Metrics
1112
from kafka.protocol.admin import (
1213
CreateTopicsRequest, DeleteTopicsRequest, DescribeConfigsRequest, AlterConfigsRequest, CreatePartitionsRequest,
1314
ListGroupsRequest, DescribeGroupsRequest)
15+
from kafka.protocol.commit import GroupCoordinatorRequest
1416
from kafka.protocol.metadata import MetadataRequest
1517
from kafka.version import __version__
1618

@@ -243,6 +245,44 @@ def _refresh_controller_id(self):
243245
"The controller appears to be running Kafka {}. KafkaAdmin requires brokers >= 0.10.0.0."
244246
.format(version))
245247

248+
def _find_group_coordinator_id(self, group_id):
249+
"""Find the broker node_id of the coordinator of the given group.
250+
251+
Sends a FindCoordinatorRequest message to the cluster. Will block until
252+
the FindCoordinatorResponse is received. Any errors are immediately
253+
raised.
254+
255+
:param group_id: The consumer group ID. This is typically the group
256+
name as a string.
257+
:return: The node_id of the broker that is the coordinator.
258+
"""
259+
# Note: Java may change how this is implemented in KAFKA-6791.
260+
#
261+
# TODO add support for dynamically picking version of
262+
# GroupCoordinatorRequest which was renamed to FindCoordinatorRequest.
263+
# When I experimented with this, GroupCoordinatorResponse_v1 didn't
264+
# match GroupCoordinatorResponse_v0 and I couldn't figure out why.
265+
gc_request = GroupCoordinatorRequest[0](group_id)
266+
gc_response = self._send_request_to_node(self._client.least_loaded_node(), gc_request)
267+
# use the extra error checking in add_group_coordinator() rather than
268+
# immediately returning the group coordinator.
269+
success = self._client.cluster.add_group_coordinator(group_id, gc_response)
270+
if not success:
271+
error_type = Errors.for_code(gc_response.error_code)
272+
assert error_type is not Errors.NoError
273+
# Note: When error_type.retriable, Java will retry... see
274+
# KafkaAdminClient's handleFindCoordinatorError method
275+
raise error_type(
276+
"Could not identify group coordinator for group_id '{}' from response '{}'."
277+
.format(group_id, gc_response))
278+
group_coordinator = self._client.cluster.coordinator_for_group(group_id)
279+
# will be None if the coordinator was never populated, which should never happen here
280+
assert group_coordinator is not None
281+
# will be -1 if add_group_coordinator() failed... but by this point the
282+
# error should have been raised.
283+
assert group_coordinator != -1
284+
return group_coordinator
285+
246286
def _send_request_to_node(self, node, request):
247287
"""Send a kafka protocol message to a specific broker. Will block until the message result is received.
248288

0 commit comments

Comments
 (0)