blob: 4b7eaae16de852c7cbf37d6ada3ff9d55b0349ec [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/mergetree/levels.h"
#include <algorithm>
namespace paimon {
bool Levels::Level0Comparator::operator()(const std::shared_ptr<DataFileMeta>& a,
const std::shared_ptr<DataFileMeta>& b) const {
if (a->max_sequence_number != b->max_sequence_number) {
// file with larger sequence number should be in front
return a->max_sequence_number > b->max_sequence_number;
} else {
// When two or more jobs are writing the same merge tree, it is
// possible that multiple files have the same maxSequenceNumber. In
// this case we have to compare their file names so that files with
// same maxSequenceNumber won't be "de-duplicated" by the tree set.
int64_t min_seq_a = a->min_sequence_number;
int64_t min_seq_b = b->min_sequence_number;
if (min_seq_a != min_seq_b) {
return min_seq_a < min_seq_b;
}
// If minSequenceNumber is also the same, use creation time
Timestamp time_a = a->creation_time;
Timestamp time_b = b->creation_time;
if (time_a != time_b) {
return time_a < time_b;
}
// Final fallback: filename (to ensure uniqueness in set)
return a->file_name < b->file_name;
}
}
Result<std::unique_ptr<Levels>> Levels::Create(
const std::shared_ptr<FieldsComparator>& key_comparator,
const std::vector<std::shared_ptr<DataFileMeta>>& input_files, int32_t num_levels) {
// in case the num of levels is not specified explicitly
int32_t restored_num_levels = -1;
for (const auto& file : input_files) {
if (file->level > restored_num_levels) {
restored_num_levels = file->level;
}
}
restored_num_levels = std::max(restored_num_levels + 1, num_levels);
if (restored_num_levels <= 1) {
return Status::Invalid("Number of levels must be at least 2.");
}
std::set<std::shared_ptr<DataFileMeta>, Levels::Level0Comparator> level0;
std::vector<SortedRun> levels;
levels.reserve(restored_num_levels - 1);
for (int32_t i = 1; i < restored_num_levels; ++i) {
levels.push_back(SortedRun::Empty());
}
auto level_map = GroupByLevel(input_files);
for (auto& [level, files] : level_map) {
PAIMON_RETURN_NOT_OK(
UpdateLevel(level, /*before=*/{}, /*after=*/files, key_comparator, &levels, &level0));
}
size_t total_file_num = level0.size();
for (const auto& run : levels) {
total_file_num += run.Files().size();
}
if (total_file_num != input_files.size()) {
return Status::Invalid(
"Number of files stored in Levels does not equal to the size of inputFiles. This "
"is unexpected.");
}
return std::unique_ptr<Levels>(new Levels(key_comparator, level0, levels));
}
int32_t Levels::NumberOfSortedRuns() const {
int32_t number_of_runs = level0_.size();
for (const auto& run : levels_) {
if (!run.IsEmpty()) {
number_of_runs++;
}
}
return number_of_runs;
}
void Levels::AddDropFileCallback(DropFileCallback* callback) {
drop_file_callbacks_.push_back(callback);
}
void Levels::RemoveDropFileCallback(DropFileCallback* callback) {
drop_file_callbacks_.erase(
std::remove(drop_file_callbacks_.begin(), drop_file_callbacks_.end(), callback),
drop_file_callbacks_.end());
}
Status Levels::AddLevel0File(const std::shared_ptr<DataFileMeta>& file) {
if (file->level != 0) {
return Status::Invalid("must add level0 file in AddLevel0File");
}
level0_.insert(file);
return Status::OK();
}
int32_t Levels::NonEmptyHighestLevel() const {
for (int32_t i = levels_.size() - 1; i >= 0; i--) {
if (!levels_[i].IsEmpty()) {
return i + 1;
}
}
return level0_.empty() ? -1 : 0;
}
int64_t Levels::TotalFileSize() const {
int64_t total_size = 0;
for (const auto& file : level0_) {
total_size += file->file_size;
}
for (const auto& run : levels_) {
total_size += run.TotalSize();
}
return total_size;
}
std::vector<std::shared_ptr<DataFileMeta>> Levels::AllFiles() const {
std::vector<std::shared_ptr<DataFileMeta>> all_files;
auto runs = LevelSortedRuns();
for (const auto& run : runs) {
all_files.insert(all_files.end(), run.run.Files().begin(), run.run.Files().end());
}
return all_files;
}
std::vector<LevelSortedRun> Levels::LevelSortedRuns() const {
std::vector<LevelSortedRun> runs;
for (const auto& file : level0_) {
runs.emplace_back(/*level=*/0, SortedRun::FromSingle(file));
}
for (int32_t i = 0; i < static_cast<int32_t>(levels_.size()); i++) {
const auto& run = levels_[i];
if (!run.IsEmpty()) {
runs.emplace_back(/*level=*/i + 1, run);
}
}
return runs;
}
Status Levels::Update(const std::vector<std::shared_ptr<DataFileMeta>>& before,
const std::vector<std::shared_ptr<DataFileMeta>>& after) {
auto grouped_before = GroupByLevel(before);
auto grouped_after = GroupByLevel(after);
int32_t number_of_levels = NumberOfLevels();
for (int32_t i = 0; i < number_of_levels; i++) {
PAIMON_RETURN_NOT_OK(UpdateLevel(i, grouped_before[i], grouped_after[i], key_comparator_,
&levels_, &level0_));
}
if (!drop_file_callbacks_.empty()) {
std::set<std::string> dropped_files;
for (const auto& file : before) {
dropped_files.insert(file->file_name);
}
// exclude upgrade files
for (const auto& file : after) {
dropped_files.erase(file->file_name);
}
for (auto* callback : drop_file_callbacks_) {
if (!callback) {
continue;
}
for (const auto& file_name : dropped_files) {
callback->NotifyDropFile(file_name);
}
}
}
return Status::OK();
}
Status Levels::UpdateLevel(int32_t level, const std::vector<std::shared_ptr<DataFileMeta>>& before,
const std::vector<std::shared_ptr<DataFileMeta>>& after,
const std::shared_ptr<FieldsComparator>& key_comparator,
std::vector<SortedRun>* levels,
std::set<std::shared_ptr<DataFileMeta>, Level0Comparator>* level0) {
if (before.empty() && after.empty()) {
return Status::OK();
}
if (level == 0) {
for (const auto& file : before) {
level0->erase(file);
}
for (const auto& file : after) {
level0->insert(file);
}
} else {
PAIMON_ASSIGN_OR_RAISE(SortedRun run, RunOfLevel(level, *levels));
std::vector<std::shared_ptr<DataFileMeta>> files = run.Files();
for (const auto& before_file : before) {
auto iter = std::find_if(files.begin(), files.end(), [&before_file](const auto& cur) {
return before_file->file_name == cur->file_name;
});
if (iter != files.end()) {
files.erase(iter);
}
}
files.insert(files.end(), after.begin(), after.end());
PAIMON_ASSIGN_OR_RAISE((*levels)[level - 1],
SortedRun::FromUnsorted(files, key_comparator));
}
return Status::OK();
}
Result<SortedRun> Levels::RunOfLevel(int32_t level, const std::vector<SortedRun>& levels) {
if (level <= 0) {
return Status::Invalid("Level0 does not have one single sorted run.");
}
return levels[level - 1];
}
std::map<int32_t, std::vector<std::shared_ptr<DataFileMeta>>> Levels::GroupByLevel(
const std::vector<std::shared_ptr<DataFileMeta>>& files) {
std::map<int32_t, std::vector<std::shared_ptr<DataFileMeta>>> level_map;
for (const auto& file : files) {
level_map[file->level].push_back(file);
}
return level_map;
}
} // namespace paimon