Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5f24acc
Porting changes from STORM-3693
reiabreu Jan 11, 2025
e596015
Merge branch 'apache:master' into master
reiabreu Jan 18, 2025
9227922
Refactoring changes proposed on STORM-3693 and adding an unit test
reiabreu Jan 18, 2025
f5db47b
Update Executor.java
reiabreu Jan 18, 2025
5ded4cd
Update SpoutExecutor.java
reiabreu Jan 18, 2025
e3f76e6
Update SpoutExecutor.java
reiabreu Jan 18, 2025
677756f
Update SpoutExecutor.java
reiabreu Jan 18, 2025
3659584
Adding missing license
reiabreu Jan 18, 2025
f34416e
Adding missing license
reiabreu Jan 18, 2025
b22de47
Merge branch 'apache:master' into master
reiabreu Feb 12, 2025
3455c2a
Update IWorkerHook.java
reiabreu Feb 12, 2025
1492996
Merge branch 'apache:master' into master
reiabreu Apr 18, 2025
357368c
Adding new MetricSet
reiabreu Apr 20, 2025
7403ceb
Adding unit tests
reiabreu Apr 20, 2025
4a5c383
Adding unit tests
reiabreu Apr 20, 2025
7a4639c
Adding unit tests
reiabreu Apr 22, 2025
1e1edb5
Adding unit tests
reiabreu Apr 22, 2025
83bbf23
Adding unit tests
reiabreu Apr 23, 2025
c07874c
Adding unit tests
reiabreu Apr 23, 2025
e1dcaa1
Adding unit tests
reiabreu Apr 23, 2025
2a8198e
Adding unit tests
reiabreu Apr 23, 2025
4dcd178
Adding unit tests
reiabreu Apr 23, 2025
08fd1a9
Adding unit tests
reiabreu Apr 23, 2025
2e2d6fb
Adding unit tests
reiabreu Apr 23, 2025
ac7768a
Merge branch 'master' of https://github.com/reiabreu/storm
reiabreu Apr 23, 2025
dd80888
Merge branch 'master' into fixing_kafka_topic_metrics
reiabreu Apr 23, 2025
44f1bdc
Update KafkaOffsetPartitionAndTopicMetrics.java
reiabreu Apr 24, 2025
be536d7
Update KafkaOffsetPartitionAndTopicMetricsTest.java
reiabreu Apr 24, 2025
08f0257
Delete external/storm-kafka-client/src/main/java/org/apache/storm/kaf…
reiabreu Apr 24, 2025
ce3acec
Update KafkaOffsetMetricManager.java
reiabreu Apr 24, 2025
781baa1
Adding unit tests
reiabreu Apr 25, 2025
22da2a4
Merge pull request #2 from reiabreu/fixing_kafka_topic_metrics
reiabreu Apr 25, 2025
39b7838
Merge branch 'apache:master' into master
reiabreu Jun 8, 2025
3af90cb
Merge branch 'apache:master' into master
reiabreu Jun 9, 2025
4d1759e
Fixing the way Kafka topic level metrics are computed (#3)
reiabreu Jun 9, 2025
ed81beb
Update KafkaOffsetMetricManager.java
reiabreu Jun 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.apache.commons.lang.Validate;
import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.clients.consumer.Consumer;
Expand Down Expand Up @@ -154,7 +155,7 @@ public void open(Map<String, Object> conf, TopologyContext context, SpoutOutputC

tupleListener.open(conf, context);
this.kafkaOffsetMetricManager
= new KafkaOffsetMetricManager<>(() -> Collections.unmodifiableMap(offsetManagers), () -> admin, context);
= new KafkaOffsetMetricManager<>(() -> Collections.unmodifiableMap(offsetManagers), () -> admin, context);

LOG.info("Kafka Spout opened with the following configuration: {}", kafkaSpoutConfig);
}
Expand Down Expand Up @@ -183,7 +184,7 @@ public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
previousAssignment = partitions;

LOG.info("Partitions revoked. [consumer-group={}, consumer={}, topic-partitions={}]",
kafkaSpoutConfig.getConsumerGroupId(), consumer, partitions);
kafkaSpoutConfig.getConsumerGroupId(), consumer, partitions);

if (isAtLeastOnceProcessing()) {
commitOffsetsForAckedTuples();
Expand All @@ -193,7 +194,7 @@ public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
@Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
LOG.info("Partitions reassignment. [task-ID={}, consumer-group={}, consumer={}, topic-partitions={}]",
context.getThisTaskId(), kafkaSpoutConfig.getConsumerGroupId(), consumer, partitions);
context.getThisTaskId(), kafkaSpoutConfig.getConsumerGroupId(), consumer, partitions);

initialize(partitions);
tupleListener.onPartitionsReassigned(partitions);
Expand Down Expand Up @@ -221,7 +222,7 @@ private void initialize(Collection<TopicPartition> partitions) {
final OffsetAndMetadata committedOffset = consumer.committed(newTp);
final long fetchOffset = doSeek(newTp, committedOffset);
LOG.debug("Set consumer position to [{}] for topic-partition [{}] with [{}] and committed offset [{}]",
fetchOffset, newTp, firstPollOffsetStrategy, committedOffset);
fetchOffset, newTp, firstPollOffsetStrategy, committedOffset);
if (isAtLeastOnceProcessing() && !offsetManagers.containsKey(newTp)) {
offsetManagers.put(newTp, new OffsetManager(newTp, fetchOffset));
}
Expand All @@ -234,13 +235,13 @@ private void initialize(Collection<TopicPartition> partitions) {
*/
private long doSeek(TopicPartition newTp, OffsetAndMetadata committedOffset) {
LOG.trace("Seeking offset for topic-partition [{}] with [{}] and committed offset [{}]",
newTp, firstPollOffsetStrategy, committedOffset);
newTp, firstPollOffsetStrategy, committedOffset);

if (committedOffset != null) {
// offset was previously committed for this consumer group and topic-partition, either by this or another topology.
if (commitMetadataManager.isOffsetCommittedByThisTopology(newTp,
committedOffset,
Collections.unmodifiableMap(offsetManagers))) {
committedOffset,
Collections.unmodifiableMap(offsetManagers))) {
// Another KafkaSpout instance (of this topology) already committed, therefore FirstPollOffsetStrategy does not apply.
consumer.seek(newTp, committedOffset.offset());
} else {
Expand Down Expand Up @@ -281,7 +282,7 @@ public void nextTuple() {
commitOffsetsForAckedTuples();
} else if (kafkaSpoutConfig.getProcessingGuarantee() == ProcessingGuarantee.NO_GUARANTEE) {
Map<TopicPartition, OffsetAndMetadata> offsetsToCommit =
createFetchedOffsetsMetadata(consumer.assignment());
createFetchedOffsetsMetadata(consumer.assignment());
consumer.commitAsync(offsetsToCommit, null);
LOG.debug("Committed offsets {} to Kafka", offsetsToCommit);
}
Expand Down Expand Up @@ -336,7 +337,7 @@ private PollablePartitionsInfo getPollablePartitionsInfo() {
pollablePartitions.add(tp);
} else {
LOG.debug("Not polling on partition [{}]. It has [{}] uncommitted offsets, which exceeds the limit of [{}]. ", tp,
numUncommittedOffsets, maxUncommittedOffsets);
numUncommittedOffsets, maxUncommittedOffsets);
}
}
}
Expand All @@ -345,7 +346,7 @@ private PollablePartitionsInfo getPollablePartitionsInfo() {

private boolean isWaitingToEmit() {
return waitingToEmit.values().stream()
.anyMatch(list -> !list.isEmpty());
.anyMatch(list -> !list.isEmpty());
}

private void setWaitingToEmit(ConsumerRecords<K, V> consumerRecords) {
Expand All @@ -365,11 +366,11 @@ private ConsumerRecords<K, V> pollKafkaBroker(PollablePartitionsInfo pollablePar
ackRetriableOffsetsIfCompactedAway(pollablePartitionsInfo.pollableEarliestRetriableOffsets, consumerRecords);
final int numPolledRecords = consumerRecords.count();
LOG.debug("Polled [{}] records from Kafka",
numPolledRecords);
numPolledRecords);
if (kafkaSpoutConfig.getProcessingGuarantee() == KafkaSpoutConfig.ProcessingGuarantee.AT_MOST_ONCE) {
//Commit polled records immediately to ensure delivery is at-most-once.
Map<TopicPartition, OffsetAndMetadata> offsetsToCommit =
createFetchedOffsetsMetadata(consumer.assignment());
createFetchedOffsetsMetadata(consumer.assignment());
consumer.commitSync(offsetsToCommit);
LOG.debug("Committed offsets {} to Kafka", offsetsToCommit);
}
Expand All @@ -387,7 +388,7 @@ private void doSeekRetriableTopicPartitions(Map<TopicPartition, Long> pollableEa
}

private void ackRetriableOffsetsIfCompactedAway(Map<TopicPartition, Long> earliestRetriableOffsets,
ConsumerRecords<K, V> consumerRecords) {
ConsumerRecords<K, V> consumerRecords) {
for (Entry<TopicPartition, Long> entry : earliestRetriableOffsets.entrySet()) {
TopicPartition tp = entry.getKey();
List<ConsumerRecord<K, V>> records = consumerRecords.records(tp);
Expand Down Expand Up @@ -529,7 +530,7 @@ private void commitOffsetsForAckedTuples() {
* to the committed offset.
*/
LOG.debug("Consumer fell behind committed offset. Catching up. Position was [{}], skipping to [{}]",
position, committedOffset);
position, committedOffset);
consumer.seek(tp, committedOffset);
}
/**
Expand All @@ -539,8 +540,8 @@ private void commitOffsetsForAckedTuples() {
if (waitingToEmitForTp != null) {
//Discard the pending records that are already committed
waitingToEmit.put(tp, waitingToEmitForTp.stream()
.filter(record -> record.offset() >= committedOffset)
.collect(Collectors.toCollection(LinkedList::new)));
.filter(record -> record.offset() >= committedOffset)
.collect(Collectors.toCollection(LinkedList::new)));
}

final OffsetManager offsetManager = offsetManagers.get(tp);
Expand Down Expand Up @@ -573,11 +574,11 @@ public void ack(Object messageId) {

if (!emitted.contains(msgId)) {
LOG.debug("Received ack for message [{}], associated with tuple emitted for a ConsumerRecord that "
+ "came from a topic-partition that this consumer group instance is no longer tracking "
+ "due to rebalance/partition reassignment. No action taken.", msgId);
+ "came from a topic-partition that this consumer group instance is no longer tracking "
+ "due to rebalance/partition reassignment. No action taken.", msgId);
} else {
Validate.isTrue(!retryService.isScheduled(msgId), "The message id " + msgId + " is queued for retry while being acked."
+ " This should never occur barring errors in the RetryService implementation or the spout code.");
+ " This should never occur barring errors in the RetryService implementation or the spout code.");
offsetManagers.get(msgId.getTopicPartition()).addToAckMsgs(msgId);
emitted.remove(msgId);
}
Expand All @@ -595,11 +596,11 @@ public void fail(Object messageId) {
final KafkaSpoutMessageId msgId = (KafkaSpoutMessageId) messageId;
if (!emitted.contains(msgId)) {
LOG.debug("Received fail for tuple this spout is no longer tracking."
+ " Partitions may have been reassigned. Ignoring message [{}]", msgId);
+ " Partitions may have been reassigned. Ignoring message [{}]", msgId);
return;
}
Validate.isTrue(!retryService.isScheduled(msgId), "The message id " + msgId + " is queued for retry while being failed."
+ " This should never occur barring errors in the RetryService implementation or the spout code.");
+ " This should never occur barring errors in the RetryService implementation or the spout code.");

msgId.incrementNumFails();

Expand Down Expand Up @@ -630,7 +631,7 @@ private void refreshAssignment() {
List<TopicPartition> allPartitionsSorted = new ArrayList<>(allPartitions);
Collections.sort(allPartitionsSorted, TopicPartitionComparator.INSTANCE);
Set<TopicPartition> assignedPartitions = kafkaSpoutConfig.getTopicPartitioner()
.getPartitionsForThisTask(allPartitionsSorted, context);
.getPartitionsForThisTask(allPartitionsSorted, context);
boolean partitionChanged = topicAssigner.assignPartitions(consumer, assignedPartitions, rebalanceListener);
if (partitionChanged && canRegisterMetrics()) {
LOG.info("Partitions assignments has changed, updating metrics.");
Expand Down Expand Up @@ -683,9 +684,9 @@ public void declareOutputFields(OutputFieldsDeclarer declarer) {
@Override
public String toString() {
return "KafkaSpout{"
+ "offsetManagers =" + offsetManagers
+ ", emitted=" + emitted
+ "}";
+ "offsetManagers =" + offsetManagers
+ ", emitted=" + emitted
+ "}";
}

@Override
Expand Down Expand Up @@ -718,8 +719,8 @@ private boolean isPrimitiveOrWrapper(Class<?> type) {

private boolean isWrapper(Class<?> type) {
return type == Double.class || type == Float.class || type == Long.class
|| type == Integer.class || type == Short.class || type == Character.class
|| type == Byte.class || type == Boolean.class || type == String.class;
|| type == Integer.class || type == Short.class || type == Character.class
|| type == Byte.class || type == Boolean.class || type == String.class;
}

private String getTopicsString() {
Expand All @@ -735,8 +736,8 @@ private static class PollablePartitionsInfo {
PollablePartitionsInfo(Set<TopicPartition> pollablePartitions, Map<TopicPartition, Long> earliestRetriableOffsets) {
this.pollablePartitions = pollablePartitions;
this.pollableEarliestRetriableOffsets = earliestRetriableOffsets.entrySet().stream()
.filter(entry -> pollablePartitions.contains(entry.getKey()))
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
.filter(entry -> pollablePartitions.contains(entry.getKey()))
.collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
}

public boolean shouldPoll() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -22,14 +22,15 @@
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.common.TopicPartition;
import org.apache.storm.kafka.spout.internal.OffsetManager;
import org.apache.storm.task.TopologyContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
* This class is used to manage both the partition and topic level offset metrics.
*/
Expand All @@ -55,20 +56,22 @@ public KafkaOffsetMetricManager(Supplier<Map<TopicPartition, OffsetManager>> off
}

public void registerMetricsForNewTopicPartitions(Set<TopicPartition> newAssignment) {

for (TopicPartition topicPartition : newAssignment) {
if (!topicPartitionMetricsMap.containsKey(topicPartition)) {
LOG.info("Registering metric for topicPartition: {}", topicPartition);
// create topic level metrics for given topic if absent
String topic = topicPartition.topic();
KafkaOffsetTopicMetrics topicMetrics = topicMetricsMap.get(topic);
if (topicMetrics == null) {
topicMetrics = new KafkaOffsetTopicMetrics(topic);
LOG.info("Registering metric for topic: {}", topic);
topicMetrics = new KafkaOffsetTopicMetrics(topic, offsetManagerSupplier, adminSupplier, newAssignment);
topicMetricsMap.put(topic, topicMetrics);
topologyContext.registerMetricSet("kafkaOffset", topicMetrics);
}

KafkaOffsetPartitionMetrics topicPartitionMetricSet
= new KafkaOffsetPartitionMetrics<>(offsetManagerSupplier, adminSupplier, topicPartition, topicMetrics);
= new KafkaOffsetPartitionMetrics<>(offsetManagerSupplier, adminSupplier, topicPartition);
topicPartitionMetricsMap.put(topicPartition, topicPartitionMetricSet);
topologyContext.registerMetricSet("kafkaOffset", topicPartitionMetricSet);
}
Expand Down
Loading