diff --git a/src/common/replication_common.cpp b/src/common/replication_common.cpp index cbcddd5a07..685995f624 100644 --- a/src/common/replication_common.cpp +++ b/src/common/replication_common.cpp @@ -24,14 +24,15 @@ * THE SOFTWARE. */ -#include "replication_common.h" - #include +#include #include #include #include +#include "replication_common.h" + namespace dsn { namespace replication { @@ -135,106 +136,31 @@ void replication_options::initialize() } slog_dir = utils::filesystem::path_combine(slog_dir, "slog"); - // data_dirs - // - if config[data_dirs] is empty: "app_dir/reps" - // - else: - // config[data_dirs] = "tag1:dir1,tag2:dir2:tag3:dir3" - // data_dir = "config[data_dirs]/app_name/reps" - std::string dirs_str = + // get config_data_dirs and config_data_dir_tags from config + const std::string &dirs_str = dsn_config_get_value_string("replication", "data_dirs", "", "replica directory list"); - std::vector dirs; - std::vector dir_tags; - ::dsn::utils::split_args(dirs_str.c_str(), dirs, ','); - if (dirs.empty()) { - dirs.push_back(app_dir); - dir_tags.push_back("default"); - } else { - for (auto &dir : dirs) { - std::vector tag_and_dir; - ::dsn::utils::split_args(dir.c_str(), tag_and_dir, ':'); - if (tag_and_dir.size() != 2) { - dassert(false, "invalid data_dir item(%s) in config", dir.c_str()); - } else { - dassert(!tag_and_dir[0].empty() && !tag_and_dir[1].empty(), - "invalid data_dir item(%s) in config", - dir.c_str()); - dir = utils::filesystem::path_combine(tag_and_dir[1], app_name); - for (unsigned i = 0; i < dir_tags.size(); ++i) { - dassert(dirs[i] != dir, - "dir(%s) and dir(%s) conflict", - dirs[i].c_str(), - dir.c_str()); - } - for (unsigned i = 0; i < dir_tags.size(); ++i) { - dassert(dir_tags[i] != tag_and_dir[0], - "dir(%s) and dir(%s) have same tag(%s)", - dirs[i].c_str(), - dir.c_str(), - tag_and_dir[0].c_str()); - } - dir_tags.push_back(tag_and_dir[0]); - } - } - } + std::vector config_data_dirs; + std::vector config_data_dir_tags; + get_data_dir_and_tag(dirs_str, app_dir, app_name, config_data_dirs, config_data_dir_tags); + // check if data_dir in black list, data_dirs doesn't contain dir in black list std::string black_list_file = dsn_config_get_value_string("replication", "data_dirs_black_list_file", "/home/work/.pegasus_data_dirs_black_list", "replica directory black list file"); - std::vector black_list; - if (!black_list_file.empty() && dsn::utils::filesystem::file_exists(black_list_file)) { - ddebug("data_dirs_black_list_file[%s] found, apply it", black_list_file.c_str()); - - std::ifstream file(black_list_file); - if (!file) { - dassert(false, "open data_dirs_black_list_file failed: %s", black_list_file.c_str()); - } - - std::string str; - int count = 0; - while (std::getline(file, str)) { - std::string str2 = dsn::utils::trim_string((char *)str.c_str()); - if (str2.empty()) - continue; - if (str2.back() != '/') - str2.append("/"); - black_list.push_back(str2); - count++; - ddebug("black_list[%d] = [%s]", count, str2.c_str()); - str.clear(); - } - } else { - ddebug("data_dirs_black_list_file[%s] not found, ignore it", black_list_file.c_str()); - } - - int dir_count = 0; - for (unsigned i = 0; i < dirs.size(); ++i) { - std::string &dir = dirs[i]; - bool in_black_list = false; - if (!black_list.empty()) { - std::string dir2 = dir; - if (dir2.back() != '/') - dir2.append("/"); - for (std::string &black : black_list) { - if (dir2.find(black) == 0) { - in_black_list = true; - break; - } - } - } - - if (in_black_list) { - dwarn("replica data dir %s is in black list, ignore it", dir.c_str()); - } else { - ddebug("data_dirs[%d] = %s, tag = %s", dir_count++, dir.c_str(), dir_tags[i].c_str()); - data_dirs.push_back(utils::filesystem::path_combine(dir, "reps")); - data_dir_tags.push_back(dir_tags[i]); + std::vector black_list_dirs; + get_data_dirs_in_black_list(black_list_file, black_list_dirs); + for (auto i = 0; i < config_data_dirs.size(); ++i) { + if (check_if_in_black_list(black_list_dirs, config_data_dirs[i])) { + continue; } + data_dirs.emplace_back(config_data_dirs[i]); + data_dir_tags.emplace_back(config_data_dir_tags[i]); } if (data_dirs.empty()) { - dassert(false, "no replica data dir found, maybe not set or excluded by black list"); + dassert_f(false, "no replica data dir found, maybe not set or excluded by black list"); } deny_client_on_start = dsn_config_get_value_bool("replication", @@ -569,6 +495,112 @@ void replica_helper::load_meta_servers(/*out*/ std::vector &se dassert(servers.size() > 0, "no meta server specified in config [%s].%s", section, key); } +/*static*/ void +replication_options::get_data_dir_and_tag(const std::string &config_dirs_str, + const std::string &default_dir, + const std::string &app_name, + /*out*/ std::vector &data_dirs, + /*out*/ std::vector &data_dir_tags) +{ + // - if {config_dirs_str} is empty (return true): + // - dir = {default_dir} + // - dir_tag/data_dir_tag = "default" + // - data_dir = {default_dir}/"reps" + // - else if {config_dirs_str} = "tag1:dir1,tag2:dir2:tag3:dir3" (return true): + // - dir1 = "dir1"/{app_name} + // - dir_tag1/data_dir_tag1 = "tag1" + // - data_dir1 = "dir1"/{app_name}/"reps" + // - else (return false): + // - invalid format and set {err_msg} + std::vector dirs; + std::vector dir_tags; + utils::split_args(config_dirs_str.c_str(), dirs, ','); + if (dirs.empty()) { + dirs.push_back(default_dir); + dir_tags.push_back("default"); + } else { + for (auto &dir : dirs) { + std::vector tag_and_dir; + utils::split_args(dir.c_str(), tag_and_dir, ':'); + if (tag_and_dir.size() != 2) { + dassert_f("invalid data_dir item({}) in config", dir); + } + if (tag_and_dir[0].empty() || tag_and_dir[1].empty()) { + dassert_f("invalid data_dir item({}) in config", dir); + } + dir = utils::filesystem::path_combine(tag_and_dir[1], app_name); + for (unsigned i = 0; i < dir_tags.size(); ++i) { + if (dirs[i] == dir) { + dassert_f("dir({}) and dir({}) conflict", dirs[i], dir); + } + } + for (unsigned i = 0; i < dir_tags.size(); ++i) { + if (dir_tags[i] == tag_and_dir[0]) { + dassert_f( + "dir({}) and dir({}) have same tag({})", dirs[i], dir, tag_and_dir[0]); + } + } + dir_tags.push_back(tag_and_dir[0]); + } + } + + for (unsigned i = 0; i < dirs.size(); ++i) { + const std::string &dir = dirs[i]; + ddebug_f("data_dirs[{}] = {}, tag = {}", i + 1, dir, dir_tags[i]); + data_dirs.push_back(utils::filesystem::path_combine(dir, "reps")); + data_dir_tags.push_back(dir_tags[i]); + } +} + +/*static*/ void +replication_options::get_data_dirs_in_black_list(const std::string &fname, + /*out*/ std::vector &dirs) +{ + if (fname.empty() || !utils::filesystem::file_exists(fname)) { + ddebug_f("data_dirs_black_list_file[{}] not found, ignore it", fname); + return; + } + + ddebug_f("data_dirs_black_list_file[{}] found, apply it", fname); + std::ifstream file(fname); + if (!file) { + dassert_f(false, "open data_dirs_black_list_file failed: {}", fname); + } + + std::string str; + int count = 0; + while (std::getline(file, str)) { + std::string str2 = utils::trim_string(const_cast(str.c_str())); + if (str2.empty()) { + continue; + } + if (str2.back() != '/') { + str2.append("/"); + } + dirs.push_back(str2); + count++; + ddebug_f("black_list[{}] = [{}]", count, str2); + } +} + +/*static*/ bool +replication_options::check_if_in_black_list(const std::vector &black_list_dir, + const std::string &dir) +{ + std::string dir_str = dir; + if (!black_list_dir.empty()) { + if (dir_str.back() != '/') { + dir_str.append("/"); + } + for (const std::string &black : black_list_dir) { + if (dir_str.find(black) == 0) { + return true; + } + } + } + return false; +} + const std::string backup_restore_constant::FORCE_RESTORE("restore.force_restore"); const std::string backup_restore_constant::BLOCK_SERVICE_PROVIDER("restore.block_service_provider"); const std::string backup_restore_constant::CLUSTER_NAME("restore.cluster_name"); diff --git a/src/common/replication_common.h b/src/common/replication_common.h index b04f35165f..881212e724 100644 --- a/src/common/replication_common.h +++ b/src/common/replication_common.h @@ -132,9 +132,19 @@ class replication_options public: replication_options(); - void initialize(); ~replication_options(); + void initialize(); + static void get_data_dir_and_tag(const std::string &config_dirs_str, + const std::string &default_dir, + const std::string &app_name, + /*out*/ std::vector &data_dirs, + /*out*/ std::vector &data_dir_tags); + static void get_data_dirs_in_black_list(const std::string &fname, + /*out*/ std::vector &dirs); + static bool check_if_in_black_list(const std::vector &black_list_dir, + const std::string &dir); + private: void sanity_check(); }; diff --git a/src/common/test/replication_common_test.cpp b/src/common/test/replication_common_test.cpp new file mode 100644 index 0000000000..2807f854bc --- /dev/null +++ b/src/common/test/replication_common_test.cpp @@ -0,0 +1,103 @@ +/* + * 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 +#include + +#include + +#include "common/replication_common.h" + +namespace dsn { +namespace replication { + +TEST(replication_common, get_data_dir_test) +{ + std::vector data_dirs; + std::vector data_dir_tags; + struct get_data_dir_test + { + std::string data_dir_str; + int32_t expected_length; + } tests[] = {{"", 1}, {"tag1:disk1", 1}, {"tag1:disk1, ", 1}, {"tag1:disk1,tag2:disk2", 2}}; + for (const auto &test : tests) { + data_dirs.clear(); + data_dir_tags.clear(); + replication_options::get_data_dir_and_tag( + test.data_dir_str, "test_dir", "replica", data_dirs, data_dir_tags); + ASSERT_EQ(data_dirs.size(), data_dir_tags.size()); + ASSERT_EQ(data_dirs.size(), test.expected_length); + } +} + +TEST(replication_common, get_black_list_test) +{ + std::string fname = "black_list_file"; + ASSERT_TRUE(utils::filesystem::create_file(fname)); + std::ofstream test_file; + test_file.open(fname); + test_file << "disk1\ndisk2\n"; + test_file.close(); + + std::vector black_list; + // Test cases: + // - file name not set + // - file not exist + // - file exist + struct get_black_list_test + { + std::string fname; + bool has_black_list; + } tests[] = {{"", false}, {"file_not_exist", false}, {"black_list_file", true}}; + for (const auto &test : tests) { + black_list.clear(); + replication_options::get_data_dirs_in_black_list(test.fname, black_list); + ASSERT_EQ(!black_list.empty(), test.has_black_list); + } + utils::filesystem::remove_file_name(fname); +} + +TEST(replication_common, check_in_black_list_test) +{ + std::vector black_list; + black_list.emplace_back("dir1/"); + black_list.emplace_back("dir2/"); + + // Test cases: + // - empty black list + // - not in list + // - in list + struct check_in_list_test + { + bool list_empty; + std::string dir_str; + bool expected_result; + } tests[]{{true, "dir1", false}, {false, "testdir", false}, {false, "dir2", true}}; + for (const auto &test : tests) { + std::vector test_list; + if (!test.list_empty) { + test_list = black_list; + } + ASSERT_EQ(replication_options::check_if_in_black_list(test_list, test.dir_str), + test.expected_result); + } +} + +} // namespace replication +} // namespace dsn