/** * 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 #include #include #include #define private public #include "logservice/palf/lsn_allocator.h" #undef private namespace oceanbase { using namespace common; using namespace palf; namespace unittest { class TestLSNAllocator : public ::testing::Test { public: TestLSNAllocator(); virtual ~TestLSNAllocator(); virtual void SetUp(); virtual void TearDown(); protected: int64_t palf_id_; LSNAllocator lsn_allocator_; }; TestLSNAllocator::TestLSNAllocator() : palf_id_(1) { } TestLSNAllocator::~TestLSNAllocator() { } void TestLSNAllocator::SetUp() { } void TestLSNAllocator::TearDown() { PALF_LOG(INFO, "TestLSNAllocator has TearDown"); PALF_LOG(INFO, "TearDown success"); } constexpr int MAX_BUF_SIZE = 2 * 1024 * 1024; const int64_t LOG_LOG_CNT = 1000; int64_t log_size_array[LOG_LOG_CNT]; LSNAllocator golbal_lsn_allocator; void init_size_array() { for (int i = 0; i < LOG_LOG_CNT; i++) { log_size_array[i] = rand() % MAX_BUF_SIZE + 1; } } void init_offset_allocator() { LSN start_lsn(0); EXPECT_EQ(OB_SUCCESS, golbal_lsn_allocator.init(0, 1, start_lsn)); } TEST_F(TestLSNAllocator, test_struct_field_value_upper_bound) { // 测试struct中的int64_t是否会比uint64_t多消耗一位用于存储符号 // 测试结论:使用int类型作为field,当最高位置为1时,直接读取它的value就会变成负数 union TestMeta { uint64_t val64_; struct { uint8_t is_need_cut_ : 1; int64_t id_ : 2; int64_t ts_ : 61; }; }; TestMeta val; val.id_ = 1; // val.id_ = (1 << 2) - 1; // 这行编译会报错,implicit truncation from 'int' to bit-field changes value from 3 to -1 val.ts_ = 100; std::cout << val.id_ << ", sizeof(val):" << sizeof(val) << std::endl; val.id_ = 0; std::cout << "val.id_ is " << val.id_ << std::endl; // 0 val.id_ = 1; printf("val.id_ is 0x%x\n", val.id_); // 0x1 val.id_++; std::cout << "val.id_ is " << val.id_ << std::endl; // -2 std::cout << "val.id_ & 0x11 is " << (val.id_ & 0x3) << std::endl; // 2, correct! printf("val.id_ is 0x%x\n", val.id_); // 0xfffffffe val.id_++; std::cout << "val.id_ is " << val.id_ << std::endl; // -1 std::cout << "val.id_ & 0x11 is " << (val.id_ & 0x3) << std::endl; // 3 printf("val.id_ is 0x%x\n", val.id_); // 0xffffffff val.id_++; std::cout << "val.id_ is " << val.id_ << std::endl; // 0 std::cout << "val.id_ & 0x11 is " << (val.id_ & 0x3) << std::endl; // 0, 加溢出了 printf("val.id_ is 0x%x\n", val.id_); // 0x0 union TestMeta2 { uint64_t val64_; struct { uint8_t is_need_cut_ : 1; uint64_t id_ : 2; uint64_t ts_ : 61; }; }; TestMeta2 val2; val2.id_ = 1; // val2.id_ = (1 << 2) - 1; // 这行编译会报错,implicit truncation from 'int' to bit-field changes value from 3 to -1 std::cout << val2.id_ << ", sizeof(val2):" << sizeof(val2) << std::endl; val2.id_ = 0; std::cout << "val2.id_ is " << val2.id_ << std::endl; // 0 val2.id_ = 1; printf("val2.id_ is 0x%x\n", val2.id_); // 0x1 val2.id_++; std::cout << "val2.id_ is " << val2.id_ << std::endl; // 2 std::cout << "val2.id_ & 0x11 is " << (val2.id_ & 0x3) << std::endl; // 2 printf("val2.id_ is 0x%x\n", val2.id_); // 0x2 val2.id_++; std::cout << "val2.id_ is " << val2.id_ << std::endl; // 3 std::cout << "val2.id_ & 0x11 is " << (val2.id_ & 0x3) << std::endl; // 3 printf("val2.id_ is 0x%x\n", val2.id_); // 0x3 val2.id_++; std::cout << "val2.id_ is " << val2.id_ << std::endl; // 0 std::cout << "val2.id_ & 0x11 is " << (val2.id_ & 0x3) << std::endl; // 0, 加溢出了 printf("val2.id_ is 0x%x\n", val2.id_); // 0x0 } TEST_F(TestLSNAllocator, test_lsn_allocator_init) { LSN start_lsn; int64_t initial_log_id = OB_INVALID_LOG_ID; int64_t initial_log_ts = OB_INVALID_TIMESTAMP; EXPECT_EQ(OB_INVALID_ARGUMENT, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); initial_log_id = 0; EXPECT_EQ(OB_INVALID_ARGUMENT, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); initial_log_ts = 1; EXPECT_EQ(OB_INVALID_ARGUMENT, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); start_lsn.val_ = 0; EXPECT_EQ(OB_SUCCESS, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); EXPECT_EQ(OB_INIT_TWICE, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); } TEST_F(TestLSNAllocator, test_lsn_allocator_alloc_lsn_ts) { LSN start_lsn; int64_t initial_log_id = OB_INVALID_LOG_ID; int64_t initial_log_ts = OB_INVALID_TIMESTAMP; initial_log_id = 0; initial_log_ts = 1; start_lsn.val_ = 0; int64_t base_ts = 1000; int64_t size = 1000000; LSN lsn; int64_t log_id; int64_t log_ts; bool is_new_log = false; bool need_gen_padding_entry = false; int64_t padding_len = 0; EXPECT_EQ(OB_NOT_INIT, lsn_allocator_.alloc_lsn_ts(base_ts, size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); EXPECT_EQ(OB_SUCCESS, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); int64_t invalid_size = 0; EXPECT_EQ(OB_INVALID_ARGUMENT, lsn_allocator_.alloc_lsn_ts(base_ts, invalid_size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); // test alloc_lsn_ts() EXPECT_EQ(OB_SUCCESS, lsn_allocator_.alloc_lsn_ts(base_ts, size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); EXPECT_EQ(initial_log_id + 1, log_id); } TEST_F(TestLSNAllocator, test_lsn_allocator_truncate) { LSN start_lsn; int64_t initial_log_id = OB_INVALID_LOG_ID; int64_t initial_log_ts = OB_INVALID_TIMESTAMP; initial_log_id = 0; initial_log_ts = 1; start_lsn.val_ = 0; LSN tmp_lsn; int64_t tmp_log_id = 9999; int64_t tmp_log_ts = 55555; LSN end_lsn; int64_t end_log_id = OB_INVALID_LOG_ID; int64_t base_ts = 1000; int64_t size = 1000000; LSN lsn; int64_t log_id; int64_t log_ts; bool is_new_log = false; bool need_gen_padding_entry = false; int64_t padding_len = 0; // test truncate() const int64_t truncate_log_id = 1024; EXPECT_EQ(OB_NOT_INIT, lsn_allocator_.truncate(tmp_lsn, truncate_log_id, 10)); EXPECT_EQ(OB_NOT_INIT, lsn_allocator_.inc_update_last_log_info(tmp_lsn, tmp_log_id, tmp_log_ts)); EXPECT_EQ(OB_NOT_INIT, lsn_allocator_.try_freeze_by_time(end_lsn, end_log_id)); EXPECT_EQ(OB_NOT_INIT, lsn_allocator_.get_curr_end_lsn(end_lsn)); EXPECT_EQ(OB_SUCCESS, lsn_allocator_.init(initial_log_id, initial_log_ts, start_lsn)); EXPECT_EQ(OB_INVALID_ARGUMENT, lsn_allocator_.truncate(tmp_lsn, truncate_log_id, 10)); tmp_lsn.val_ = 100; EXPECT_EQ(OB_SUCCESS, lsn_allocator_.truncate(tmp_lsn, truncate_log_id, 10)); EXPECT_EQ(OB_SUCCESS, lsn_allocator_.alloc_lsn_ts(base_ts, size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); EXPECT_EQ(truncate_log_id + 1, log_id); // test truncate() tmp_lsn.reset(); EXPECT_EQ(OB_INVALID_ARGUMENT, lsn_allocator_.inc_update_last_log_info(tmp_lsn, tmp_log_id, tmp_log_ts)); tmp_lsn.val_ = 10; // no need update EXPECT_EQ(OB_SUCCESS, lsn_allocator_.inc_update_last_log_info(tmp_lsn, tmp_log_id, tmp_log_ts)); EXPECT_EQ(OB_SUCCESS, lsn_allocator_.alloc_lsn_ts(base_ts, size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); EXPECT_EQ(truncate_log_id + 1, log_id); // 聚合到上一条日志中 // update success tmp_lsn.val_ = 10000000; EXPECT_EQ(OB_SUCCESS, lsn_allocator_.inc_update_last_log_info(tmp_lsn, tmp_log_id, tmp_log_ts)); size = 2 * 1024 * 1024; EXPECT_EQ(OB_SUCCESS, lsn_allocator_.alloc_lsn_ts(base_ts, size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); EXPECT_EQ(tmp_log_id + 1, log_id); // 由于之前alloc的size比较大,故当前is_need_cut为true,这里会报-4109 EXPECT_EQ(OB_STATE_NOT_MATCH, lsn_allocator_.try_freeze_by_time(end_lsn, end_log_id)); // 生成一条新的小日志,预期is_need_cut会为false size = 10; EXPECT_EQ(OB_SUCCESS, lsn_allocator_.alloc_lsn_ts(base_ts, size, lsn, log_id, log_ts, is_new_log, need_gen_padding_entry, padding_len)); EXPECT_EQ(log_id, lsn_allocator_.get_max_log_id()); EXPECT_EQ(log_ts, lsn_allocator_.get_max_log_ts()); EXPECT_EQ(OB_SUCCESS, lsn_allocator_.get_curr_end_lsn(end_lsn)); LSN old_end_lsn = end_lsn; EXPECT_EQ(OB_SUCCESS, lsn_allocator_.try_freeze_by_time(end_lsn, end_log_id)); EXPECT_EQ(old_end_lsn, end_lsn); EXPECT_EQ(log_id, end_log_id); EXPECT_EQ(OB_SUCCESS, lsn_allocator_.try_freeze(end_lsn, end_log_id)); EXPECT_EQ(old_end_lsn, end_lsn); EXPECT_EQ(log_id, end_log_id); } TEST_F(TestLSNAllocator, test_alloc_offset_single_thread) { init_size_array(); init_offset_allocator(); int64_t avg_cost = 0; int64_t ROUND = 1; for (int64_t j = 0; j < ROUND; j++) { int64_t idx = rand() % LOG_LOG_CNT; const int64_t begin_ts = ObTimeUtility::current_time_ns(); for (int i = 0; i < 1000000; i++) { int64_t base_ts = 1; int64_t size = log_size_array[idx]; LSN ret_offset; int64_t ret_log_id; int64_t ret_log_ts; bool is_new_log = false; bool need_gen_padding_entry = false; int64_t padding_len = 0; EXPECT_EQ(OB_SUCCESS, golbal_lsn_allocator.alloc_lsn_ts(base_ts, size, ret_offset, ret_log_id, ret_log_ts, is_new_log, need_gen_padding_entry, padding_len)); } int64_t cost = ObTimeUtility::current_time_ns() - begin_ts; // PALF_LOG(ERROR, "100w alloc cost time ns", K(cost)); std::cout << "100w alloc cost time ns:" << cost << std::endl; avg_cost += cost; } std::cout << ROUND << " round 100w alloc avg cost time ns:" << avg_cost / ROUND << std::endl; std::cout << "finish test_alloc_offset_single_thread" << std::endl; } // 下面测试多线程alloc_lsn_ts的性能 class TestThread { public: TestThread() {} virtual ~TestThread() { } public: void create_and_run(int64_t th_idx) { log_size_ = log_size_array[th_idx % LOG_LOG_CNT]; th_idx_ = th_idx; if (0 != pthread_create(&thread_, NULL, routine, this)){ PALF_LOG(ERROR, "create thread fail", K(thread_)); } else { PALF_LOG(INFO, "create thread success", K(thread_), K(th_idx_), K(log_size_)); } } void join() { pthread_join(thread_, NULL); } static void* routine(void *arg) { TestThread *test_thread = static_cast(arg); int64_t base_ts = 1; int64_t size = test_thread->log_size_; LSN ret_offset; int64_t ret_log_id; int64_t ret_log_ts; bool is_new_log = false; bool need_gen_padding_entry = false; int64_t padding_len = 0; for (int j = 0; j < 1; j++) { const int64_t begin_ts = ObTimeUtility::current_time_ns(); for (int i = 0; i < 1000; i++) { // size = log_size_array[i % LOG_LOG_CNT]; EXPECT_EQ(OB_SUCCESS, golbal_lsn_allocator.alloc_lsn_ts(base_ts, size, ret_offset, ret_log_id, ret_log_ts, is_new_log, need_gen_padding_entry, padding_len)); } int64_t cost = ObTimeUtility::current_time_ns() - begin_ts; // std::cout << test_thread->th_idx_ << " finish 100w alloc cost time ns:" << cost << std::endl; } return NULL; } public: pthread_t thread_; int64_t th_idx_; int64_t log_size_; }; TEST_F(TestLSNAllocator, test_alloc_offset_multi_thread) { init_size_array(); const int64_t THREAD_CNT = 128; TestThread threads[THREAD_CNT]; for (int tidx = 0; tidx < THREAD_CNT; ++tidx) { threads[tidx].create_and_run(tidx); PALF_LOG(INFO, "create thread", K(tidx)); } std::cout << " finish create all threads, count:" << THREAD_CNT << std::endl; for (int tidx = 0; tidx < THREAD_CNT; ++tidx) { threads[tidx].join(); } }; } // END of unittest } // end of oceanbase int main(int argc, char **argv) { system("rm -rf ./test_lsn_allocator.log*"); OB_LOGGER.set_file_name("test_lsn_allocator.log", true); OB_LOGGER.set_log_level("INFO"); PALF_LOG(INFO, "begin unittest::test_lsn_allocator"); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }