Skip to content

Commit 50c9a3f

Browse files
authored
Add out_of_sync_broker_id tag to partition metrics (DataDog#23428)
* Add out_of_sync_broker_id tag to Kafka partition metrics When `enable_cluster_monitoring` is true, emit an `out_of_sync_broker_id:<id>` tag on the kafka.partition.* metrics for each replica that is assigned to the partition but not present in the ISR. Fully-replicated partitions are unchanged (no new tags emitted), preserving existing dashboards and alerts. This lets operators query kafka.partition.under_replicated (or the other partition metrics) grouped by out_of_sync_broker_id to attribute under- replicated partitions to specific broker IDs. * Update style and test coverage to address feedback * Assert out_of_sync_broker_id tag on all shared partition gauges
1 parent fd79c30 commit 50c9a3f

3 files changed

Lines changed: 63 additions & 1 deletion

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add an `out_of_sync_broker_id` tag to the `kafka.partition.*` metrics (when `enable_cluster_monitoring` is true) identifying each assigned replica that is not in the partition's ISR. Use it to attribute under-replicated partitions to specific broker IDs.

kafka_consumer/datadog_checks/kafka_consumer/cluster_metadata.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,12 +571,17 @@ def _collect_topic_metadata(self, metadata, highwater_offsets):
571571
for replica in replicas:
572572
partition_broker_tags.append(f'replica_broker_id:{replica}')
573573

574+
isr_set = set(isrs)
575+
out_of_sync_broker_ids = [broker_id for broker_id in replicas if broker_id not in isr_set]
576+
for broker_id in out_of_sync_broker_ids:
577+
partition_broker_tags.append(f'out_of_sync_broker_id:{broker_id}')
578+
574579
self.check.gauge('partition.replicas', len(replicas), tags=partition_broker_tags)
575580
self.check.gauge('partition.isr', len(isrs), tags=partition_broker_tags)
576581

577582
self.check.gauge('partition.size', partition_size, tags=partition_broker_tags)
578583

579-
is_under_replicated = len(isrs) < len(replicas)
584+
is_under_replicated = bool(out_of_sync_broker_ids)
580585
self.check.gauge(
581586
'partition.under_replicated',
582587
1 if is_under_replicated else 0,

kafka_consumer/tests/test_cluster_metadata.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,3 +1351,59 @@ def test_schema_registry_url_encodes_subject_names(check):
13511351
collector.http.get.assert_called_with(
13521352
'http://localhost:8081/subjects/google%2Fprotobuf%2Ftimestamp.proto/versions/latest'
13531353
)
1354+
1355+
1356+
@pytest.mark.parametrize(
1357+
"replicas, isrs, expected_oos, expected_under",
1358+
[
1359+
pytest.param([1, 2], [1, 2], [], 0, id="fully_in_sync"),
1360+
pytest.param([1, 2], [1], [2], 1, id="single_oos"),
1361+
pytest.param([1, 2, 3], [1], [2, 3], 1, id="multiple_oos"),
1362+
pytest.param([1, 2], [], [1, 2], 1, id="empty_isr"),
1363+
pytest.param([1], [1], [], 0, id="single_replica"),
1364+
],
1365+
)
1366+
def test_partition_out_of_sync_broker_id_tag(
1367+
check, dd_run_check, aggregator, replicas, isrs, expected_oos, expected_under
1368+
):
1369+
"""Under-replicated partitions expose an ``out_of_sync_broker_id`` tag per replica missing from the ISR."""
1370+
instance = {
1371+
'kafka_connect_str': 'localhost:9092',
1372+
'enable_cluster_monitoring': True,
1373+
'tags': ['test_tag:test_value'],
1374+
}
1375+
1376+
kafka_consumer_check = check(instance)
1377+
mock_kafka_client = seed_mock_kafka_client()
1378+
1379+
topic_metadata = mock_kafka_client.kafka_client.list_topics.return_value.topics['test-topic']
1380+
topic_metadata.partitions[0].replicas = replicas
1381+
topic_metadata.partitions[0].isrs = isrs
1382+
1383+
kafka_consumer_check.client = mock_kafka_client
1384+
kafka_consumer_check.metadata_collector.client = mock_kafka_client
1385+
mock_schema_registry_methods(kafka_consumer_check.metadata_collector)
1386+
1387+
kafka_consumer_check.read_persistent_cache = mock.Mock(return_value=None)
1388+
kafka_consumer_check.write_persistent_cache = mock.Mock()
1389+
kafka_consumer_check.event_platform_event = mock.Mock()
1390+
1391+
dd_run_check(kafka_consumer_check)
1392+
1393+
expected_tags = [
1394+
'test_tag:test_value',
1395+
'kafka_cluster_id:test-cluster-id',
1396+
'topic:test-topic',
1397+
'partition:0',
1398+
'leader_broker_id:1',
1399+
*(f'replica_broker_id:{r}' for r in replicas),
1400+
*(f'out_of_sync_broker_id:{b}' for b in expected_oos),
1401+
]
1402+
aggregator.assert_metric('kafka.partition.under_replicated', value=expected_under, tags=expected_tags)
1403+
for metric in (
1404+
'kafka.partition.replicas',
1405+
'kafka.partition.isr',
1406+
'kafka.partition.size',
1407+
'kafka.partition.offline',
1408+
):
1409+
aggregator.assert_metric(metric, tags=expected_tags)

0 commit comments

Comments
 (0)