io.streamnative.pulsar.handlers.kop.PendingTopicFutures Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pulsar-protocol-handler-kafka Show documentation
Show all versions of pulsar-protocol-handler-kafka Show documentation
Kafka on Pulsar implemented using Pulsar Protocol Handler
The newest version!
/**
* Copyright (c) 2019 - 2024 StreamNative, Inc.. All Rights Reserved.
*/
/**
* Licensed 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.streamnative.pulsar.handlers.kop;
import com.google.common.annotations.VisibleForTesting;
import io.streamnative.pulsar.handlers.kop.storage.PartitionLog;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import lombok.Getter;
import lombok.NonNull;
/**
* Pending futures of PersistentTopic.
* It's used when multiple produce requests encountered while the partition's PersistentTopic was not available.
*/
public class PendingTopicFutures {
private int count = 0;
private CompletableFuture currentTopicFuture;
public PendingTopicFutures() {}
private synchronized void decrementCount() {
count--;
}
public synchronized void addListener(CompletableFuture topicFuture,
@NonNull Consumer persistentTopicConsumer,
@NonNull Consumer exceptionConsumer) {
if (count == 0) {
count = 1;
// The first pending future comes
currentTopicFuture = topicFuture.thenApply(persistentTopic -> {
persistentTopicConsumer.accept(persistentTopic);
decrementCount();
return TopicThrowablePair.withTopic(persistentTopic);
}).exceptionally(e -> {
exceptionConsumer.accept(e.getCause());
decrementCount();
return TopicThrowablePair.withThrowable(e.getCause());
});
} else {
count++;
// The next pending future reuses the completed result of the previous topic future
currentTopicFuture = currentTopicFuture.thenApply(topicThrowablePair -> {
if (topicThrowablePair.getThrowable() == null) {
persistentTopicConsumer.accept(topicThrowablePair.getPersistentTopicOpt());
} else {
exceptionConsumer.accept(topicThrowablePair.getThrowable());
}
decrementCount();
return topicThrowablePair;
}).exceptionally(e -> {
exceptionConsumer.accept(e.getCause());
decrementCount();
return TopicThrowablePair.withThrowable(e.getCause());
});
}
}
@VisibleForTesting
public synchronized int waitAndGetSize() throws ExecutionException, InterruptedException {
currentTopicFuture.get();
return count;
}
@VisibleForTesting
public synchronized int size() {
return count;
}
}
class TopicThrowablePair {
@Getter
private final PartitionLog persistentTopicOpt;
@Getter
private final Throwable throwable;
public static TopicThrowablePair withTopic(final PartitionLog persistentTopicOpt) {
return new TopicThrowablePair(persistentTopicOpt, null);
}
public static TopicThrowablePair withThrowable(final Throwable throwable) {
return new TopicThrowablePair(null, throwable);
}
private TopicThrowablePair(final PartitionLog persistentTopicOpt, final Throwable throwable) {
this.persistentTopicOpt = persistentTopicOpt;
this.throwable = throwable;
}
};