blob: a60ddc1f7254927a3cf0b40a172b8bdcc7a28d21 [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.
*/
// IWYU pragma: no_include <ext/alloc_traits.h>
// IWYU pragma: no_include <gtest/gtest-message.h>
// IWYU pragma: no_include <gtest/gtest-test-part.h>
#include <gtest/gtest.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <string>
#include <vector>
#include "common/fs_manager.h"
#include "common/gpid.h"
#include "common/replication_other_types.h"
#include "metadata_types.h"
#include "utils/fail_point.h"
#include "utils/filesystem.h"
using namespace dsn::utils::filesystem;
namespace dsn {
namespace replication {
TEST(dir_node, replica_dir)
{
dir_node dn("tag", "path");
ASSERT_EQ("path/1.0.test", dn.replica_dir("test", gpid(1, 0)));
}
TEST(fs_manager, initialize)
{
fail::setup();
struct broken_disk_test
{
std::string create_dir_ok;
std::string check_dir_rw_ok;
int32_t data_dir_size;
} tests[]{{"true", "true", 3}, {"true", "false", 2}, {"false", "false", 2}};
int i = 0;
for (const auto &test : tests) {
fail::cfg("filesystem_create_directory", "return(" + test.create_dir_ok + ")");
fail::cfg("filesystem_check_dir_rw", "return(" + test.check_dir_rw_ok + ")");
fs_manager fm;
fm.initialize({"disk1", "disk2", "disk3"}, {"tag1", "tag2", "tag3"});
ASSERT_EQ(test.data_dir_size, fm.get_dir_nodes().size()) << i;
i++;
}
fail::teardown();
}
TEST(fs_manager, dir_update_disk_status)
{
struct update_disk_status
{
bool mock_insufficient;
disk_status::type old_disk_status;
disk_status::type new_disk_status;
} tests[] = {{false, disk_status::NORMAL, disk_status::NORMAL},
{false, disk_status::SPACE_INSUFFICIENT, disk_status::NORMAL},
{true, disk_status::NORMAL, disk_status::SPACE_INSUFFICIENT},
{true, disk_status::SPACE_INSUFFICIENT, disk_status::SPACE_INSUFFICIENT}};
for (const auto &test : tests) {
auto node = std::make_shared<dir_node>("tag", "path", 0, 0, 0, test.old_disk_status);
fail::setup();
if (test.mock_insufficient) {
fail::cfg("filesystem_get_disk_space_info", "return(insufficient)");
} else {
fail::cfg("filesystem_get_disk_space_info", "return(normal)");
}
node->update_disk_stat();
ASSERT_EQ(test.new_disk_status, node->status);
fail::teardown();
}
}
TEST(fs_manager, get_dir_node)
{
fs_manager fm;
fm.initialize({"./data1"}, {"data1"});
const auto &dns = fm.get_dir_nodes();
ASSERT_EQ(1, dns.size());
const auto &base_dir =
dns[0]->full_dir.substr(0, dns[0]->full_dir.size() - std::string("/data1").size());
ASSERT_EQ(nullptr, fm.get_dir_node(""));
ASSERT_EQ(nullptr, fm.get_dir_node("/"));
ASSERT_NE(nullptr, fm.get_dir_node(base_dir + "/data1"));
ASSERT_NE(nullptr, fm.get_dir_node(base_dir + "/data1/"));
ASSERT_NE(nullptr, fm.get_dir_node(base_dir + "/data1/replica1"));
ASSERT_EQ(nullptr, fm.get_dir_node(base_dir + "/data2"));
ASSERT_EQ(nullptr, fm.get_dir_node(base_dir + "/data2/"));
ASSERT_EQ(nullptr, fm.get_dir_node(base_dir + "/data2/replica1"));
}
TEST(fs_manager, find_replica_dir)
{
fs_manager fm;
fm.initialize({"./data1", "./data2", "./data3"}, {"data1", "data2", "data3"});
const char *app_type = "find_replica_dir";
gpid test_pid(1, 0);
// Clear up the remaining directories if exist.
for (const auto &dn : fm.get_dir_nodes()) {
remove_path(dn->replica_dir(app_type, test_pid));
}
ASSERT_EQ(nullptr, fm.find_replica_dir(app_type, test_pid));
auto dn = fm.create_replica_dir_if_necessary(app_type, test_pid);
ASSERT_NE(nullptr, dn);
const auto dir = dn->replica_dir(app_type, test_pid);
ASSERT_TRUE(directory_exists(dir));
auto dn1 = fm.find_replica_dir(app_type, test_pid);
ASSERT_EQ(dn, dn1);
}
TEST(fs_manager, create_replica_dir_if_necessary)
{
fs_manager fm;
const char *app_type = "create_replica_dir_if_necessary";
gpid test_pid(1, 0);
// Could not find a valid dir_node.
ASSERT_EQ(nullptr, fm.create_replica_dir_if_necessary(app_type, test_pid));
// It's able to create a dir for the replica after a valid dir_node has been added.
fm.add_new_dir_node("./data1", "data1");
dir_node *dn = fm.create_replica_dir_if_necessary(app_type, test_pid);
ASSERT_NE(nullptr, dn);
ASSERT_EQ("data1", dn->tag);
}
TEST(fs_manager, create_child_replica_dir)
{
fs_manager fm;
fm.initialize({"./data1", "./data2", "./data3"}, {"data1", "data2", "data3"});
const char *app_type = "create_child_replica_dir";
gpid test_pid(1, 0);
gpid test_child_pid(1, 0);
dir_node *dn = fm.create_replica_dir_if_necessary(app_type, test_pid);
ASSERT_NE(nullptr, dn);
const auto dir = dn->replica_dir(app_type, test_pid);
auto child_dn = fm.create_child_replica_dir(app_type, test_child_pid, dir);
ASSERT_EQ(dn, child_dn);
const auto child_dir = child_dn->replica_dir(app_type, test_child_pid);
ASSERT_TRUE(directory_exists(child_dir));
ASSERT_EQ(dir, child_dir);
}
TEST(fs_manager, find_best_dir_for_new_replica)
{
// dn1 | 1.0, 1.1 +1.6
// dn2 | 1.2, 1.3 +1.7 2.0
// dn3 | 1.4 +1.5 +1.7 2.1
auto dn1 = std::make_shared<dir_node>("./data1", "data1");
dn1->holding_replicas[1] = {gpid(1, 0), gpid(1, 1)};
auto dn2 = std::make_shared<dir_node>("./data2", "data2");
dn2->holding_replicas[1] = {gpid(1, 2), gpid(1, 3)};
dn2->holding_replicas[2] = {gpid(2, 0)};
auto dn3 = std::make_shared<dir_node>("./data3", "data3");
dn3->holding_replicas[1] = {gpid(1, 4)};
dn3->holding_replicas[2] = {gpid(2, 1)};
fs_manager fm;
fm._dir_nodes = {dn1, dn2, dn3};
gpid pid_1_5(1, 5);
auto dn = fm.find_best_dir_for_new_replica(pid_1_5);
ASSERT_EQ(dn3.get(), dn);
dn->holding_replicas[pid_1_5.get_app_id()].emplace(pid_1_5);
gpid pid_1_6(1, 6);
dn = fm.find_best_dir_for_new_replica(pid_1_6);
ASSERT_EQ(dn1.get(), dn);
dn->holding_replicas[pid_1_6.get_app_id()].emplace(pid_1_6);
gpid pid_1_7(1, 7);
dn = fm.find_best_dir_for_new_replica(pid_1_7);
ASSERT_TRUE(dn == dn2.get() || dn == dn3.get());
dn->holding_replicas[pid_1_7.get_app_id()].emplace(pid_1_7);
}
} // namespace replication
} // namespace dsn