349 lines
10 KiB
C++
349 lines
10 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "lib/utility/ob_test_util.h"
|
|
#include "lib/json/ob_json.h"
|
|
#include "rootserver/ob_unit_placement_strategy.h"
|
|
#include "ob_rs_test_utils.h"
|
|
using namespace oceanbase::common;
|
|
using namespace oceanbase::rootserver;
|
|
using namespace oceanbase;
|
|
|
|
class TestUnitPlacement: public ::testing::Test
|
|
{
|
|
public:
|
|
TestUnitPlacement();
|
|
virtual ~TestUnitPlacement();
|
|
virtual void SetUp();
|
|
virtual void TearDown();
|
|
private:
|
|
// disallow copy
|
|
DISALLOW_COPY_AND_ASSIGN(TestUnitPlacement);
|
|
static const char* const BASE_DIR;
|
|
protected:
|
|
// function members
|
|
void run_case(const char* casename);
|
|
void build_case(json::Value *root,
|
|
common::ObArray<ObUnitPlacementStrategy::ObServerResource> &servers,
|
|
common::ObArray<share::ObUnitConfig> &units);
|
|
void output_result(const char* filename,
|
|
const common::ObArray<ObUnitPlacementStrategy::ObServerResource> &servers,
|
|
const ObArray<int64_t> &units_placement);
|
|
protected:
|
|
// data members
|
|
};
|
|
|
|
const char* const TestUnitPlacement::BASE_DIR = "./unit_placement_testcase/";
|
|
|
|
TestUnitPlacement::TestUnitPlacement()
|
|
{
|
|
}
|
|
|
|
TestUnitPlacement::~TestUnitPlacement()
|
|
{
|
|
}
|
|
|
|
void TestUnitPlacement::SetUp()
|
|
{
|
|
}
|
|
|
|
void TestUnitPlacement::TearDown()
|
|
{
|
|
}
|
|
|
|
class CaseBuilder: public json::Walker
|
|
{
|
|
enum State
|
|
{
|
|
IS_NONE,
|
|
IS_SERVER,
|
|
IS_ZONE,
|
|
IS_SERVER_CPU,
|
|
IS_SERVER_MEM,
|
|
IS_UNIT_CPU,
|
|
IS_UNIT_MEM,
|
|
IS_UNIT_NUM
|
|
};
|
|
public:
|
|
CaseBuilder(const json::Value *root,
|
|
common::ObArray<ObUnitPlacementStrategy::ObServerResource> &servers,
|
|
common::ObArray<share::ObUnitConfig> &units)
|
|
:Walker(root),
|
|
servers_(servers),
|
|
units_(units),
|
|
in_servers_(false),
|
|
in_units_(false),
|
|
state_(IS_NONE),
|
|
cur_unit_config_id_(1)
|
|
{}
|
|
virtual ~CaseBuilder() {}
|
|
|
|
virtual int on_null(int level, json::Type parent)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_true(int level, json::Type parent)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_false(int level, json::Type parent)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_string(int level, json::Type parent, const json::String &str)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
if (state_ == IS_SERVER) {
|
|
cur_server_resource_.addr_.set_ip_addr(str, 8888);
|
|
OB_LOG(DEBUG, "addr value", K(str), "addr", cur_server_resource_.addr_);
|
|
}
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_number(int level, json::Type parent, const json::Number &num)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
switch(state_) {
|
|
case IS_SERVER_CPU:
|
|
cur_server_resource_.capacity_[RES_CPU] = static_cast<double>(num);
|
|
break;
|
|
case IS_SERVER_MEM:
|
|
cur_server_resource_.capacity_[RES_MEM] = static_cast<double>(num);
|
|
break;
|
|
case IS_UNIT_CPU:
|
|
cur_unit_.min_cpu_ = static_cast<double>(num);
|
|
break;
|
|
case IS_UNIT_MEM:
|
|
cur_unit_.min_memory_ = num;
|
|
break;
|
|
case IS_UNIT_NUM:
|
|
cur_unit_num_ = num;
|
|
default:
|
|
break;
|
|
}
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_array_start(int level, json::Type parent, const json::Array &arr)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
UNUSED(arr);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_array_end(int level, json::Type parent, const json::Array &arr)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
UNUSED(arr);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_array_item_start(int level, json::Type parent, const json::Value *val)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
UNUSED(val);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_array_item_end(int level, json::Type parent, const json::Value *val, bool is_last)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
UNUSED(val);
|
|
UNUSED(is_last);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_object_start(int level, json::Type parent, const json::Object &obj)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
UNUSED(obj);
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_object_end(int level, json::Type parent, const json::Object &obj)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(level);
|
|
UNUSED(obj);
|
|
if (parent == json::JT_ARRAY
|
|
&& in_servers_) {
|
|
ret = servers_.push_back(cur_server_resource_);
|
|
} else if (in_units_ && parent == json::JT_ARRAY) {
|
|
cur_unit_.unit_config_id_ = cur_unit_config_id_++;
|
|
for (int64_t i = 0; i < cur_unit_num_ && OB_SUCC(ret); ++i) {
|
|
ret = units_.push_back(cur_unit_);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
virtual int on_object_member_start(int level, json::Type parent, const json::Pair *kv)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
if (kv->name_ == ObString::make_string("servers")) {
|
|
servers_.reuse();
|
|
in_servers_ = true;
|
|
} else if (kv->name_ == ObString::make_string("units")) {
|
|
units_.reuse();
|
|
in_units_ = true;
|
|
}
|
|
|
|
state_ = IS_NONE;
|
|
if (in_servers_) {
|
|
if (kv->name_ == ObString::make_string("server")) {
|
|
state_ = IS_SERVER;
|
|
} else if (kv->name_ == ObString::make_string("zone")) {
|
|
} else if (kv->name_ == ObString::make_string("resources")) {
|
|
} else if (kv->name_ == ObString::make_string("cpu")) {
|
|
state_ = IS_SERVER_CPU;
|
|
} else if (kv->name_ == ObString::make_string("memory")) {
|
|
state_ = IS_SERVER_MEM;
|
|
}
|
|
} else if (in_units_) {
|
|
if (kv->name_ == ObString::make_string("cpu")) {
|
|
state_ = IS_UNIT_CPU;
|
|
} else if (kv->name_ == ObString::make_string("memory")) {
|
|
state_ = IS_UNIT_MEM;
|
|
} else if (kv->name_ == ObString::make_string("num")) {
|
|
state_ = IS_UNIT_NUM;
|
|
}
|
|
}
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_object_member_end(int level, json::Type parent, const json::Pair *kv, bool is_last)
|
|
{
|
|
UNUSED(level);
|
|
UNUSED(parent);
|
|
UNUSED(is_last);
|
|
if (kv->name_ == ObString::make_string("servers")) {
|
|
in_servers_ = false;
|
|
} else if (kv->name_ == ObString::make_string("units")) {
|
|
in_units_ = false;
|
|
}
|
|
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_walk_start()
|
|
{
|
|
return OB_SUCCESS;
|
|
}
|
|
virtual int on_walk_end()
|
|
{
|
|
return OB_SUCCESS;
|
|
}
|
|
private:
|
|
common::ObArray<ObUnitPlacementStrategy::ObServerResource> &servers_;
|
|
common::ObArray<share::ObUnitConfig> &units_;
|
|
ObUnitPlacementStrategy::ObServerResource cur_server_resource_;
|
|
share::ObUnitConfig cur_unit_;
|
|
int64_t cur_unit_num_;
|
|
bool in_servers_;
|
|
bool in_units_;
|
|
State state_;
|
|
uint64_t cur_unit_config_id_;
|
|
};
|
|
|
|
void TestUnitPlacement::build_case(json::Value *root,
|
|
common::ObArray<ObUnitPlacementStrategy::ObServerResource> &servers,
|
|
common::ObArray<share::ObUnitConfig> &units)
|
|
{
|
|
CaseBuilder case_builder(root, servers, units);
|
|
ASSERT_EQ(OB_SUCCESS, case_builder.go());
|
|
OB_LOG(INFO, "CASE", K(servers), K(units));
|
|
}
|
|
|
|
void TestUnitPlacement::output_result(const char* filename,
|
|
const common::ObArray<ObUnitPlacementStrategy::ObServerResource> &servers,
|
|
const ObArray<int64_t> &units_placement)
|
|
{
|
|
FILE *fp = fopen(filename, "w+");
|
|
ASSERT_TRUE(NULL != fp);
|
|
|
|
for (int64_t i = 0; i < servers.count(); ++i) {
|
|
const ObUnitPlacementStrategy::ObServerResource &s = servers.at(i);
|
|
fprintf(fp, "Server: %s\n", S(s.addr_));
|
|
fprintf(fp, "CPU: %f\n", s.assigned_[RES_CPU]/s.capacity_[RES_CPU]);
|
|
fprintf(fp, "MEM: %f\n", s.assigned_[RES_MEM]/s.capacity_[RES_MEM]);
|
|
fprintf(fp, "UNITS: ");
|
|
for (int64_t j = 0; j < units_placement.count(); ++j) {
|
|
if (units_placement.at(j) == i) {
|
|
fprintf(fp, "%ld ", j);
|
|
}
|
|
}
|
|
fprintf(fp, "\n\n");
|
|
}
|
|
|
|
if (NULL != fp) {
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
void TestUnitPlacement::run_case(const char* casename)
|
|
{
|
|
char filename[512];
|
|
snprintf(filename, 512, "%s%s.test", BASE_DIR, casename);
|
|
printf("run case: %s\n", filename);
|
|
ObArenaAllocator allocator(ObModIds::TEST);
|
|
json::Value *root = NULL;
|
|
common::ObArray<ObUnitPlacementStrategy::ObServerResource> servers;
|
|
common::ObArray<share::ObUnitConfig> units;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(ob_parse_case_file(allocator, filename, root));
|
|
ASSERT_NO_FATAL_FAILURE(build_case(root, servers, units));
|
|
|
|
// go
|
|
ObUnitPlacementDPStrategy placement_s;
|
|
common::ObAddr chosen;
|
|
ObArray<int64_t> units_placement;
|
|
for (int64_t i = 0; i < units.count(); ++i) {
|
|
ASSERT_EQ(OB_SUCCESS, placement_s.choose_server(servers, units.at(i), chosen));
|
|
OB_LOG(INFO, "server is choosen", K(i), K(chosen));
|
|
for (int j = 0; j < servers.count(); ++j) {
|
|
ObUnitPlacementStrategy::ObServerResource &s = servers.at(j);
|
|
if (s.addr_ == chosen) {
|
|
s.assigned_[RES_CPU] += units.at(i).min_cpu_;
|
|
s.assigned_[RES_MEM] += static_cast<double>(units.at(i).min_memory_);
|
|
ASSERT_EQ(OB_SUCCESS, units_placement.push_back(j));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// print result
|
|
OB_LOG(INFO, "OUTPUT", K(servers));
|
|
char output_file[512];
|
|
snprintf(output_file, 512, "%s%s.tmp", BASE_DIR, casename);
|
|
ASSERT_NO_FATAL_FAILURE(output_result(output_file, servers, units_placement));
|
|
ASSERT_NO_FATAL_FAILURE(ob_check_result(BASE_DIR, casename));
|
|
}
|
|
|
|
TEST_F(TestUnitPlacement, basic_test)
|
|
{
|
|
ASSERT_NO_FATAL_FAILURE(run_case("shenglian"));
|
|
ASSERT_NO_FATAL_FAILURE(run_case("ffd_avg_sum"));
|
|
ASSERT_NO_FATAL_FAILURE(run_case("split_demands"));
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
oceanbase::common::ObLogger::get_logger().set_log_level("DEBUG");
|
|
::testing::InitGoogleTest(&argc,argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|