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();
 | |
| }
 | 
