Skip to content

Commit fcaa09c

Browse files
authored
[da-vinci][server] Fix OTel duplicate attribute errors in KafkaConsumerServiceStats by adding region and pool type dimensions (linkedin#2695)
- Added two new OTel dimensions to all 12 KafkaConsumerServiceOtelMetricEntity metrics: VENICE_REGION_NAME and VENICE_CONSUMER_POOL_TYPE. This differentiates each instance's OTel attributes, matching Tehuti's per-region per-pool-type differentiation. - Replaced the kafkaClusterAlias constructor parameter (a pre-concatenated regionAlias + poolType.getStatSuffix() string) with separate pubsubRegionAlias throughout the constructor chain. -ConsumerPoolType implements VeniceDimensionInterface - Added setRegionName() to OpenTelemetryMetricsSetup.Builder as a first-class builder method - Added null validation to addCustomDimension(VeniceDimensionInterface) for consistency — throws IllegalArgumentException instead of implicit NPE.
1 parent 44476a0 commit fcaa09c

19 files changed

Lines changed: 346 additions & 119 deletions

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/AggKafkaConsumerService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,11 @@ public synchronized AbstractKafkaConsumerService createKafkaConsumerService(fina
383383
return alreadyCreatedConsumerService;
384384
}
385385

386+
// getOrDefault returns the stored value (even if null) when the key IS present.
387+
// kafkaClusterUrlToAliasMap can have null values when a kafka cluster config entry
388+
// has a URL but no "name" field. Fall back to the resolved URL in that case.
389+
String rawAlias = kafkaClusterUrlToAliasMap.getOrDefault(resolvedKafkaUrl, resolvedKafkaUrl);
390+
String regionAlias = (rawAlias == null || rawAlias.isEmpty()) ? resolvedKafkaUrl : rawAlias;
386391
KafkaConsumerServiceDelegator.KafkaConsumerServiceBuilder consumerServiceBuilder =
387392
(poolSize, poolType) -> sharedConsumerAssignmentStrategy.constructor.construct(
388393
poolType,
@@ -392,7 +397,7 @@ public synchronized AbstractKafkaConsumerService createKafkaConsumerService(fina
392397
ingestionThrottler,
393398
kafkaClusterBasedRecordThrottler,
394399
metricsRepository,
395-
kafkaClusterUrlToAliasMap.getOrDefault(resolvedKafkaUrl, resolvedKafkaUrl) + poolType.getStatSuffix(),
400+
regionAlias,
396401
sharedConsumerNonExistingTopicCleanupDelayMS,
397402
staleTopicChecker,
398403
liveConfigBasedKafkaThrottlingEnabled,

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/ConsumerPoolType.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
package com.linkedin.davinci.kafka.consumer;
22

3-
public enum ConsumerPoolType {
3+
import com.linkedin.venice.stats.dimensions.VeniceDimensionInterface;
4+
import com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions;
5+
6+
7+
/**
8+
* Consumer pool type for {@link KafkaConsumerService} instances.
9+
*
10+
* <p>Two string representations exist for each pool type:
11+
* <ul>
12+
* <li>{@link #getStatSuffix()}: Tehuti stat name suffix (e.g., {@code _for_current_aa_wc_leader})</li>
13+
* <li>{@link #getDimensionValue()}: for OTel dimension value (e.g., {@code current_version_aa_wc_leader_pool})</li>
14+
* </ul>
15+
* These differ because Tehuti names are legacy and cannot change.
16+
*/
17+
public enum ConsumerPoolType implements VeniceDimensionInterface {
418
REGULAR_POOL(""), // For other kinds of workload, and this pool type is also being used when using a single consumer
519
// pool.
620

@@ -22,4 +36,9 @@ public enum ConsumerPoolType {
2236
public String getStatSuffix() {
2337
return statSuffix;
2438
}
39+
40+
@Override
41+
public VeniceMetricsDimensions getDimensionName() {
42+
return VeniceMetricsDimensions.VENICE_CONSUMER_POOL_TYPE;
43+
}
2544
}

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/KafkaConsumerService.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ protected KafkaConsumerService(
115115
final IngestionThrottler ingestionThrottler,
116116
final KafkaClusterBasedRecordThrottler kafkaClusterBasedRecordThrottler,
117117
final MetricsRepository metricsRepository,
118-
final String kafkaClusterAlias,
118+
final String pubsubRegionAlias,
119119
final long sharedConsumerNonExistingTopicCleanupDelayMS,
120120
final StaleTopicChecker staleTopicChecker,
121121
final boolean liveConfigBasedKafkaThrottlingEnabled,
@@ -152,7 +152,7 @@ protected KafkaConsumerService(
152152
? statsOverride
153153
: createAggKafkaConsumerServiceStats(
154154
metricsRepository,
155-
kafkaClusterAlias,
155+
pubsubRegionAlias,
156156
this::getMaxElapsedTimeMSSinceLastPollInConsumerPool,
157157
metadataRepository,
158158
isUnregisterMetricForDeletedStoreEnabled,
@@ -437,19 +437,21 @@ public boolean hasAnySubscriptionFor(PubSubTopic versionTopic) {
437437

438438
private AggKafkaConsumerServiceStats createAggKafkaConsumerServiceStats(
439439
MetricsRepository metricsRepository,
440-
String kafkaClusterAlias,
440+
String regionAlias,
441441
LongSupplier getMaxElapsedTimeSinceLastPollInConsumerPool,
442442
ReadOnlyStoreRepository metadataRepository,
443443
boolean isUnregisterMetricForDeletedStoreEnabled,
444444
String veniceClusterName) {
445-
String nameWithKafkaClusterAlias = "kafka_consumer_service_for_" + kafkaClusterAlias;
445+
String tehutiStatsNamePrefix = "kafka_consumer_service_for_" + regionAlias + poolType.getStatSuffix();
446446
return new AggKafkaConsumerServiceStats(
447-
nameWithKafkaClusterAlias,
447+
tehutiStatsNamePrefix,
448448
metricsRepository,
449449
metadataRepository,
450450
getMaxElapsedTimeSinceLastPollInConsumerPool,
451451
isUnregisterMetricForDeletedStoreEnabled,
452-
veniceClusterName);
452+
veniceClusterName,
453+
regionAlias,
454+
poolType);
453455
}
454456

455457
@Override
@@ -565,7 +567,7 @@ KafkaConsumerService construct(
565567
IngestionThrottler ingestionThrottler,
566568
KafkaClusterBasedRecordThrottler kafkaClusterBasedRecordThrottler,
567569
MetricsRepository metricsRepository,
568-
String kafkaClusterAlias,
570+
String pubsubRegionAlias,
569571
long sharedConsumerNonExistingTopicCleanupDelayMS,
570572
StaleTopicChecker staleTopicChecker,
571573
boolean liveConfigBasedKafkaThrottlingEnabled,

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/PartitionWiseKafkaConsumerService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class PartitionWiseKafkaConsumerService extends KafkaConsumerService {
4848
final IngestionThrottler ingestionThrottler,
4949
final KafkaClusterBasedRecordThrottler kafkaClusterBasedRecordThrottler,
5050
final MetricsRepository metricsRepository,
51-
final String kafkaClusterAlias,
51+
final String pubsubRegionAlias,
5252
final long sharedConsumerNonExistingTopicCleanupDelayMS,
5353
final StaleTopicChecker staleTopicChecker,
5454
final boolean liveConfigBasedKafkaThrottlingEnabled,
@@ -68,7 +68,7 @@ public class PartitionWiseKafkaConsumerService extends KafkaConsumerService {
6868
ingestionThrottler,
6969
kafkaClusterBasedRecordThrottler,
7070
metricsRepository,
71-
kafkaClusterAlias,
71+
pubsubRegionAlias,
7272
sharedConsumerNonExistingTopicCleanupDelayMS,
7373
staleTopicChecker,
7474
liveConfigBasedKafkaThrottlingEnabled,
@@ -91,7 +91,7 @@ public class PartitionWiseKafkaConsumerService extends KafkaConsumerService {
9191
final IngestionThrottler ingestionThrottler,
9292
final KafkaClusterBasedRecordThrottler kafkaClusterBasedRecordThrottler,
9393
final MetricsRepository metricsRepository,
94-
final String kafkaClusterAlias,
94+
final String pubsubRegionAlias,
9595
final long sharedConsumerNonExistingTopicCleanupDelayMS,
9696
final StaleTopicChecker staleTopicChecker,
9797
final boolean liveConfigBasedKafkaThrottlingEnabled,
@@ -112,7 +112,7 @@ public class PartitionWiseKafkaConsumerService extends KafkaConsumerService {
112112
ingestionThrottler,
113113
kafkaClusterBasedRecordThrottler,
114114
metricsRepository,
115-
kafkaClusterAlias,
115+
pubsubRegionAlias,
116116
sharedConsumerNonExistingTopicCleanupDelayMS,
117117
staleTopicChecker,
118118
liveConfigBasedKafkaThrottlingEnabled,

clients/da-vinci-client/src/main/java/com/linkedin/davinci/kafka/consumer/StoreAwarePartitionWiseKafkaConsumerService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class StoreAwarePartitionWiseKafkaConsumerService extends PartitionWiseKa
4141
final IngestionThrottler ingestionThrottler,
4242
final KafkaClusterBasedRecordThrottler kafkaClusterBasedRecordThrottler,
4343
final MetricsRepository metricsRepository,
44-
final String kafkaClusterAlias,
44+
final String pubsubRegionAlias,
4545
final long sharedConsumerNonExistingTopicCleanupDelayMS,
4646
final StaleTopicChecker staleTopicChecker,
4747
final boolean liveConfigBasedKafkaThrottlingEnabled,
@@ -61,7 +61,7 @@ public class StoreAwarePartitionWiseKafkaConsumerService extends PartitionWiseKa
6161
ingestionThrottler,
6262
kafkaClusterBasedRecordThrottler,
6363
metricsRepository,
64-
kafkaClusterAlias,
64+
pubsubRegionAlias,
6565
sharedConsumerNonExistingTopicCleanupDelayMS,
6666
staleTopicChecker,
6767
liveConfigBasedKafkaThrottlingEnabled,

clients/da-vinci-client/src/main/java/com/linkedin/davinci/stats/AggKafkaConsumerServiceStats.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.linkedin.davinci.stats;
22

3+
import com.linkedin.davinci.kafka.consumer.ConsumerPoolType;
34
import com.linkedin.venice.exceptions.VeniceException;
45
import com.linkedin.venice.meta.ReadOnlyStoreRepository;
56
import com.linkedin.venice.stats.AbstractVeniceAggStoreStats;
@@ -23,11 +24,17 @@ public AggKafkaConsumerServiceStats(
2324
ReadOnlyStoreRepository metadataRepository,
2425
LongSupplier getMaxElapsedTimeSinceLastPollInConsumerPool,
2526
boolean isUnregisterMetricForDeletedStoreEnabled,
26-
String veniceClusterName) {
27+
String veniceClusterName,
28+
String pubsubRegionAlias,
29+
ConsumerPoolType poolType) {
2730
super(
2831
regionName,
2932
metricsRepository,
30-
new KafkaConsumerServiceStatsSupplier(getMaxElapsedTimeSinceLastPollInConsumerPool, veniceClusterName),
33+
new KafkaConsumerServiceStatsSupplier(
34+
getMaxElapsedTimeSinceLastPollInConsumerPool,
35+
veniceClusterName,
36+
pubsubRegionAlias,
37+
poolType),
3138
metadataRepository,
3239
isUnregisterMetricForDeletedStoreEnabled,
3340
true);
@@ -97,12 +104,18 @@ public void recordTotalPartitionAssignmentForOtel(int partitionCount) {
97104
static class KafkaConsumerServiceStatsSupplier implements StatsSupplier<KafkaConsumerServiceStats> {
98105
private final LongSupplier getMaxElapsedTimeSinceLastPollInConsumerPool;
99106
private final String veniceClusterName;
107+
private final String pubsubRegionAlias;
108+
private final ConsumerPoolType poolType;
100109

101110
KafkaConsumerServiceStatsSupplier(
102111
LongSupplier getMaxElapsedTimeSinceLastPollInConsumerPool,
103-
String veniceClusterName) {
112+
String veniceClusterName,
113+
String pubsubRegionAlias,
114+
ConsumerPoolType poolType) {
104115
this.getMaxElapsedTimeSinceLastPollInConsumerPool = getMaxElapsedTimeSinceLastPollInConsumerPool;
105116
this.veniceClusterName = veniceClusterName;
117+
this.pubsubRegionAlias = pubsubRegionAlias;
118+
this.poolType = poolType;
106119
}
107120

108121
@Override
@@ -122,7 +135,9 @@ public KafkaConsumerServiceStats get(
122135
getMaxElapsedTimeSinceLastPollInConsumerPool,
123136
totalStats,
124137
SystemTime.INSTANCE,
125-
veniceClusterName);
138+
veniceClusterName,
139+
pubsubRegionAlias,
140+
poolType);
126141
}
127142
}
128143
}

clients/da-vinci-client/src/main/java/com/linkedin/davinci/stats/KafkaConsumerServiceOtelMetricEntity.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_CLUSTER_NAME;
44
import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_CONSUMER_POOL_ACTION;
5+
import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_CONSUMER_POOL_TYPE;
6+
import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_REGION_NAME;
57
import static com.linkedin.venice.stats.dimensions.VeniceMetricsDimensions.VENICE_STORE_NAME;
68
import static com.linkedin.venice.utils.Utils.setOf;
79

@@ -21,48 +23,54 @@
2123
public enum KafkaConsumerServiceOtelMetricEntity implements ModuleMetricEntityInterface {
2224
POLL_BYTES(
2325
"ingestion.pubsub.consumer.poll.bytes", MetricType.MIN_MAX_COUNT_SUM_AGGREGATIONS, MetricUnit.BYTES,
24-
"Byte size of polled PubSub messages per poll request", setOf(VENICE_STORE_NAME, VENICE_CLUSTER_NAME)
26+
"Byte size of polled PubSub messages per poll request",
27+
setOf(VENICE_STORE_NAME, VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
2528
),
2629

2730
POLL_RECORD_COUNT(
2831
"ingestion.pubsub.consumer.poll.record_count", MetricType.MIN_MAX_COUNT_SUM_AGGREGATIONS, MetricUnit.NUMBER,
29-
"Number of records returned per poll request", setOf(VENICE_STORE_NAME, VENICE_CLUSTER_NAME)
32+
"Number of records returned per poll request",
33+
setOf(VENICE_STORE_NAME, VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
3034
),
3135

3236
POLL_COUNT(
3337
"ingestion.pubsub.consumer.poll.count", MetricType.ASYNC_COUNTER_FOR_HIGH_PERF_CASES, MetricUnit.NUMBER,
34-
"Total count of poll requests to the PubSub consumer", setOf(VENICE_CLUSTER_NAME)
38+
"Total count of poll requests to the PubSub consumer",
39+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
3540
),
3641

3742
POLL_TIME(
3843
"ingestion.pubsub.consumer.poll.time", MetricType.HISTOGRAM, MetricUnit.MILLISECOND,
39-
"Latency of PubSub consumer poll requests", setOf(VENICE_CLUSTER_NAME)
44+
"Latency of PubSub consumer poll requests",
45+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
4046
),
4147

4248
POLL_NON_EMPTY_COUNT(
4349
"ingestion.pubsub.consumer.poll.non_empty_count", MetricType.ASYNC_COUNTER_FOR_HIGH_PERF_CASES, MetricUnit.NUMBER,
44-
"Count of poll requests that returned at least one record", setOf(VENICE_CLUSTER_NAME)
50+
"Count of poll requests that returned at least one record",
51+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
4552
),
4653

4754
POLL_ERROR_COUNT(
4855
"ingestion.pubsub.consumer.poll.error_count", MetricType.COUNTER, MetricUnit.NUMBER,
49-
"Count of PubSub consumer poll errors", setOf(VENICE_CLUSTER_NAME)
56+
"Count of PubSub consumer poll errors", setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
5057
),
5158

5259
PRODUCE_TO_WRITE_BUFFER_TIME(
5360
"ingestion.pubsub.consumer.produce_to_write_buffer_time", MetricType.HISTOGRAM, MetricUnit.MILLISECOND,
54-
"Latency of producing consumed records to the write buffer", setOf(VENICE_CLUSTER_NAME)
61+
"Latency of producing consumed records to the write buffer",
62+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
5563
),
5664

5765
TOPIC_DETECTED_DELETED_COUNT(
5866
"ingestion.pubsub.consumer.topic.detected_deleted_count", MetricType.COUNTER, MetricUnit.NUMBER,
59-
"Count of topics detected as deleted", setOf(VENICE_CLUSTER_NAME)
67+
"Count of topics detected as deleted", setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
6068
),
6169

6270
ORPHAN_TOPIC_PARTITION_COUNT(
6371
"ingestion.pubsub.consumer.orphan_subscription_count", MetricType.COUNTER, MetricUnit.NUMBER,
6472
"Count of orphan subscriptions, topic partitions assigned to a consumer with no running ingestion task",
65-
setOf(VENICE_CLUSTER_NAME)
73+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
6674
),
6775

6876
/**
@@ -72,7 +80,7 @@ public enum KafkaConsumerServiceOtelMetricEntity implements ModuleMetricEntityIn
7280
POOL_ACTION_TIME(
7381
"ingestion.pubsub.consumer.pool_action.time", MetricType.HISTOGRAM, MetricUnit.MILLISECOND,
7482
"Latency of consumer pool actions (subscribe, update assignment)",
75-
setOf(VENICE_CLUSTER_NAME, VENICE_CONSUMER_POOL_ACTION)
83+
setOf(VENICE_CLUSTER_NAME, VENICE_CONSUMER_POOL_ACTION, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
7684
),
7785

7886
/**
@@ -86,14 +94,14 @@ public enum KafkaConsumerServiceOtelMetricEntity implements ModuleMetricEntityIn
8694
POLL_TIME_SINCE_LAST_SUCCESS(
8795
"ingestion.pubsub.consumer.poll.time_since_last_success", MetricType.MIN_MAX_COUNT_SUM_AGGREGATIONS,
8896
MetricUnit.MILLISECOND, "Max elapsed time since last successful poll across consumers in the pool",
89-
setOf(VENICE_CLUSTER_NAME)
97+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
9098
),
9199

92100
/** Raw per-consumer partition assignment counts. OTel-only (Tehuti uses 4 pre-computed gauges). */
93101
PARTITION_ASSIGNMENT_COUNT(
94102
"ingestion.pubsub.consumer.partition_assignment.count", MetricType.MIN_MAX_COUNT_SUM_AGGREGATIONS,
95103
MetricUnit.NUMBER, "Raw per-consumer partition assignment counts across the consumer pool",
96-
setOf(VENICE_CLUSTER_NAME)
104+
setOf(VENICE_CLUSTER_NAME, VENICE_REGION_NAME, VENICE_CONSUMER_POOL_TYPE)
97105
);
98106

99107
private final MetricEntity metricEntity;

0 commit comments

Comments
 (0)