blob: 436a0fb94bbfef7d60b898c790debe54670f5459 [file] [log] [blame]
// 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 <gtest/gtest.h>
#include <random>
#include "io/fs/file_writer.h"
#include "io/fs/local_file_system.h"
#include "olap/olap_common.h"
#include "olap/rowset/beta_rowset.h"
#include "olap/rowset/rowset_meta_manager.h"
#include "olap/storage_engine.h"
#include "olap/tablet_fwd.h"
#include "olap/tablet_manager.h"
#include "runtime/exec_env.h"
namespace doris {
TEST(PathGcTest, GcTabletAndRowset) {
const std::string dir_path = "ut_dir/path_gc_test";
Defer defer {[&] {
ExecEnv::GetInstance()->set_storage_engine(nullptr);
auto st = io::global_local_filesystem()->delete_directory(dir_path);
}};
auto&& fs = io::global_local_filesystem();
auto st = fs->delete_directory(dir_path);
ASSERT_TRUE(st.ok()) << st;
st = fs->create_directory(dir_path);
ASSERT_TRUE(st.ok()) << st;
StorageEngine engine({});
DataDir data_dir(engine, dir_path);
st = data_dir._init_meta();
ASSERT_TRUE(st.ok()) << st;
// Prepare tablets
auto create_tablet = [&](int64_t tablet_id) {
auto tablet_meta = std::make_shared<TabletMeta>();
tablet_meta->_tablet_id = tablet_id;
(void)tablet_meta->set_partition_id(10000);
tablet_meta->set_tablet_uid({tablet_id, 0});
tablet_meta->set_shard_id(tablet_id % 4);
tablet_meta->_schema_hash = tablet_id;
auto tablet = std::make_shared<Tablet>(engine, std::move(tablet_meta), &data_dir);
auto& tablet_map = engine.tablet_manager()->_get_tablet_map(tablet_id);
tablet_map[tablet_id] = tablet;
return tablet;
};
std::vector<TabletSharedPtr> active_tablets;
int64_t next_tablet_id = 10000;
for (int64_t i = 0; i < 10; ++i) {
int64_t tablet_id = ++next_tablet_id;
active_tablets.push_back(create_tablet(tablet_id));
}
// Prepare tablet directories
for (auto&& tablet : active_tablets) {
st = fs->create_directory(tablet->tablet_path());
ASSERT_TRUE(st.ok()) << st;
}
// Prepare garbage tablet directories
for (int64_t i = 0; i < 10; ++i) {
int64_t tablet_id = ++next_tablet_id;
// {dir_path}/data/{shard_id}/{tablet_id}/{schema_hash}
st = fs->create_directory(
fmt::format("{}/data/{}/{}/{}", dir_path, tablet_id % 4, tablet_id, tablet_id));
ASSERT_TRUE(st.ok()) << st;
}
// Test tablet gc
// Prepare rowsets
auto rng = std::default_random_engine {static_cast<uint32_t>(::time(nullptr))};
std::uniform_int_distribution<int64_t> u(0, active_tablets.size() - 1);
auto create_rowset = [&]() {
auto rowset_meta = std::make_shared<RowsetMeta>();
auto&& tablet = active_tablets[u(rng)];
rowset_meta->set_tablet_id(tablet->tablet_id());
rowset_meta->set_tablet_uid(tablet->tablet_uid());
rowset_meta->set_rowset_id(engine.next_rowset_id());
return std::make_shared<BetaRowset>(tablet->tablet_schema(), std::move(rowset_meta),
tablet->tablet_path());
};
// tablet_id -> filenames
std::unordered_map<int64_t, std::vector<std::string>> expected_rowset_files;
auto create_rowset_files = [&](const BetaRowset& rs, bool is_garbage) {
auto& filenames = expected_rowset_files[rs.rowset_meta()->tablet_id()];
std::unique_ptr<io::FileWriter> writer;
auto filename = fmt::format("{}_{}.dat", rs.rowset_id().to_string(), 0);
RETURN_IF_ERROR(fs->create_file(rs.tablet_path() + '/' + filename, &writer));
if (!is_garbage) {
filenames.push_back(std::move(filename));
}
RETURN_IF_ERROR(writer->close());
filename = fmt::format("{}_{}_{}.idx", rs.rowset_id().to_string(), 0, 987);
RETURN_IF_ERROR(fs->create_file(rs.tablet_path() + '/' + filename, &writer));
if (!is_garbage) {
filenames.push_back(std::move(filename));
}
RETURN_IF_ERROR(writer->close());
filename = fmt::format("{}_{}.dat", rs.rowset_id().to_string(), 1);
RETURN_IF_ERROR(fs->create_file(rs.tablet_path() + '/' + filename, &writer));
if (!is_garbage) {
filenames.push_back(std::move(filename));
}
RETURN_IF_ERROR(writer->close());
filename = fmt::format("{}_{}_{}.idx", rs.rowset_id().to_string(), 1, 987);
RETURN_IF_ERROR(fs->create_file(rs.tablet_path() + '/' + filename, &writer));
if (!is_garbage) {
filenames.push_back(std::move(filename));
}
return writer->close();
};
// Prepare pending rowsets
std::vector<PendingRowsetGuard> guards;
for (int i = 0; i < 20; ++i) {
auto rs = create_rowset();
st = create_rowset_files(*rs, false);
ASSERT_TRUE(st.ok()) << st;
guards.push_back(engine.pending_local_rowsets().add(rs->rowset_id()));
}
// Prepare unused rowsets
for (int i = 0; i < 30; ++i) {
auto rs = create_rowset();
st = create_rowset_files(*rs, false);
ASSERT_TRUE(st.ok()) << st;
engine.add_unused_rowset(std::move(rs));
}
// Prepare visible rowsets
for (int i = 0; i < 30; ++i) {
auto rs = create_rowset();
st = create_rowset_files(*rs, false);
ASSERT_TRUE(st.ok()) << st;
auto tablet = engine.tablet_manager()->get_tablet(rs->rowset_meta()->tablet_id());
ASSERT_TRUE(tablet) << rs->rowset_meta()->tablet_id();
auto max_version = tablet->max_version_unlocked();
rs->rowset_meta()->set_version({max_version + 1, max_version + 1});
st = tablet->add_inc_rowset(rs);
ASSERT_TRUE(st.ok()) << st;
}
// Prepare rowsets in OlapMeta
for (int i = 0; i < 20; ++i) {
auto rs = create_rowset();
st = create_rowset_files(*rs, false);
ASSERT_TRUE(st.ok()) << st;
st = RowsetMetaManager::save(data_dir.get_meta(), rs->rowset_meta()->tablet_uid(),
rs->rowset_id(), rs->rowset_meta()->get_rowset_pb(), false);
ASSERT_TRUE(st.ok()) << st;
}
// Prepare garbage rowset files
for (int i = 0; i < 20; ++i) {
auto rs = create_rowset();
st = create_rowset_files(*rs, true);
ASSERT_TRUE(st.ok()) << st;
}
// Test rowset gc
data_dir.perform_path_gc();
for (auto&& t : active_tablets) {
std::vector<io::FileInfo> files;
bool exists;
st = fs->list(t->tablet_path(), true, &files, &exists);
ASSERT_TRUE(st.ok()) << st;
auto&& expected_files = expected_rowset_files[t->tablet_id()];
ASSERT_EQ(files.size(), expected_files.size());
std::sort(expected_files.begin(), expected_files.end());
std::sort(files.begin(), files.end(),
[](auto&& file1, auto&& file2) { return file1.file_name < file2.file_name; });
for (size_t i = 0; i < files.size(); ++i) {
EXPECT_EQ(files[i].file_name, expected_files[i]);
}
}
}
} // namespace doris