| /* |
| * 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 |