|
19 | 19 | from kafka.metrics import MetricConfig, Metrics
|
20 | 20 | from kafka.protocol.admin import (
|
21 | 21 | CreateTopicsRequest, DeleteTopicsRequest, DescribeConfigsRequest, AlterConfigsRequest, CreatePartitionsRequest,
|
22 |
| - ListGroupsRequest, DescribeGroupsRequest, DescribeAclsRequest, CreateAclsRequest, DeleteAclsRequest) |
| 22 | + ListGroupsRequest, DescribeGroupsRequest, DescribeAclsRequest, CreateAclsRequest, DeleteAclsRequest, |
| 23 | + DeleteGroupsRequest |
| 24 | +) |
23 | 25 | from kafka.protocol.commit import GroupCoordinatorRequest, OffsetFetchRequest
|
24 | 26 | from kafka.protocol.metadata import MetadataRequest
|
25 | 27 | from kafka.protocol.types import Array
|
@@ -337,12 +339,34 @@ def _find_coordinator_id(self, group_id):
|
337 | 339 | name as a string.
|
338 | 340 | :return: The node_id of the broker that is the coordinator.
|
339 | 341 | """
|
340 |
| - # Note: Java may change how this is implemented in KAFKA-6791. |
341 | 342 | future = self._find_coordinator_id_send_request(group_id)
|
342 | 343 | self._wait_for_futures([future])
|
343 | 344 | response = future.value
|
344 | 345 | return self._find_coordinator_id_process_response(response)
|
345 | 346 |
|
| 347 | + def _find_many_coordinator_ids(self, group_ids): |
| 348 | + """Find the broker node_id of the coordinator for each of the given groups. |
| 349 | +
|
| 350 | + Sends a FindCoordinatorRequest message to the cluster for each group_id. |
| 351 | + Will block until the FindCoordinatorResponse is received for all groups. |
| 352 | + Any errors are immediately raised. |
| 353 | +
|
| 354 | + :param group_ids: A list of consumer group IDs. This is typically the group |
| 355 | + name as a string. |
| 356 | + :return: A list of tuples (group_id, node_id) where node_id is the id |
| 357 | + of the broker that is the coordinator for the corresponding group. |
| 358 | + """ |
| 359 | + futures = { |
| 360 | + group_id: self._find_coordinator_id_send_request(group_id) |
| 361 | + for group_id in group_ids |
| 362 | + } |
| 363 | + self._wait_for_futures(list(futures.values())) |
| 364 | + groups_coordinators = [ |
| 365 | + (group_id, self._find_coordinator_id_process_response(f.value)) |
| 366 | + for group_id, f in futures.items() |
| 367 | + ] |
| 368 | + return groups_coordinators |
| 369 | + |
346 | 370 | def _send_request_to_node(self, node_id, request):
|
347 | 371 | """Send a Kafka protocol message to a specific broker.
|
348 | 372 |
|
@@ -1261,8 +1285,69 @@ def list_consumer_group_offsets(self, group_id, group_coordinator_id=None,
|
1261 | 1285 | response = future.value
|
1262 | 1286 | return self._list_consumer_group_offsets_process_response(response)
|
1263 | 1287 |
|
1264 |
| - # delete groups protocol not yet implemented |
1265 |
| - # Note: send the request to the group's coordinator. |
| 1288 | + def delete_consumer_groups(self, group_ids, group_coordinator_id=None): |
| 1289 | + """Delete Consumer Group Offsets for given consumer groups. |
| 1290 | +
|
| 1291 | + Note: |
| 1292 | + This does not verify that the group ids actually exist and |
| 1293 | + group_coordinator_id is the correct coordinator for all these groups. |
| 1294 | +
|
| 1295 | + The result needs checking for potential errors. |
| 1296 | +
|
| 1297 | + :param group_ids: The consumer group ids of the groups which are to be deleted. |
| 1298 | + :param group_coordinator_id: The node_id of the broker which is the coordinator for |
| 1299 | + all the groups. Use only if all groups are coordinated by the same broker. |
| 1300 | + If set to None, will query the cluster to find the coordinator for every single group. |
| 1301 | + Explicitly specifying this can be useful to prevent |
| 1302 | + that extra network round trips if you already know the group |
| 1303 | + coordinator. Default: None. |
| 1304 | + :return: A list of tuples (group_id, KafkaError) |
| 1305 | + """ |
| 1306 | + if group_coordinator_id is not None: |
| 1307 | + futures = [self._delete_consumer_groups_send_request(group_ids, group_coordinator_id)] |
| 1308 | + else: |
| 1309 | + groups_coordinators = defaultdict(list) |
| 1310 | + for group_id, group_coordinator_id in self._find_many_coordinator_ids(group_ids): |
| 1311 | + groups_coordinators[group_coordinator_id].append(group_id) |
| 1312 | + futures = [ |
| 1313 | + self._delete_consumer_groups_send_request(group_ids, group_coordinator_id) |
| 1314 | + for group_coordinator_id, group_ids in groups_coordinators.items() |
| 1315 | + ] |
| 1316 | + |
| 1317 | + self._wait_for_futures(futures) |
| 1318 | + |
| 1319 | + results = [] |
| 1320 | + for f in futures: |
| 1321 | + results.extend(self._convert_delete_groups_response(f.value)) |
| 1322 | + return results |
| 1323 | + |
| 1324 | + def _convert_delete_groups_response(self, response): |
| 1325 | + if response.API_VERSION <= 1: |
| 1326 | + results = [] |
| 1327 | + for group_id, error_code in response.results: |
| 1328 | + results.append((group_id, Errors.for_code(error_code))) |
| 1329 | + return results |
| 1330 | + else: |
| 1331 | + raise NotImplementedError( |
| 1332 | + "Support for DeleteGroupsResponse_v{} has not yet been added to KafkaAdminClient." |
| 1333 | + .format(response.API_VERSION)) |
| 1334 | + |
| 1335 | + def _delete_consumer_groups_send_request(self, group_ids, group_coordinator_id): |
| 1336 | + """Send a DeleteGroups request to a broker. |
| 1337 | +
|
| 1338 | + :param group_ids: The consumer group ids of the groups which are to be deleted. |
| 1339 | + :param group_coordinator_id: The node_id of the broker which is the coordinator for |
| 1340 | + all the groups. |
| 1341 | + :return: A message future |
| 1342 | + """ |
| 1343 | + version = self._matching_api_version(DeleteGroupsRequest) |
| 1344 | + if version <= 1: |
| 1345 | + request = DeleteGroupsRequest[version](group_ids) |
| 1346 | + else: |
| 1347 | + raise NotImplementedError( |
| 1348 | + "Support for DeleteGroupsRequest_v{} has not yet been added to KafkaAdminClient." |
| 1349 | + .format(version)) |
| 1350 | + return self._send_request_to_node(group_coordinator_id, request) |
1266 | 1351 |
|
1267 | 1352 | def _wait_for_futures(self, futures):
|
1268 | 1353 | while not all(future.succeeded() for future in futures):
|
|
0 commit comments