-
Notifications
You must be signed in to change notification settings - Fork 14.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KAFKA-12469: Deprecated and corrected topic metrics for consumer (KIP-1109) #18232
Changes from all commits
edc209e
b42ffdf
5332c5c
8cea00e
05f30bc
3fcb1f5
ae9fa4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,10 +24,12 @@ | |
import org.apache.kafka.common.metrics.stats.WindowedCount; | ||
|
||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import static org.apache.kafka.common.utils.Utils.mkEntry; | ||
import static org.apache.kafka.common.utils.Utils.mkMap; | ||
|
||
/** | ||
* The {@link FetchMetricsManager} class provides wrapper methods to record lag, lead, latency, and fetch metrics. | ||
* It keeps an internal ID of the assigned set of partitions which is updated to ensure the set of metrics it | ||
|
@@ -101,32 +103,38 @@ void recordRecordsFetched(int records) { | |
|
||
void recordBytesFetched(String topic, int bytes) { | ||
String name = topicBytesFetchedMetricName(topic); | ||
Sensor bytesFetched = new SensorBuilder(metrics, name, () -> topicTags(topic)) | ||
.withAvg(metricsRegistry.topicFetchSizeAvg) | ||
.withMax(metricsRegistry.topicFetchSizeMax) | ||
.withMeter(metricsRegistry.topicBytesConsumedRate, metricsRegistry.topicBytesConsumedTotal) | ||
.build(); | ||
maybeRecordDeprecatedBytesFetched(name, topic, bytes); | ||
|
||
Sensor bytesFetched = new SensorBuilder(metrics, name, () -> Map.of("topic", topic)) | ||
.withAvg(metricsRegistry.topicFetchSizeAvg) | ||
.withMax(metricsRegistry.topicFetchSizeMax) | ||
.withMeter(metricsRegistry.topicBytesConsumedRate, metricsRegistry.topicBytesConsumedTotal) | ||
.build(); | ||
bytesFetched.record(bytes); | ||
} | ||
|
||
void recordRecordsFetched(String topic, int records) { | ||
String name = topicRecordsFetchedMetricName(topic); | ||
Sensor recordsFetched = new SensorBuilder(metrics, name, () -> topicTags(topic)) | ||
.withAvg(metricsRegistry.topicRecordsPerRequestAvg) | ||
.withMeter(metricsRegistry.topicRecordsConsumedRate, metricsRegistry.topicRecordsConsumedTotal) | ||
.build(); | ||
maybeRecordDeprecatedRecordsFetched(name, topic, records); | ||
|
||
Sensor recordsFetched = new SensorBuilder(metrics, name, () -> Map.of("topic", topic)) | ||
.withAvg(metricsRegistry.topicRecordsPerRequestAvg) | ||
.withMeter(metricsRegistry.topicRecordsConsumedRate, metricsRegistry.topicRecordsConsumedTotal) | ||
.build(); | ||
recordsFetched.record(records); | ||
} | ||
|
||
void recordPartitionLag(TopicPartition tp, long lag) { | ||
this.recordsLag.record(lag); | ||
|
||
String name = partitionRecordsLagMetricName(tp); | ||
Sensor recordsLag = new SensorBuilder(metrics, name, () -> topicPartitionTags(tp)) | ||
.withValue(metricsRegistry.partitionRecordsLag) | ||
.withMax(metricsRegistry.partitionRecordsLagMax) | ||
.withAvg(metricsRegistry.partitionRecordsLagAvg) | ||
.build(); | ||
maybeRecordDeprecatedPartitionLag(name, tp, lag); | ||
|
||
Sensor recordsLag = new SensorBuilder(metrics, name, () -> mkMap(mkEntry("topic", tp.topic()), mkEntry("partition", String.valueOf(tp.partition())))) | ||
.withValue(metricsRegistry.partitionRecordsLag) | ||
.withMax(metricsRegistry.partitionRecordsLagMax) | ||
.withAvg(metricsRegistry.partitionRecordsLagAvg) | ||
.build(); | ||
|
||
recordsLag.record(lag); | ||
} | ||
|
@@ -135,11 +143,13 @@ void recordPartitionLead(TopicPartition tp, long lead) { | |
this.recordsLead.record(lead); | ||
|
||
String name = partitionRecordsLeadMetricName(tp); | ||
Sensor recordsLead = new SensorBuilder(metrics, name, () -> topicPartitionTags(tp)) | ||
.withValue(metricsRegistry.partitionRecordsLead) | ||
.withMin(metricsRegistry.partitionRecordsLeadMin) | ||
.withAvg(metricsRegistry.partitionRecordsLeadAvg) | ||
.build(); | ||
maybeRecordDeprecatedPartitionLead(name, tp, lead); | ||
|
||
Sensor recordsLead = new SensorBuilder(metrics, name, () -> mkMap(mkEntry("topic", tp.topic()), mkEntry("partition", String.valueOf(tp.partition())))) | ||
.withValue(metricsRegistry.partitionRecordsLead) | ||
.withMin(metricsRegistry.partitionRecordsLeadMin) | ||
.withAvg(metricsRegistry.partitionRecordsLeadAvg) | ||
.build(); | ||
|
||
recordsLead.record(lead); | ||
} | ||
|
@@ -162,16 +172,22 @@ void maybeUpdateAssignment(SubscriptionState subscription) { | |
metrics.removeSensor(partitionRecordsLagMetricName(tp)); | ||
metrics.removeSensor(partitionRecordsLeadMetricName(tp)); | ||
metrics.removeMetric(partitionPreferredReadReplicaMetricName(tp)); | ||
// Remove deprecated metrics. | ||
metrics.removeSensor(deprecatedMetricName(partitionRecordsLagMetricName(tp))); | ||
metrics.removeSensor(deprecatedMetricName(partitionRecordsLeadMetricName(tp))); | ||
metrics.removeMetric(deprecatedPartitionPreferredReadReplicaMetricName(tp)); | ||
} | ||
} | ||
|
||
for (TopicPartition tp : newAssignedPartitions) { | ||
if (!this.assignedPartitions.contains(tp)) { | ||
maybeRecordDeprecatedPreferredReadReplica(tp, subscription); | ||
|
||
MetricName metricName = partitionPreferredReadReplicaMetricName(tp); | ||
metrics.addMetricIfAbsent( | ||
metricName, | ||
null, | ||
(Gauge<Integer>) (config, now) -> subscription.preferredReadReplica(tp, 0L).orElse(-1) | ||
metricName, | ||
null, | ||
(Gauge<Integer>) (config, now) -> subscription.preferredReadReplica(tp, 0L).orElse(-1) | ||
); | ||
} | ||
} | ||
|
@@ -181,6 +197,67 @@ void maybeUpdateAssignment(SubscriptionState subscription) { | |
} | ||
} | ||
|
||
@Deprecated // To be removed in Kafka 5.0 release. | ||
private void maybeRecordDeprecatedBytesFetched(String name, String topic, int bytes) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be useful to follow the approach used in https://github.com/apache/kafka/pull/11302/files to add "deprecated" in the description of the metric name and update the ops doc about the deprecation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the metrics with a specfic tag has been deprectaed hence it's hard to specify same metric as deprecated. Hence I have added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally speaking, we should always include a note explaining the deprecation. This should include the first version it was deprecated, when it's expected to be removed and what should be used instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, so currently below Current:
Example: Should we change it to:
wdyt? |
||
if (shouldReportDeprecatedMetric(topic)) { | ||
Sensor deprecatedBytesFetched = new SensorBuilder(metrics, deprecatedMetricName(name), () -> topicTags(topic)) | ||
.withAvg(metricsRegistry.topicFetchSizeAvg) | ||
.withMax(metricsRegistry.topicFetchSizeMax) | ||
.withMeter(metricsRegistry.topicBytesConsumedRate, metricsRegistry.topicBytesConsumedTotal) | ||
.build(); | ||
deprecatedBytesFetched.record(bytes); | ||
} | ||
} | ||
|
||
@Deprecated // To be removed in Kafka 5.0 release. | ||
private void maybeRecordDeprecatedRecordsFetched(String name, String topic, int records) { | ||
if (shouldReportDeprecatedMetric(topic)) { | ||
Sensor deprecatedRecordsFetched = new SensorBuilder(metrics, deprecatedMetricName(name), () -> topicTags(topic)) | ||
.withAvg(metricsRegistry.topicRecordsPerRequestAvg) | ||
.withMeter(metricsRegistry.topicRecordsConsumedRate, metricsRegistry.topicRecordsConsumedTotal) | ||
.build(); | ||
deprecatedRecordsFetched.record(records); | ||
} | ||
} | ||
|
||
@Deprecated // To be removed in Kafka 5.0 release. | ||
private void maybeRecordDeprecatedPartitionLag(String name, TopicPartition tp, long lag) { | ||
if (shouldReportDeprecatedMetric(tp.topic())) { | ||
Sensor deprecatedRecordsLag = new SensorBuilder(metrics, deprecatedMetricName(name), () -> topicPartitionTags(tp)) | ||
.withValue(metricsRegistry.partitionRecordsLag) | ||
.withMax(metricsRegistry.partitionRecordsLagMax) | ||
.withAvg(metricsRegistry.partitionRecordsLagAvg) | ||
.build(); | ||
|
||
deprecatedRecordsLag.record(lag); | ||
} | ||
} | ||
|
||
@Deprecated // To be removed in Kafka 5.0 release. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please open the jira to ensure we will remove them from 5.0 :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
private void maybeRecordDeprecatedPartitionLead(String name, TopicPartition tp, double lead) { | ||
if (shouldReportDeprecatedMetric(tp.topic())) { | ||
Sensor deprecatedRecordsLead = new SensorBuilder(metrics, deprecatedMetricName(name), () -> topicPartitionTags(tp)) | ||
.withValue(metricsRegistry.partitionRecordsLead) | ||
.withMin(metricsRegistry.partitionRecordsLeadMin) | ||
.withAvg(metricsRegistry.partitionRecordsLeadAvg) | ||
.build(); | ||
|
||
deprecatedRecordsLead.record(lead); | ||
} | ||
} | ||
|
||
@Deprecated // To be removed in Kafka 5.0 release. | ||
private void maybeRecordDeprecatedPreferredReadReplica(TopicPartition tp, SubscriptionState subscription) { | ||
if (shouldReportDeprecatedMetric(tp.topic())) { | ||
MetricName metricName = deprecatedPartitionPreferredReadReplicaMetricName(tp); | ||
metrics.addMetricIfAbsent( | ||
metricName, | ||
null, | ||
(Gauge<Integer>) (config, now) -> subscription.preferredReadReplica(tp, 0L).orElse(-1) | ||
); | ||
} | ||
} | ||
|
||
private static String topicBytesFetchedMetricName(String topic) { | ||
return "topic." + topic + ".bytes-fetched"; | ||
} | ||
|
@@ -197,22 +274,34 @@ private static String partitionRecordsLagMetricName(TopicPartition tp) { | |
return tp + ".records-lag"; | ||
} | ||
|
||
private static String deprecatedMetricName(String name) { | ||
return name + ".deprecated"; | ||
} | ||
|
||
private static boolean shouldReportDeprecatedMetric(String topic) { | ||
return topic.contains("."); | ||
} | ||
|
||
private MetricName partitionPreferredReadReplicaMetricName(TopicPartition tp) { | ||
Map<String, String> metricTags = mkMap(mkEntry("topic", tp.topic()), mkEntry("partition", String.valueOf(tp.partition()))); | ||
return this.metrics.metricInstance(metricsRegistry.partitionPreferredReadReplica, metricTags); | ||
} | ||
|
||
@Deprecated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should never deprecate something without including a @deprecated javadoc with the details I outlined in a separate comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, I have added details here #18232 (comment), please let me know your thoughts. |
||
private MetricName deprecatedPartitionPreferredReadReplicaMetricName(TopicPartition tp) { | ||
Map<String, String> metricTags = topicPartitionTags(tp); | ||
return this.metrics.metricInstance(metricsRegistry.partitionPreferredReadReplica, metricTags); | ||
} | ||
|
||
@Deprecated | ||
static Map<String, String> topicTags(String topic) { | ||
Map<String, String> metricTags = new HashMap<>(1); | ||
metricTags.put("topic", topic.replace('.', '_')); | ||
return metricTags; | ||
return Map.of("topic", topic.replace('.', '_')); | ||
} | ||
|
||
@Deprecated | ||
static Map<String, String> topicPartitionTags(TopicPartition tp) { | ||
Map<String, String> metricTags = new HashMap<>(2); | ||
metricTags.put("topic", tp.topic().replace('.', '_')); | ||
metricTags.put("partition", String.valueOf(tp.partition())); | ||
return metricTags; | ||
return mkMap(mkEntry("topic", tp.topic().replace('.', '_')), | ||
mkEntry("partition", String.valueOf(tp.partition()))); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we do the same for the deprecated metrics too for better consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason why you're still using the
Utils.mkMap
methods instead ofMap.of
from Java 9+?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ijuma please see my previous comment #18232 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, if we want insertion ordering to be preserved, we should change
SensorBuilder
to use aLinkedHashMap
(SequencedMap
would be better, but that requires Java 21). Having this implicit requirement and hoping people get it right is very error prone.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah agree a explicit requirement to send an ordered map would be better in builder itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have opened https://issues.apache.org/jira/browse/KAFKA-18390 to address @ijuma's comment. The fix is targeted for version 4.1.0, as it may introduce significant changes to the codebase.