blob: 4bde69b1f7894eb1873539a8603e80218c2da237 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* 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
*
* 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.
*/
#include "paimon/core/utils/consumer_manager.h"
#include <algorithm>
#include <chrono>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "paimon/common/utils/path_util.h"
#include "paimon/common/utils/string_utils.h"
#include "paimon/core/utils/branch_manager.h"
#include "paimon/fs/file_system.h"
#include "rapidjson/document.h"
namespace paimon {
ConsumerManager::ConsumerManager(std::shared_ptr<FileSystem> fs, std::string table_path,
std::string branch)
: fs_(std::move(fs)),
table_path_(std::move(table_path)),
branch_(BranchManager::NormalizeBranch(branch)) {}
std::string ConsumerManager::ConsumerDirectory() const {
return PathUtil::JoinPath(BranchManager::BranchPath(table_path_, branch_), "consumer");
}
std::string ConsumerManager::ConsumerPath(const std::string& consumer_id) const {
return PathUtil::JoinPath(ConsumerDirectory(), std::string(kConsumerPrefix) + consumer_id);
}
Result<std::vector<std::string>> ConsumerManager::ListConsumers() const {
std::vector<std::string> consumers;
std::string consumer_dir = ConsumerDirectory();
PAIMON_ASSIGN_OR_RAISE(bool exists, fs_->Exists(consumer_dir));
if (!exists) {
return consumers;
}
std::vector<std::unique_ptr<BasicFileStatus>> file_status_list;
PAIMON_RETURN_NOT_OK(fs_->ListDir(consumer_dir, &file_status_list));
std::string prefix = kConsumerPrefix;
for (const auto& file_status : file_status_list) {
if (file_status->IsDir()) {
continue;
}
std::string file_name = PathUtil::GetName(file_status->GetPath());
if (StringUtils::StartsWith(file_name, prefix, /*start_pos=*/0)) {
consumers.push_back(file_name.substr(prefix.length()));
}
}
std::sort(consumers.begin(), consumers.end());
return consumers;
}
Result<std::optional<int64_t>> ConsumerManager::GetNextSnapshotId(
const std::string& consumer_id) const {
constexpr int32_t kMaxRetryCount = 10;
constexpr int32_t kRetryIntervalMillis = 200;
Status last_error;
for (int32_t i = 0; i < kMaxRetryCount; ++i) {
std::string content;
Status read_status = fs_->ReadFile(ConsumerPath(consumer_id), &content);
if (!read_status.ok()) {
if (read_status.IsNotExist()) {
return std::optional<int64_t>();
}
return read_status;
}
std::optional<int64_t> snapshot_id = StringUtils::StringToValue<int64_t>(content);
if (snapshot_id) {
return snapshot_id;
}
rapidjson::Document document;
document.Parse(content.c_str());
if (!document.HasParseError() && document.IsObject() &&
document.HasMember("nextSnapshot") && document["nextSnapshot"].IsInt64()) {
return std::optional<int64_t>(document["nextSnapshot"].GetInt64());
}
last_error =
Status::Invalid("failed to parse consumer metadata: ", ConsumerPath(consumer_id));
std::this_thread::sleep_for(std::chrono::milliseconds(kRetryIntervalMillis));
}
return last_error;
}
Result<std::map<std::string, int64_t>> ConsumerManager::Consumers() const {
std::map<std::string, int64_t> consumers;
PAIMON_ASSIGN_OR_RAISE(std::vector<std::string> consumer_ids, ListConsumers());
for (const auto& id : consumer_ids) {
PAIMON_ASSIGN_OR_RAISE(std::optional<int64_t> next_snapshot_id, GetNextSnapshotId(id));
if (next_snapshot_id) {
consumers[id] = next_snapshot_id.value();
}
}
return consumers;
}
} // namespace paimon