* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "KafkaSourceReader.h"
#include "fetcher/KafkaSourceFetcherManager.h"
KafkaSourceReader::KafkaSourceReader(
FutureCompletingBlockingQueue<RdKafka::Message>* elementsQueue,
SingleThreadFetcherManager<RdKafka::Message, KafkaPartitionSplit>* splitFetcherManager,
RecordEmitter<RdKafka::Message, KafkaPartitionSplitState>* recordEmitter,
SourceReaderContext* context,
bool isBatch)
: SingleThreadMultiplexSourceReaderBase<RdKafka::Message, KafkaPartitionSplit, KafkaPartitionSplitState>(
elementsQueue, splitFetcherManager, recordEmitter, context, isBatch)
{
commitOffsetsOnCheckpoint_ = false;
}
KafkaPartitionSplitState* KafkaSourceReader::initializedState(KafkaPartitionSplit* split)
{
return new KafkaPartitionSplitState(split);
}
std::vector<KafkaPartitionSplit> KafkaSourceReader::snapshotState(long checkpointId)
{
std::vector<KafkaPartitionSplit> splits = SourceReaderBase::snapshotState(checkpointId);
if (!commitOffsetsOnCheckpoint_) {
return splits;
}
if (splits.empty() && offsetsOfFinishedSplits.empty()) {
std::lock_guard<std::mutex> lock(mutex_);
offsetsToCommit_[checkpointId] = std::unordered_map<std::shared_ptr<RdKafka::TopicPartition>, long>();
} else {
std::lock_guard<std::mutex> lock(mutex_);
auto& offsetsMap = offsetsToCommit_[checkpointId];
for (const auto& split : splits) {
if (split.getStartingOffset() >= 0) {
offsetsMap[split.getTopicPartition()] = split.getStartingOffset();
}
}
for (const auto& [tp, offset] : offsetsOfFinishedSplits) {
offsetsMap[tp] = offset;
}
}
return splits;
}
void KafkaSourceReader::notifyCheckpointComplete(long checkpointId)
{
if (!commitOffsetsOnCheckpoint_) {
return;
}
std::shared_ptr<std::map<std::shared_ptr<RdKafka::TopicPartition>, int64_t>> committedPartitions;
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = offsetsToCommit_.find(checkpointId);
if (it == offsetsToCommit_.end()) {
return;
}
auto convertedMap = std::make_shared<std::map<std::shared_ptr<RdKafka::TopicPartition>, int64_t>>();
for (const auto& entry : it->second) {
(*convertedMap)[entry.first] = entry.second;
}
committedPartitions = convertedMap;
}
auto kafkaFetcherManager = static_cast<KafkaSourceFetcherManager*>(splitFetcherManager);
kafkaFetcherManager->commitOffsets(
*committedPartitions,
[this, checkpointId, committedPartitions](
const std::map<std::shared_ptr<RdKafka::TopicPartition>, int64_t>&, const std::exception_ptr& e) {
if (e != nullptr) {
INFO_RELEASE("Error:Failed to commit consumer offsets for checkpoint:" << checkpointId << ", error: ");
} else {
INFO_RELEASE("Successfully committed offsets for checkpoint:" << checkpointId);
std::lock_guard<std::mutex> lock(mutex_);
for (const auto& entry : *committedPartitions) {
offsetsOfFinishedSplits.erase(entry.first);
}
auto it = offsetsToCommit_.begin();
while (it != offsetsToCommit_.end() && it->first <= checkpointId) {
it = offsetsToCommit_.erase(it);
}
}
});
}