/** * 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 #define protected public #define OK(ass) ASSERT_EQ(OB_SUCCESS, (ass)) #define private public #include "storage/blocksstable/ob_data_file_prepare.h" #include "storage/blocksstable/ob_row_generate.h" #include "observer/ob_server_struct.h" #include "observer/ob_service.h" #include "observer/omt/ob_tenant_node_balancer.h" #include "share/config/ob_server_config.h" #include "share/ob_simple_mem_limit_getter.h" #include "share/rc/ob_tenant_base.h" #include "storage/blocksstable/ob_index_block_builder.h" #include "storage/blocksstable/ob_macro_block_writer.h" #include "storage/blocksstable/ob_sstable_meta.h" #include "storage/blocksstable/ob_storage_cache_suite.h" #include "storage/memtable/ob_memtable_interface.h" #include "storage/ob_i_store.h" #include "storage/compaction/ob_tenant_freeze_info_mgr.h" #include "storage/blocksstable/ob_index_block_macro_iterator.h" #include "mtlenv/mock_tenant_module_env.h" #include "share/scn.h" #include "storage/blocksstable/ob_shared_macro_block_manager.h" namespace oceanbase { using namespace common; using namespace blocksstable; using namespace storage; using namespace share::schema; using namespace std; static ObSimpleMemLimitGetter getter; namespace unittest { class TestIndexTree : public ::testing::Test { public: TestIndexTree(); virtual ~TestIndexTree(); static void SetUpTestCase(); static void TearDownTestCase(); virtual void SetUp(); virtual void TearDown(); protected: void fake_freeze_info(); void prepare_schema(); void prepare_4K_cols_schema(); void prepare_data(); void prepare_index_builder(ObDataStoreDesc &index_desc, ObSSTableIndexBuilder &sstable_builder); void prepare_data_desc(ObDataStoreDesc &data_desc, ObSSTableIndexBuilder *sstable_builder); void prepare_index_desc(ObDataStoreDesc &index_desc); void mock_compaction(const int64_t test_row_num, ObIArray &data_write_ctxs, ObIArray &index_write_ctxs, ObMacroMetasArray *&meta_info_list, ObSSTableMergeRes &ctx, IndexMicroBlockDescList *&roots); static const int64_t TEST_COLUMN_CNT = ObExtendType - 1; static const int64_t TEST_ROWKEY_COLUMN_CNT = 2; static const int64_t TEST_ROW_CNT = 1000; static const int64_t MAX_TEST_COLUMN_CNT = TEST_COLUMN_CNT + 3; static const int64_t SNAPSHOT_VERSION = 2; const uint64_t tenant_id_; ObTenantFreezeInfoMgr *mgr_; share::ObTenantBase tenant_base_; ObTableSchema table_schema_; ObTableSchema index_schema_; ObRowGenerate row_generate_; ObRowGenerate index_row_generate_; ObITable::TableKey table_key_; ObArenaAllocator allocator_; ObSharedMacroBlockMgr *shared_blk_mgr_; }; TestIndexTree::TestIndexTree() : tenant_id_(500), mgr_(nullptr), tenant_base_(500), shared_blk_mgr_(nullptr) { ObAddr self; rpc::frame::ObReqTransport req_transport(NULL, NULL); obrpc::ObSrvRpcProxy rpc_proxy; obrpc::ObCommonRpcProxy rs_rpc_proxy; share::ObRsMgr rs_mgr; int64_t tenant_id = 1; self.set_ip_addr("127.0.0.1", 8086); getter.add_tenant(tenant_id, 2L * 1024L * 1024L * 1024L, 4L * 1024L * 1024L * 1024L); } TestIndexTree::~TestIndexTree() { } void TestIndexTree::SetUpTestCase() { int ret = OB_SUCCESS; STORAGE_LOG(INFO, "SetUpTestCase"); EXPECT_EQ(OB_SUCCESS, MockTenantModuleEnv::get_instance().init()); } void TestIndexTree::TearDownTestCase() { MockTenantModuleEnv::get_instance().destroy(); } void TestIndexTree::SetUp() { int ret = OB_SUCCESS; mgr_ = OB_NEW(ObTenantFreezeInfoMgr, ObModIds::TEST); shared_blk_mgr_ = OB_NEW(ObSharedMacroBlockMgr, ObModIds::TEST); tenant_base_.set(shared_blk_mgr_); tenant_base_.set(mgr_); share::ObTenantEnv::set_tenant(&tenant_base_); ASSERT_EQ(OB_SUCCESS, tenant_base_.init()); ASSERT_EQ(OB_SUCCESS, mgr_->init(500, *GCTX.sql_proxy_)); ASSERT_EQ(OB_SUCCESS, shared_blk_mgr_->init()); fake_freeze_info(); ASSERT_EQ(shared_blk_mgr_, MTL(ObSharedMacroBlockMgr *)); ASSERT_EQ(mgr_, MTL(ObTenantFreezeInfoMgr *)); int tmp_ret = OB_SUCCESS; ObTenantIOConfig io_config = ObTenantIOConfig::default_instance(); if (OB_TMP_FAIL(ObIOManager::get_instance().add_tenant_io_manager(OB_SERVER_TENANT_ID, io_config))) { STORAGE_LOG(WARN, "add tenant io config failed", K(tmp_ret)); } prepare_schema(); row_generate_.reset(); index_row_generate_.reset(); ret = row_generate_.init(table_schema_, &allocator_); ASSERT_EQ(OB_SUCCESS, ret); ret = index_row_generate_.init(index_schema_, &allocator_); ASSERT_EQ(OB_SUCCESS, ret); table_key_.tablet_id_ = table_schema_.get_table_id(); table_key_.table_type_ = ObITable::TableType::MINOR_SSTABLE; table_key_.version_range_.snapshot_version_ = share::OB_MAX_SCN_TS_NS - 1; ASSERT_TRUE(table_key_.is_valid()); } void TestIndexTree::TearDown() { row_generate_.reset(); index_row_generate_.reset(); table_schema_.reset(); tenant_base_.destroy(); // stop threads share::ObTenantEnv::set_tenant(nullptr); } void TestIndexTree::fake_freeze_info() { common::ObArray freeze_info; common::ObArray snapshots; ASSERT_TRUE(nullptr != mgr_); const int64_t snapshot_gc_ts = 500; bool changed = false; ASSERT_EQ(OB_SUCCESS, freeze_info.push_back(ObTenantFreezeInfoMgr::FreezeInfo(1, 1, 0))); ASSERT_EQ(OB_SUCCESS, freeze_info.push_back(ObTenantFreezeInfoMgr::FreezeInfo(100, 1, 0))); ASSERT_EQ(OB_SUCCESS, freeze_info.push_back(ObTenantFreezeInfoMgr::FreezeInfo(200, 1, 0))); ASSERT_EQ(OB_SUCCESS, freeze_info.push_back(ObTenantFreezeInfoMgr::FreezeInfo(400, 1, 0))); ASSERT_EQ(OB_SUCCESS, mgr_->update_info( snapshot_gc_ts, freeze_info, snapshots, INT64_MAX, changed)); } static void convert_to_multi_version_row(const ObDatumRow &org_row, const int64_t schema_rowkey_cnt, const int64_t column_cnt, const int64_t snapshot_version, const ObDmlFlag dml_flag, ObDatumRow &multi_row) { for (int64_t i = 0; i < schema_rowkey_cnt; ++i) { multi_row.storage_datums_[i] = org_row.storage_datums_[i]; } multi_row.storage_datums_[schema_rowkey_cnt].set_int(-snapshot_version); multi_row.storage_datums_[schema_rowkey_cnt + 1].set_int(0); for(int64_t i = schema_rowkey_cnt; i < column_cnt; ++i) { multi_row.storage_datums_[i + 2] = org_row.storage_datums_[i]; } multi_row.count_ = column_cnt + 2; if (ObDmlFlag::DF_DELETE == dml_flag) { multi_row.row_flag_= ObDmlFlag::DF_DELETE; } else { multi_row.row_flag_ = ObDmlFlag::DF_INSERT; } multi_row.mvcc_row_flag_.set_last_multi_version_row(true); } void TestIndexTree::prepare_schema() { ObColumnSchemaV2 column; uint64_t table_id = 3001; int64_t micro_block_size = 16 * 1024; //init table schema table_schema_.reset(); index_schema_.reset(); ASSERT_EQ(OB_SUCCESS, table_schema_.set_table_name("test_index_tree")); ASSERT_EQ(OB_SUCCESS, index_schema_.set_table_name("test_index_tree")); table_schema_.set_tenant_id(1); table_schema_.set_tablegroup_id(1); table_schema_.set_database_id(1); table_schema_.set_table_id(table_id); table_schema_.set_rowkey_column_num(TEST_ROWKEY_COLUMN_CNT); table_schema_.set_max_used_column_id(TEST_COLUMN_CNT); table_schema_.set_block_size(micro_block_size); table_schema_.set_compress_func_name("none"); table_schema_.set_row_store_type(ENCODING_ROW_STORE); index_schema_.set_tenant_id(1); index_schema_.set_tablegroup_id(1); index_schema_.set_database_id(1); index_schema_.set_table_id(table_id); index_schema_.set_rowkey_column_num(TEST_ROWKEY_COLUMN_CNT); index_schema_.set_max_used_column_id(TEST_ROWKEY_COLUMN_CNT + 1); index_schema_.set_block_size(micro_block_size); index_schema_.set_compress_func_name("none"); index_schema_.set_row_store_type(ENCODING_ROW_STORE); //init column ObArray out_cols; const int64_t schema_version = 1; const int64_t rowkey_count = TEST_ROWKEY_COLUMN_CNT; const int64_t column_count = TEST_COLUMN_CNT; char name[OB_MAX_FILE_NAME_LENGTH]; memset(name, 0, sizeof(name)); for(int64_t i = 0; i < TEST_COLUMN_CNT; ++i){ ObObjType obj_type = static_cast(i + 1); column.reset(); column.set_table_id(table_id); column.set_column_id(i + OB_APP_MIN_COLUMN_ID); sprintf(name, "test%020ld", i); ASSERT_EQ(OB_SUCCESS, column.set_column_name(name)); column.set_data_type(obj_type); column.set_collation_type(CS_TYPE_BINARY); column.set_data_length(1); if(obj_type == common::ObInt32Type){ column.set_rowkey_position(1); column.set_order_in_rowkey(ObOrderType::ASC); } else if(obj_type == common::ObVarcharType) { column.set_rowkey_position(2); column.set_collation_type(CS_TYPE_UTF8MB4_GENERAL_CI); column.set_order_in_rowkey(ObOrderType::DESC); } else { column.set_rowkey_position(0); } share::schema::ObColDesc col; col.col_id_ = static_cast(i + OB_APP_MIN_COLUMN_ID); col.col_type_.set_type(obj_type); ASSERT_EQ(OB_SUCCESS, out_cols.push_back(col)); ASSERT_EQ(OB_SUCCESS, table_schema_.add_column(column)); if (column.is_rowkey_column()) { ASSERT_EQ(OB_SUCCESS, index_schema_.add_column(column)); } } column.reset(); column.set_table_id(table_id); column.set_column_id(TEST_COLUMN_CNT +OB_APP_MIN_COLUMN_ID); column.set_data_type(ObVarcharType); column.set_collation_type(CS_TYPE_BINARY); column.set_data_length(1); column.set_rowkey_position(0); ASSERT_EQ(OB_SUCCESS, index_schema_.add_column(column)); } void TestIndexTree::prepare_4K_cols_schema() { ObColumnSchemaV2 column; uint64_t table_id = 3001; int64_t micro_block_size = 16 * 1024; const int64_t test_rowkey_cnt = 1; const int64_t test_col_cnt = 4096; //init table schema table_schema_.reset(); index_schema_.reset(); ASSERT_EQ(OB_SUCCESS, table_schema_.set_table_name("test_index_tree")); ASSERT_EQ(OB_SUCCESS, index_schema_.set_table_name("test_index_tree")); table_schema_.set_tenant_id(1); table_schema_.set_tablegroup_id(1); table_schema_.set_database_id(1); table_schema_.set_table_id(table_id); table_schema_.set_rowkey_column_num(test_rowkey_cnt); table_schema_.set_max_used_column_id(test_col_cnt); table_schema_.set_block_size(micro_block_size); table_schema_.set_compress_func_name("none"); table_schema_.set_row_store_type(FLAT_ROW_STORE); index_schema_.set_tenant_id(1); index_schema_.set_tablegroup_id(1); index_schema_.set_database_id(1); index_schema_.set_table_id(table_id); index_schema_.set_rowkey_column_num(test_rowkey_cnt); index_schema_.set_max_used_column_id(test_col_cnt + 1); index_schema_.set_block_size(micro_block_size); index_schema_.set_compress_func_name("none"); index_schema_.set_row_store_type(FLAT_ROW_STORE); //init column ObArray out_cols; const int64_t schema_version = 1; const int64_t rowkey_count = test_rowkey_cnt; const int64_t column_count = test_col_cnt; char name[OB_MAX_FILE_NAME_LENGTH]; memset(name, 0, sizeof(name)); for(int64_t i = 0; i < test_col_cnt; ++i){ ObObjType obj_type = ObObjType::ObIntType; column.reset(); column.set_table_id(table_id); column.set_column_id(i + OB_APP_MIN_COLUMN_ID); sprintf(name, "test%020ld", i); ASSERT_EQ(OB_SUCCESS, column.set_column_name(name)); column.set_data_type(obj_type); column.set_collation_type(CS_TYPE_BINARY); column.set_data_length(1); if(0 == i){ column.set_rowkey_position(1); column.set_order_in_rowkey(ObOrderType::ASC); } else { column.set_rowkey_position(0); } share::schema::ObColDesc col; col.col_id_ = static_cast(i + OB_APP_MIN_COLUMN_ID); col.col_type_.set_type(obj_type); ASSERT_EQ(OB_SUCCESS, out_cols.push_back(col)); ASSERT_EQ(OB_SUCCESS, table_schema_.add_column(column)); if (column.is_rowkey_column()) { ASSERT_EQ(OB_SUCCESS, index_schema_.add_column(column)); } } column.reset(); column.set_table_id(table_id); column.set_column_id(test_col_cnt +OB_APP_MIN_COLUMN_ID); column.set_data_type(ObVarcharType); column.set_collation_type(CS_TYPE_BINARY); column.set_data_length(1); column.set_rowkey_position(0); ASSERT_EQ(OB_SUCCESS, index_schema_.add_column(column)); } void TestIndexTree::prepare_data() { int ret = OB_SUCCESS; //ObSSTable *data_sstable = NULL; ObDataStoreDesc data_desc; ObMacroBlockWriter writer; ObMacroDataSeq start_seq(0); ObRowGenerate data_row_generate; ObDatumRow multi_row; ASSERT_EQ(OB_SUCCESS, multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ret = data_desc.init(table_schema_, ObLSID(1), ObTabletID(1), MAJOR_MERGE); ASSERT_EQ(OB_SUCCESS, ret); ret = writer.open(data_desc, start_seq); ASSERT_EQ(OB_SUCCESS, ret); ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_COLUMN_CNT)); int64_t free_macro_block_cnt = OB_SERVER_BLOCK_MGR.get_free_macro_block_count(); int64_t row_id = 0; while (free_macro_block_cnt - OB_SERVER_BLOCK_MGR.get_free_macro_block_count() > 0) { ret = row_generate_.get_next_row(row_id, row); ASSERT_EQ(OB_SUCCESS, ret); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ret = writer.append_row(multi_row); ASSERT_EQ(OB_SUCCESS, ret) << "rowkey cnt: " << table_schema_.get_rowkey_column_num() << "column cnt: " << table_schema_.get_column_count(); ++row_id; } int64_t border_row_id = row_id; row_id += 10000; free_macro_block_cnt = OB_SERVER_BLOCK_MGR.get_free_macro_block_count(); while (free_macro_block_cnt - OB_SERVER_BLOCK_MGR.get_free_macro_block_count() > 0) { ret = row_generate_.get_next_row(row_id, row); ASSERT_EQ(OB_SUCCESS, ret); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ret = writer.append_row(multi_row); ASSERT_EQ(OB_SUCCESS, ret) << "rowkey cnt: " << table_schema_.get_rowkey_column_num() << "column cnt: " << table_schema_.get_column_count(); ++row_id; } ret = writer.close(); ASSERT_EQ(OB_SUCCESS, ret); writer.reset(); ret = writer.open(data_desc, start_seq); ASSERT_EQ(OB_SUCCESS, ret); for (int64_t i = border_row_id + 1; i < border_row_id + 10000; ++i) { ret = row_generate_.get_next_row(i, row); ASSERT_EQ(OB_SUCCESS, ret); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ret = writer.append_row(multi_row); ASSERT_EQ(OB_SUCCESS, ret) << "rowkey cnt: " << table_schema_.get_rowkey_column_num() << "column cnt: " << table_schema_.get_column_count(); ASSERT_EQ(OB_SUCCESS, ret); } ret = writer.close(); ASSERT_EQ(OB_SUCCESS, ret); } class TestIndexTreeStress : public share::ObThreadPool { public: TestIndexTreeStress(); virtual ~TestIndexTreeStress() {} int init(uint64_t tenant_id, const int64_t thread_cnt, ObRowGenerate &row_generate, ObDataStoreDesc &data_store_desc, ObMacroDataSeq &start_seq, ObSSTableSecMetaIterator *lob_iter, ObMacroBlockWriter *index_writer = NULL); virtual void run1(); private: uint64_t tenant_id_; int64_t thread_cnt_; int64_t row_count_; ObArenaAllocator allocator_; ObRowGenerate *row_generate_; ObDataStoreDesc *data_store_desc_; ObMacroDataSeq *start_seq_; ObSSTableSecMetaIterator *lob_iter_; ObMacroBlockWriter *index_writer_; ObSSTableIndexBuilder *sstable_builder_; common::SpinRWLock lock_; share::ObTenantBase tenant_base_; }; TestIndexTreeStress::TestIndexTreeStress() : tenant_id_(0), thread_cnt_(0), row_count_(0), allocator_(), row_generate_(NULL), data_store_desc_(NULL), start_seq_(NULL), lob_iter_(NULL), index_writer_(NULL), sstable_builder_(NULL), lock_(), tenant_base_(500) { } int TestIndexTreeStress::init(uint64_t tenant_id, const int64_t thread_cnt, ObRowGenerate &row_generate, ObDataStoreDesc &data_store_desc, ObMacroDataSeq &start_seq, ObSSTableSecMetaIterator *lob_iter, ObMacroBlockWriter *index_writer) { int ret = OB_SUCCESS; if (thread_cnt < 0) { ret = OB_INVALID_ARGUMENT; STORAGE_LOG(WARN, "invalid argument", K(ret), K(thread_cnt)); } else { tenant_id_ = tenant_id; thread_cnt_ = thread_cnt; row_count_ = 0; row_generate_ = &row_generate; data_store_desc_ = &data_store_desc; start_seq_ = &start_seq; lob_iter_ = lob_iter; index_writer_ = index_writer; set_thread_count(static_cast(thread_cnt)); share::ObTenantEnv::set_tenant(&tenant_base_); tenant_base_.init(); } return ret; } void TestIndexTreeStress::run1() { int ret = OB_SUCCESS; ObTenantEnv::set_tenant(&tenant_base_); ObMacroBlockWriter *macro_writer = new ObMacroBlockWriter(); //{ SpinWLockGuard guard(lock_); start_seq_->set_parallel_degree(get_thread_idx() + 2); ret = macro_writer->open(*data_store_desc_, *start_seq_); //} ASSERT_EQ(OB_SUCCESS, ret); static const int64_t MAX_TEST_COLUMN_CNT = TestIndexTree::TEST_COLUMN_CNT + 3; ObDatumRow multi_row; ASSERT_EQ(OB_SUCCESS, multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; int64_t test_row_num = row_count_ > 0 ? row_count_ : TestIndexTree::TEST_ROW_CNT; ObDatumRow row; ObArenaAllocator allocator; ASSERT_EQ(OB_SUCCESS, row.init(allocator, TestIndexTree::TEST_COLUMN_CNT)); for(int64_t i = 0; i < test_row_num; ++i) { ASSERT_EQ(OB_SUCCESS, row_generate_->get_next_row(row)); convert_to_multi_version_row(row, TestIndexTree::TEST_ROWKEY_COLUMN_CNT, TestIndexTree::TEST_COLUMN_CNT, 2, dml, multi_row); ASSERT_EQ(OB_SUCCESS, macro_writer->append_row(multi_row)); } ASSERT_EQ(OB_SUCCESS, macro_writer->close()); delete macro_writer; } void TestIndexTree::prepare_index_desc(ObDataStoreDesc &index_desc) { int ret = OB_SUCCESS; ret = index_desc.init(index_schema_, ObLSID(1), ObTabletID(1), MAJOR_MERGE); index_desc.major_working_cluster_version_ = DATA_VERSION_4_0_0_0; ASSERT_EQ(OB_SUCCESS, ret); } void TestIndexTree::prepare_index_builder(ObDataStoreDesc &index_desc, ObSSTableIndexBuilder &sstable_builder) { int ret = OB_SUCCESS; prepare_index_desc(index_desc); ASSERT_EQ(nullptr, index_desc.sstable_index_builder_); ret = sstable_builder.init(index_desc); ASSERT_EQ(OB_SUCCESS, ret); } void TestIndexTree::prepare_data_desc(ObDataStoreDesc &data_desc, ObSSTableIndexBuilder *sstable_builder) { int ret = OB_SUCCESS; ret = data_desc.init(table_schema_, ObLSID(1), ObTabletID(1), MAJOR_MERGE, 1); data_desc.sstable_index_builder_ = sstable_builder; ASSERT_EQ(OB_SUCCESS, ret); } void TestIndexTree::mock_compaction(const int64_t test_row_num, ObIArray &data_write_ctxs, ObIArray &index_write_ctxs, ObMacroMetasArray *&meta_info_list, ObSSTableMergeRes &res, IndexMicroBlockDescList *&roots) { int ret = OB_SUCCESS; ObDataStoreDesc index_desc; char *buf = static_cast (allocator_.alloc(sizeof(ObSSTableIndexBuilder))); ObSSTableIndexBuilder *sstable_builder = new (buf) ObSSTableIndexBuilder(); prepare_index_builder(index_desc, *sstable_builder); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, sstable_builder); ObDatumRow multi_row; ASSERT_EQ(OB_SUCCESS, multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObMacroDataSeq data_seq(0); ObMacroBlockWriter data_writer; ASSERT_EQ(OB_SUCCESS, data_writer.open(data_desc, data_seq)); ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_COLUMN_CNT)); for(int64_t i = 0; i < test_row_num; ++i){ ASSERT_EQ(OB_SUCCESS, row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ASSERT_EQ(OB_SUCCESS, data_writer.append_row(multi_row)); ASSERT_EQ(OB_SUCCESS, data_writer.build_micro_block()); ASSERT_EQ(OB_SUCCESS, data_writer.try_switch_macro_block()); } ObMacroBlock &fir_blk = data_writer.macro_blocks_[0]; ObMacroBlock &sec_blk = data_writer.macro_blocks_[1]; ASSERT_EQ(fir_blk.original_size_, fir_blk.data_zsize_); ASSERT_EQ(sec_blk.original_size_, sec_blk.data_zsize_); ASSERT_EQ(OB_SUCCESS, data_writer.close()); ASSERT_EQ(OB_SUCCESS, sstable_builder->close(data_desc.row_column_count_, res)); ObSSTableMergeRes tmp_res; ASSERT_EQ(OB_ERR_UNEXPECTED, data_writer.close()); // not re-entrant OK(sstable_builder->close(data_desc.row_column_count_, tmp_res)); // re-entrant ASSERT_EQ(tmp_res.root_desc_.buf_, res.root_desc_.buf_); ASSERT_EQ(tmp_res.data_root_desc_.buf_, res.data_root_desc_.buf_); ASSERT_EQ(tmp_res.data_blocks_cnt_, res.data_blocks_cnt_); OK(data_write_ctxs.push_back(sstable_builder->roots_[0]->data_write_ctx_)); meta_info_list = sstable_builder->roots_[0]->macro_metas_; roots = &(sstable_builder->roots_); for (int64_t i = 0; i < sstable_builder->index_write_ctxs_.count(); ++i) { ASSERT_EQ(OB_SUCCESS, index_write_ctxs.push_back(sstable_builder->index_write_ctxs_.at(i))); } } TEST_F(TestIndexTree, test_macro_id_index_block) { int ret = OB_SUCCESS; ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; ASSERT_EQ(OB_SUCCESS, multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObDataStoreDesc index_desc; index_desc.micro_block_size_limit_ = 1 * 1024L; ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder); int64_t num = 100; ObMacroDataSeq data_seq(num); ObMacroBlockWriter data_writer; ASSERT_EQ(OB_SUCCESS, data_writer.open(data_desc, data_seq)); ObDataIndexBlockBuilder *builder = data_writer.builder_; ASSERT_NE(data_writer.builder_, nullptr); ASSERT_EQ(OB_SUCCESS, row_generate_.get_next_row(0, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ASSERT_EQ(OB_SUCCESS, data_writer.append_row(multi_row)); ASSERT_EQ(OB_SUCCESS, data_writer.build_micro_block()); const MacroBlockId first_macro_id = data_writer.macro_handles_[0].get_macro_id(); ASSERT_TRUE(first_macro_id.is_valid()); ASSERT_EQ(OB_SUCCESS, data_writer.close()); // get macro row ObIndexBlockRowDesc ¯o_row_desc = builder->macro_row_desc_; ASSERT_EQ(macro_row_desc.macro_id_, ObIndexBlockRowHeader::DEFAULT_IDX_ROW_MACRO_ID); ObDataMacroBlockMeta *macro_meta = sstable_builder.roots_[0]->macro_metas_->at(0); ASSERT_EQ(macro_meta->val_.macro_id_, first_macro_id); // read macro block ObMacroBlockReadInfo read_info; ObMacroBlockHandle macro_handle; const int64_t macro_block_size = 2 * 1024 * 1024; read_info.macro_block_id_ = first_macro_id; read_info.io_desc_.set_wait_event(ObWaitEventIds::DB_FILE_DATA_READ); read_info.offset_ = 0; read_info.size_ = macro_block_size; OK(ObBlockManager::read_block(read_info, macro_handle)); ASSERT_NE(macro_handle.get_buffer(), nullptr); ASSERT_EQ(macro_handle.get_data_size(), macro_block_size); const char *macro_block_buf = macro_handle.get_buffer(); int64_t pos = sizeof(ObMacroBlockCommonHeader); ObSSTableMacroBlockHeader macro_header; OK(macro_header.deserialize(macro_block_buf, macro_block_size, pos)); ASSERT_EQ(macro_header.fixed_header_.idx_block_offset_, macro_row_desc.block_offset_); // n-1 level block offset ASSERT_EQ(macro_header.fixed_header_.idx_block_size_, macro_row_desc.block_size_); // n-1 level block size int64_t size = macro_row_desc.block_size_; const char *leaf_block_buf = macro_block_buf + macro_row_desc.block_offset_; int64_t payload_size = 0; const char *payload_buf = nullptr; ObMicroBlockHeader header; pos = 0; OK(header.deserialize(leaf_block_buf, size, pos)); OK(header.check_and_get_record( leaf_block_buf, size, MICRO_BLOCK_HEADER_MAGIC, payload_buf, payload_size)); ObMicroBlockData root_block(payload_buf, payload_size); ASSERT_NE(sstable_builder.micro_reader_, nullptr); ObIMicroBlockReader *micro_reader = sstable_builder.micro_reader_; ObTableReadInfo index_read_info; OK(index_read_info.init(allocator_, 16000, TEST_ROWKEY_COLUMN_CNT, lib::is_oracle_mode(), index_desc.col_desc_array_, true)); OK(micro_reader->init(root_block, index_read_info)); ObDatumRow index_row; OK(index_row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); void *buf = allocator_.alloc(common::OB_ROW_MAX_COLUMNS_COUNT * sizeof(common::ObObj)); int64_t row_count = micro_reader->row_count(); ASSERT_EQ(row_count, 1); for (int64_t it = 0; it != row_count; ++it) { OK(micro_reader->get_row(it, index_row)); ObString val = index_row.storage_datums_[TEST_ROWKEY_COLUMN_CNT + 2].get_string(); ObIndexBlockRowHeader *header = reinterpret_cast(val.ptr()); ASSERT_EQ(header->get_macro_id(), ObIndexBlockRowHeader::DEFAULT_IDX_ROW_MACRO_ID); } } TEST_F(TestIndexTree, test_macro_writer_bug1) { int ret = OB_SUCCESS; ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObDataStoreDesc data_desc; prepare_data_desc(data_desc, nullptr); ObMacroBlockWriter data_writer; OK(data_writer.open(data_desc, 0)); OK(row_generate_.get_next_row(0, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row)); OK(data_writer.build_micro_block()); OK(data_writer.try_switch_macro_block()); // force split // failed to append fake_block, but succeeded to switch block again ObMacroBlockDesc fake_block; ASSERT_EQ(OB_INVALID_ARGUMENT, data_writer.append_macro_block(fake_block)); OK(row_generate_.get_next_row(1, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row)); OK(data_writer.close()); } TEST_F(TestIndexTree, test_micro_block_size) { int ret = OB_SUCCESS; ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; const int64_t micro_block_size = 10; // limit the micro block size table_schema_.set_row_store_type(FLAT_ROW_STORE); index_schema_.set_row_store_type(FLAT_ROW_STORE); ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; prepare_index_desc(index_desc); index_desc.micro_block_size_ = micro_block_size; OK(sstable_builder.init(index_desc)); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder); data_desc.micro_block_size_ = micro_block_size; ObMacroBlockWriter data_writer; OK(data_writer.open(data_desc, 0)); ASSERT_EQ(data_writer.builder_->data_store_desc_->micro_block_size_, micro_block_size); // == ASSERT_GT(data_writer.builder_->index_store_desc_->micro_block_size_, micro_block_size); // > OK(row_generate_.get_next_row(0, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row, 2 << 20L)); OK(data_writer.close()); // mock sstable_builder.close() ObSSTableMergeRes res; ASSERT_EQ(sstable_builder.index_store_desc_.micro_block_size_, micro_block_size); // == OK(sstable_builder.sort_roots()); ASSERT_EQ(sstable_builder.merge_index_tree(res), OB_NOT_SUPPORTED); } TEST_F(TestIndexTree, test_index_macro_writer) { int ret = OB_SUCCESS; ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ObDatumRow multi_row; ASSERT_EQ(OB_SUCCESS, multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObMacroDataSeq data_seq(0); ObDataStoreDesc &desc = sstable_builder.container_store_desc_; ASSERT_EQ(nullptr, desc.sstable_index_builder_); ObMacroBlockWriter container_macro_writer; ret = container_macro_writer.open(desc, data_seq); ASSERT_EQ(OB_SUCCESS, ret); const int64_t test_row_num = 100; ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 1)); for(int64_t i = 0; i < test_row_num; ++i){ ASSERT_EQ(OB_SUCCESS, index_row_generate_.get_next_row(row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), TEST_ROWKEY_COLUMN_CNT + 1, SNAPSHOT_VERSION, dml, multi_row); ASSERT_EQ(OB_SUCCESS, container_macro_writer.append_row(multi_row)); } ASSERT_EQ(OB_SUCCESS, container_macro_writer.close()); } TEST_F(TestIndexTree, test_empty_index_tree) { int ret = OB_SUCCESS; ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ObDataStoreDesc data_desc; ObMacroDataSeq data_seq(0); prepare_data_desc(data_desc, &sstable_builder); ObMacroBlockWriter data_writer; ASSERT_EQ(OB_SUCCESS, data_writer.open(data_desc, data_seq)); // do not insert any data ASSERT_EQ(OB_SUCCESS, data_writer.close()); ObSSTableMergeRes res; ret = sstable_builder.close(data_desc.row_column_count_, res); ASSERT_EQ(OB_SUCCESS, ret); ASSERT_TRUE(res.root_desc_.is_empty()); // test easy index tree ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; { // mock sstable builder release before index_builder and rebuilder ObMacroBlockWriter data_writer; ObIndexBlockRebuilder rebuilder; { ObSSTableIndexBuilder other_sstable_builder; prepare_index_builder(index_desc, other_sstable_builder); ObDataStoreDesc other_data_desc; prepare_data_desc(other_data_desc, &other_sstable_builder); OK(rebuilder.init(other_sstable_builder)); OK(data_writer.open(other_data_desc, data_seq)); for(int64_t i = 0; i < 10; ++i) { ASSERT_EQ(OB_SUCCESS, row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ASSERT_EQ(OB_SUCCESS, data_writer.append_row(multi_row)); } OK(data_writer.close()); } // release other_sstable_builder data_writer.reset(); rebuilder.reset(); } } TEST_F(TestIndexTree, test_accumulative_info) { int ret = OB_SUCCESS; ObMacroDataSeq data_seq(0); ObDataStoreDesc index_desc; prepare_index_desc(index_desc); ObMacroBlockWriter index_macro_writer; ASSERT_EQ(OB_SUCCESS, index_macro_writer.open(index_desc, data_seq)); ObBaseIndexBlockBuilder builder; ret = builder.init(index_desc, allocator_, &index_macro_writer); ASSERT_EQ(OB_SUCCESS, ret); ObIMicroBlockWriter *micro_writer = builder.micro_writer_; const int64_t test_row_num = 100; const int64_t round_row_num = 10; // 10 rows one micro block const int64_t len = 128; ObStorageDatum obj[4]; obj[0].set_int32(0); obj[1].set_string(ObString(1, "1")); obj[2].set_int(-2); obj[3].set_int(0); ObDatumRowkey row_key; row_key.assign(obj, 4); ObIndexBlockRowDesc index_row(index_desc); index_row.row_count_ = 1; index_row.micro_block_count_ = 1; index_row.macro_block_count_ = 1; index_row.row_key_ = row_key; for(int64_t i = 0; i < test_row_num; ++i) { ASSERT_EQ(OB_SUCCESS, builder.append_row(index_row)); if (micro_writer->get_row_count() == round_row_num) { ASSERT_EQ(round_row_num, builder.row_count_); ASSERT_EQ(OB_SUCCESS, builder.append_index_micro_block()); ASSERT_EQ(0, builder.row_count_); } } ObBaseIndexBlockBuilder *next_builder = builder.next_level_builder_; ASSERT_NE(next_builder, nullptr); ASSERT_EQ(test_row_num, next_builder->row_count_); const int64_t row_cnt = test_row_num / round_row_num; ASSERT_EQ(OB_SUCCESS, index_macro_writer.try_switch_macro_block()); const int64_t cur_idx = index_macro_writer.current_index_; ObMacroBlock &cur_macro_block = index_macro_writer.macro_blocks_[cur_idx]; ObSSTableMacroBlockHeader &header = cur_macro_block.macro_header_; ASSERT_EQ(OB_SUCCESS, next_builder->append_index_micro_block()); ASSERT_EQ(1, header.fixed_header_.micro_block_count_); ASSERT_EQ(row_cnt, header.fixed_header_.row_count_); ObBaseIndexBlockBuilder *next_next_builder = next_builder->next_level_builder_; ASSERT_NE(next_next_builder, nullptr); ASSERT_EQ(test_row_num, next_next_builder->row_count_); ASSERT_EQ(test_row_num, next_next_builder->micro_block_count_); ASSERT_EQ(test_row_num, next_next_builder->macro_block_count_); } TEST_F(TestIndexTree, test_multi_writers_with_close) { int ret = OB_SUCCESS; ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; index_desc.micro_block_size_limit_ = 16 * 1024L; prepare_index_builder(index_desc, sstable_builder); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder); ObMacroDataSeq data_seq(0); TestIndexTreeStress stress; int thread_ct = 3; stress.init(tenant_id_, thread_ct, row_generate_, data_desc, data_seq, nullptr); ASSERT_EQ(OB_SUCCESS, ret); stress.start(); stress.wait(); ASSERT_EQ(OB_SUCCESS, ret); // test sstable_builder re-entrant 4005 bug ObSSTableMergeRes res; OK(sstable_builder.index_builder_.init( sstable_builder.index_store_desc_, sstable_builder.self_allocator_, &sstable_builder.macro_writer_, 1)); ASSERT_GT(sstable_builder.self_allocator_.used(), 0); res.reset(); sstable_builder.clean_status(); OK(sstable_builder.sort_roots()); OK(sstable_builder.merge_index_tree(res)); res.reset(); OK(sstable_builder.close(data_desc.row_column_count_, res)); ObIndexTreeRootBlockDesc &root_desc = res.root_desc_; ASSERT_TRUE(root_desc.is_valid()); ASSERT_TRUE(root_desc.is_mem_type()); char *root_buf = root_desc.buf_; int64_t root_size = root_desc.addr_.size_; ObMicroBlockData root_block(root_buf, root_size); ASSERT_NE(sstable_builder.micro_reader_, nullptr); ObIMicroBlockReader *micro_reader = sstable_builder.micro_reader_; ObTableReadInfo read_info; ASSERT_EQ(OB_SUCCESS, read_info.init(allocator_, 16000, TEST_ROWKEY_COLUMN_CNT, lib::is_oracle_mode(), index_desc.col_desc_array_, true)); ret = micro_reader->init(root_block, read_info); ASSERT_EQ(OB_SUCCESS, ret); ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); void *buf = allocator_.alloc(common::OB_ROW_MAX_COLUMNS_COUNT * sizeof(common::ObObj)); int64_t row_count = micro_reader->row_count(); int64_t total_row_cnt = 0; for (int64_t it = 0; it != row_count; ++it) { ret = micro_reader->get_row(it, row); ASSERT_EQ(OB_SUCCESS, ret); ObString val = row.storage_datums_[TEST_ROWKEY_COLUMN_CNT + 2].get_string(); ObIndexBlockRowHeader *header = reinterpret_cast(val.ptr()); total_row_cnt += header->row_count_; } int32_t last_rowkey_int = row.storage_datums_[0].get_int32(); int64_t test_row_num = TestIndexTree::TEST_ROW_CNT; ASSERT_EQ(thread_ct * test_row_num - 1, last_rowkey_int); ASSERT_EQ(total_row_cnt, test_row_num * thread_ct); } //===================== test secondary meta ================ TEST_F(TestIndexTree, test_merge_info_build_row) { int ret = OB_SUCCESS; const int64_t test_row_num = 100; ObArray data_write_ctxs; ObArray index_write_ctxs; ObMacroMetasArray *merge_info_list = nullptr; ObSSTableMergeRes res; IndexMicroBlockDescList *roots = nullptr; mock_compaction(test_row_num, data_write_ctxs, index_write_ctxs, merge_info_list, res, roots); const int64_t rowkey_cnt = TEST_ROWKEY_COLUMN_CNT; blocksstable::ObDatumRow datum_row; ASSERT_EQ(OB_SUCCESS, datum_row.init(allocator_, rowkey_cnt + 3)); for (int64_t i = 0; i < merge_info_list->count(); ++i) { ObDataMacroBlockMeta *info = merge_info_list->at(i); ASSERT_NE(nullptr, info); ObDataBlockMetaVal &info_val = info->val_; ASSERT_TRUE(info->is_valid()); ASSERT_EQ(OB_SUCCESS, info->build_row(datum_row, allocator_)); ASSERT_EQ(i, datum_row.storage_datums_[0].get_int()); ObDataMacroBlockMeta parsed_meta; ASSERT_EQ(OB_SUCCESS, parsed_meta.parse_row(datum_row)); ASSERT_TRUE(parsed_meta.is_valid()); ASSERT_EQ(info_val.column_count_, parsed_meta.val_.column_count_); for (int64_t i = 0; i < info_val.column_count_; ++i) { ASSERT_EQ(info_val.column_checksums_[i], parsed_meta.val_.column_checksums_[i]); } } // test deep_copy of macro_meta with given allocator ObArenaAllocator arena_allocator; ObFIFOAllocator safe_allocator; OK(safe_allocator.init(&arena_allocator, OB_MALLOC_BIG_BLOCK_SIZE, ObMemAttr(OB_SERVER_TENANT_ID, ObNewModIds::TEST))); ObDataMacroBlockMeta *copy_meta = nullptr; ObDataMacroBlockMeta &large_meta = *merge_info_list->at(0); int64_t test_col_cnt = 100; for (int64_t i = 0; i < test_col_cnt; ++i) { OK(large_meta.val_.column_checksums_.push_back(i)); } ASSERT_EQ(safe_allocator.current_using_, nullptr); ASSERT_EQ(safe_allocator.normal_used_, 0); OK(large_meta.deep_copy(copy_meta, safe_allocator)); ASSERT_NE(safe_allocator.current_using_, nullptr); safe_allocator.free(copy_meta); copy_meta->~ObDataMacroBlockMeta(); const int64_t end_key_deep_copy_size = 64; // due to end_key::reset by memset ASSERT_EQ(safe_allocator.normal_used_, end_key_deep_copy_size); } TEST_F(TestIndexTree, test_meta_builder) { int ret = OB_SUCCESS; const int64_t test_row_num = 100; ObArray data_write_ctxs; ObArray index_write_ctxs; ObMacroMetasArray *merge_info_list = nullptr; ObSSTableMergeRes res; IndexMicroBlockDescList *roots = nullptr; mock_compaction(test_row_num, data_write_ctxs, index_write_ctxs, merge_info_list, res, roots); ObDatumRow leaf_row; ASSERT_EQ(OB_SUCCESS, leaf_row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); ObDataStoreDesc index_desc; prepare_index_desc(index_desc); ObMacroDataSeq data_seq(0); ObMacroBlockWriter container_macro_writer; ASSERT_EQ(OB_SUCCESS, container_macro_writer.open(index_desc, data_seq)); ObMetaIndexBlockBuilder meta_builder; ASSERT_EQ(OB_SUCCESS, meta_builder.init(index_desc, allocator_, container_macro_writer)); for (int64_t i = 0; i < merge_info_list->count(); ++i) { ObDataMacroBlockMeta *info = merge_info_list->at(i); ASSERT_EQ(OB_SUCCESS, info->build_row(leaf_row, allocator_)); ASSERT_EQ(OB_SUCCESS, meta_builder.append_leaf_row(leaf_row)); } ObIndexTreeRootBlockDesc root_desc; ASSERT_EQ(OB_SUCCESS, meta_builder.close(*roots, root_desc)); ASSERT_TRUE(root_desc.is_valid()); ASSERT_TRUE(root_desc.is_mem_type()); int64_t root_size = root_desc.addr_.size_; char *root_buf = root_desc.buf_; ObMicroBlockData root_block(root_buf, root_size); ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ASSERT_NE(sstable_builder.micro_reader_, nullptr); ObIMicroBlockReader *micro_reader = sstable_builder.micro_reader_; ObTableReadInfo read_info; ASSERT_EQ(OB_SUCCESS, read_info.init(allocator_, 16000, TEST_ROWKEY_COLUMN_CNT, lib::is_oracle_mode(), index_desc.col_desc_array_, true)); ret = micro_reader->init(root_block, read_info); ASSERT_EQ(OB_SUCCESS, ret); blocksstable::ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); int64_t total_row_cnt = 0; for (int64_t it = 0; it != micro_reader->row_count(); ++it) { ASSERT_EQ(OB_SUCCESS, micro_reader->get_row(it, row)); ObString val = row.storage_datums_[TEST_ROWKEY_COLUMN_CNT + 2].get_string(); ObIndexBlockRowHeader *header = reinterpret_cast(val.ptr()); total_row_cnt += header->row_count_; } ASSERT_EQ(total_row_cnt, test_row_num); } TEST_F(TestIndexTree, test_meta_builder_data_root) { int ret = OB_SUCCESS; const int64_t test_row_num = 2; ObArray data_write_ctxs; ObArray index_write_ctxs; ObMacroMetasArray *merge_info_list = nullptr; ObSSTableMergeRes res; IndexMicroBlockDescList *roots = nullptr; mock_compaction(test_row_num, data_write_ctxs, index_write_ctxs, merge_info_list, res, roots); ObDatumRow leaf_row; ASSERT_EQ(OB_SUCCESS, leaf_row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); ObDataStoreDesc index_desc; prepare_index_desc(index_desc); ObMacroDataSeq data_seq(0); ObMacroBlockWriter container_macro_writer; ASSERT_EQ(OB_SUCCESS, container_macro_writer.open(index_desc, data_seq)); ObMetaIndexBlockBuilder meta_builder; ASSERT_EQ(OB_SUCCESS, meta_builder.init(index_desc, allocator_, container_macro_writer)); for (int64_t i = 0; i < merge_info_list->count(); ++i) { ObDataMacroBlockMeta *info = merge_info_list->at(i); ASSERT_EQ(OB_SUCCESS, info->build_row(leaf_row, allocator_)); ASSERT_EQ(OB_SUCCESS, meta_builder.append_leaf_row(leaf_row)); } ObIndexTreeRootBlockDesc root_desc; ASSERT_EQ(OB_SUCCESS, meta_builder.close(*roots, root_desc)); ASSERT_TRUE(root_desc.is_valid()); ASSERT_TRUE(root_desc.is_mem_type()); ASSERT_TRUE(root_desc.is_meta_root_); int64_t root_size = root_desc.addr_.size_; char *root_buf = root_desc.buf_; ObMicroBlockData root_block(root_buf, root_size); ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ASSERT_NE(sstable_builder.micro_reader_, nullptr); ObIMicroBlockReader *micro_reader = sstable_builder.micro_reader_; ObTableReadInfo read_info; ASSERT_EQ(OB_SUCCESS, read_info.init(allocator_, 16000, TEST_ROWKEY_COLUMN_CNT, lib::is_oracle_mode(), index_desc.col_desc_array_, true)); ret = micro_reader->init(root_block, read_info); ASSERT_EQ(OB_SUCCESS, ret); blocksstable::ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); int64_t total_row_cnt = 0; ASSERT_EQ(micro_reader->row_count(), test_row_num); } TEST_F(TestIndexTree, test_single_row_desc) { // we need use 4K columns to test ObMetaIndexBlockBuilder::build_single_macro_row_desc prepare_4K_cols_schema(); ObDataStoreDesc index_desc; prepare_index_desc(index_desc); ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder); ObMacroDataSeq data_seq(0); ObMacroBlockWriter data_writer; OK(data_writer.open(data_desc, data_seq)); ObDatumRow row; const int64_t test_col_cnt = 4096; OK(row.init(allocator_, test_col_cnt)); OK(row_generate_.get_next_row(0, row)); ObDatumRow multi_row; OK(multi_row.init(allocator_, test_col_cnt + 10)); ObDmlFlag dml = DF_INSERT; convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row)); OK(data_writer.close()); ObSSTableMergeRes res; sstable_builder.optimization_mode_ = ObSSTableIndexBuilder::ObSpaceOptimizationMode::DISABLE; OK(sstable_builder.close(data_desc.row_column_count_, res)); // test rebuild sstable ObSSTableIndexBuilder sstable_builder2; prepare_index_builder(index_desc, sstable_builder2); ObIndexBlockRebuilder rebuilder; OK(rebuilder.init(sstable_builder2)); ObMacroBlockHandle macro_handle; macro_handle.reset(); ObMacroBlockReadInfo info; const int64_t macro_block_size = 2 * 1024 * 1024; info.io_desc_.set_wait_event(ObWaitEventIds::DB_FILE_DATA_READ); info.offset_ = 0; info.size_ = macro_block_size; info.macro_block_id_ = res.data_block_ids_[0]; OK(ObBlockManager::read_block(info, macro_handle)); OK(rebuilder.append_macro_row(macro_handle.get_buffer(), macro_handle.get_data_size(), info.macro_block_id_)); OK(rebuilder.close()); ObSSTableMergeRes res2; OK(sstable_builder2.close(res.data_column_cnt_, res2)); // test rebuild sstable by another append_macro_row ObSSTableIndexBuilder sstable_builder3; prepare_index_builder(index_desc, sstable_builder3); sstable_builder3.index_store_desc_.major_working_cluster_version_ = DATA_VERSION_4_1_0_0; sstable_builder3.container_store_desc_.major_working_cluster_version_ = DATA_VERSION_4_1_0_0; ObIndexBlockRebuilder other_rebuilder; OK(other_rebuilder.init(sstable_builder3)); ObDataMacroBlockMeta *macro_meta = nullptr; OK(sstable_builder.roots_[0]->macro_metas_->at(0)->deep_copy(macro_meta, allocator_)); OK(other_rebuilder.append_macro_row(*macro_meta)); OK(other_rebuilder.close()); ObSSTableMergeRes res3; sstable_builder3.optimization_mode_ = ObSSTableIndexBuilder::ObSpaceOptimizationMode::AUTO; OK(sstable_builder3.close(res.data_column_cnt_, res3)); } TEST_F(TestIndexTree, test_data_block_checksum) { int ret = OB_SUCCESS; const int64_t test_row_num = 100; ObArray data_write_ctxs; ObArray index_write_ctxs; ObMacroMetasArray *merge_info_list = nullptr; ObSSTableMergeRes res; IndexMicroBlockDescList *roots = nullptr; mock_compaction(test_row_num, data_write_ctxs, index_write_ctxs, merge_info_list, res, roots); ASSERT_EQ(test_row_num, merge_info_list->count()); int64_t max_reported_column_id = TEST_COLUMN_CNT + 2; ObArray column_metas; ObArray column_default_checksum; for (int64_t i = 0; i < TEST_COLUMN_CNT + 2; ++i) { ObSSTableColumnMeta column_meta; column_meta.column_id_ = i; ASSERT_EQ(OB_SUCCESS, column_metas.push_back(column_meta)); ASSERT_EQ(OB_SUCCESS, column_default_checksum.push_back(column_meta.column_default_checksum_)); } for (int64_t i = TEST_COLUMN_CNT + 2; i < max_reported_column_id; ++i) { ObSSTableColumnMeta column_meta; column_meta.column_id_ = i; column_meta.column_default_checksum_ = i - TEST_COLUMN_CNT; ASSERT_EQ(OB_SUCCESS, column_metas.push_back(column_meta)); ASSERT_EQ(OB_SUCCESS, column_default_checksum.push_back(column_meta.column_default_checksum_)); } ObArray column_checksums; ASSERT_EQ(OB_SUCCESS, res.fill_column_checksum(column_default_checksum, column_checksums)); int64_t expected_column_checksum[max_reported_column_id]; for (int64_t i = 0; i < TEST_COLUMN_CNT + 2; ++i) { expected_column_checksum[i] = 0; for (int64_t j = 0; j < test_row_num; ++j) { ObDataMacroBlockMeta &meta= *(merge_info_list->at(j)); STORAGE_LOG(DEBUG, "data macro meta after copy: ", K(ret), K(meta)); expected_column_checksum[i] += meta.val_.column_checksums_[i]; } } for (int64_t i = TEST_COLUMN_CNT + 2; i < max_reported_column_id; ++i) { expected_column_checksum[i] = 0; expected_column_checksum[i] = test_row_num * (i - TEST_COLUMN_CNT); } for (int64_t i = 0; i < max_reported_column_id; ++i) { ASSERT_EQ(expected_column_checksum[i], column_checksums.at(i)); } } TEST_F(TestIndexTree, test_reuse_macro_block) { int ret = OB_SUCCESS; const int64_t test_row_num = 100; ObArray data_write_ctxs; ObArray index_write_ctxs; ObMacroMetasArray *macro_metas = nullptr; ObSSTableMergeRes res; IndexMicroBlockDescList *roots = nullptr; mock_compaction(test_row_num, data_write_ctxs, index_write_ctxs, macro_metas, res, roots); ASSERT_EQ(test_row_num, macro_metas->count()); ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder); ObMacroDataSeq data_seq(0); ObMacroBlockWriter data_writer; OK(data_writer.open(data_desc, data_seq)); for (int64_t i = 0; i < test_row_num; ++i) { ObMacroBlockDesc macro_desc; macro_desc.macro_meta_ = macro_metas->at(i); OK(data_writer.append_macro_block(macro_desc)); } OK(data_writer.close()); ObSSTableMergeRes reused_res; OK(sstable_builder.close(data_desc.row_column_count_, reused_res)); ASSERT_EQ(res.data_blocks_cnt_, reused_res.data_blocks_cnt_); ASSERT_EQ(res.row_count_, reused_res.row_count_); for (int64_t i = 0; i < res.data_blocks_cnt_; ++i) { ASSERT_EQ(res.data_block_ids_.at(i), reused_res.data_block_ids_.at(i)); } } TEST_F(TestIndexTree, DISABLED_test_writer_try_to_append_row) { // fix try_to_append_row, enable this case int ret = OB_SUCCESS; ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObDataStoreDesc data_desc; ObMacroBlockWriter data_writer; ObMicroBlockDesc micro_block_desc; prepare_data_desc(data_desc, nullptr); data_desc.row_store_type_ = ObRowStoreType::ENCODING_ROW_STORE; OK(data_writer.open(data_desc, 0)); ObIMicroBlockWriter *micro_writer = data_writer.micro_writer_; const int64_t test_num = 1; // only add one row to avoid encoding optimization for (int64_t i = 0; i < test_num; ++i) { OK(row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(micro_writer->append_row(multi_row)); } OK(micro_writer->build_micro_block_desc(micro_block_desc)); const int64_t need_store_size = micro_block_desc.buf_size_ + micro_block_desc.header_->header_size_; { // set size upper bound after reuse micro_writer->reuse(); micro_writer->set_block_size_upper_bound(need_store_size - 1); // if append succeeded, set_block_size_upper_bound has not limited the block size ASSERT_EQ(-4024, micro_writer->append_row(multi_row)); } { // set size upper bound after reset OK(data_writer.open(data_desc, 0)); micro_writer = data_writer.micro_writer_; micro_writer->set_block_size_upper_bound(need_store_size - 1); // if append succeeded, set_block_size_upper_bound has not limited the block size ASSERT_EQ(-4024, micro_writer->append_row(multi_row)); } } TEST_F(TestIndexTree, test_writer_try_to_append_row) { // If fail to pass this test, please check ObIMicroBlockWriter::try_to_append_row int ret = OB_SUCCESS; ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObDataStoreDesc data_desc; ObMacroBlockWriter data_writer; ObMicroBlockDesc micro_block_desc; prepare_data_desc(data_desc, nullptr); for (int64_t j = 0; j < ObRowStoreType::MAX_ROW_STORE; ++j) { data_desc.row_store_type_ = static_cast(j); OK(data_writer.open(data_desc, 0)); ObIMicroBlockWriter *micro_writer = data_writer.micro_writer_; const int64_t test_num = 10; for (int64_t i = 0; i < test_num; ++i) { OK(row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(micro_writer->append_row(multi_row)); } OK(micro_writer->build_micro_block_desc(micro_block_desc)); int64_t estimate_size = 0; if (j == 0) { estimate_size = micro_block_desc.original_size_ + micro_block_desc.header_->header_size_; } else { estimate_size = micro_block_desc.original_size_ + static_cast(micro_writer)->header_size_; } { // set size upper bound after reuse micro_writer->reuse(); micro_writer->set_block_size_upper_bound(estimate_size); for (int64_t i = 0; i < test_num; ++i) { OK(row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(micro_writer->append_row(multi_row)); } } { // set size upper bound after reset OK(data_writer.open(data_desc, 0)); micro_writer = data_writer.micro_writer_; micro_writer->set_block_size_upper_bound(estimate_size); for (int64_t i = 0; i < test_num; ++i) { OK(row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(micro_writer->append_row(multi_row)); } } } } TEST_F(TestIndexTree, test_rebuilder) { int ret = OB_SUCCESS; ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObDataStoreDesc index_desc1; ObDataStoreDesc index_desc2; ObSSTableIndexBuilder sstable_builder1; ObSSTableIndexBuilder sstable_builder2; prepare_index_builder(index_desc1, sstable_builder1); prepare_index_builder(index_desc2, sstable_builder2); // write data ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder1); int64_t test_num = 10; ObMacroDataSeq data_seq(0); ObMacroBlockWriter data_writer; OK(data_writer.open(data_desc, data_seq)); for (int64_t i = 0; i < test_num; ++i) { OK(row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row)); OK(data_writer.build_micro_block()); OK(data_writer.try_switch_macro_block()); } OK(data_writer.close()); ObSSTableMergeRes res1; OK(sstable_builder1.close(data_desc.row_column_count_, res1)); ObIndexBlockRebuilder rebuilder; OK(rebuilder.init(sstable_builder2)); // read data blocks ObMacroBlockReadInfo info; ObMacroBlockHandle macro_handle; const int64_t macro_block_size = 2 * 1024 * 1024; info.io_desc_.set_wait_event(ObWaitEventIds::DB_FILE_DATA_READ); info.offset_ = 0; info.size_ = macro_block_size; ObIArray &data_block_ids = res1.data_block_ids_; ObDataMacroBlockMeta *macro_meta = nullptr; ObArenaAllocator meta_allocator; ASSERT_EQ(data_block_ids.count(), test_num); // rebuild index tree for (int64_t i = data_block_ids.count() - 1; OB_SUCC(ret) && i >= 0; --i) { // i starts from count()-1, to mock disordered data MacroBlockId &cur_id = data_block_ids.at(i); info.macro_block_id_ = cur_id; macro_handle.reset(); OK(ObBlockManager::read_block(info, macro_handle)); ASSERT_NE(macro_handle.get_buffer(), nullptr); ASSERT_EQ(macro_handle.get_data_size(), macro_block_size); OK(rebuilder.get_macro_meta(macro_handle.get_buffer(), macro_block_size, cur_id, meta_allocator, macro_meta)); OK(rebuilder.append_macro_row(*macro_meta)); } OK(rebuilder.close()); ObSSTableMergeRes res2; OK(sstable_builder2.close(data_desc.row_column_count_, res2)); // compare merge res ASSERT_EQ(res1.root_desc_.height_, res2.root_desc_.height_); ASSERT_EQ(res1.root_desc_.height_, 2); ObMicroBlockData root_block1(res1.root_desc_.buf_, res1.root_desc_.addr_.size_); ObMicroBlockData root_block2(res2.root_desc_.buf_, res2.root_desc_.addr_.size_); ObIMicroBlockReader *micro_reader1 = sstable_builder1.micro_reader_; ObIMicroBlockReader *micro_reader2 = sstable_builder2.micro_reader_; ObTableReadInfo read_info; OK(read_info.init(allocator_, 16000, TEST_ROWKEY_COLUMN_CNT, lib::is_oracle_mode(), index_desc1.col_desc_array_, true)); OK(micro_reader1->init(root_block1, read_info)); OK(micro_reader2->init(root_block2, read_info)); blocksstable::ObDatumRow row1; blocksstable::ObDatumRow row2; OK(row1.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); OK(row2.init(allocator_, TEST_ROWKEY_COLUMN_CNT + 3)); ASSERT_EQ(micro_reader1->row_count(), micro_reader2->row_count()); ASSERT_EQ(micro_reader1->row_count(), test_num); for (int64_t it = 0; it != micro_reader1->row_count(); ++it) { OK(micro_reader1->get_row(it, row1)); OK(micro_reader2->get_row(it, row2)); ObString val1 = row1.storage_datums_[TEST_ROWKEY_COLUMN_CNT + 2].get_string(); ObString val2 = row2.storage_datums_[TEST_ROWKEY_COLUMN_CNT + 2].get_string(); ASSERT_EQ(val1, val2); } } TEST_F(TestIndexTree, test_diagnose_dump) { int ret = OB_SUCCESS; ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; prepare_index_builder(index_desc, sstable_builder); ObDataStoreDesc data_desc; prepare_data_desc(data_desc, &sstable_builder); ObDatumRow multi_row; ASSERT_EQ(OB_SUCCESS, multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; ObMacroDataSeq data_seq(0); ObMacroBlockWriter data_writer; ASSERT_EQ(OB_SUCCESS, data_writer.open(data_desc, data_seq)); ObDatumRow row; ASSERT_EQ(OB_SUCCESS, row.init(allocator_, TEST_COLUMN_CNT)); for(int64_t i = 0; i < 10; ++i){ ASSERT_EQ(OB_SUCCESS, row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); ASSERT_EQ(OB_SUCCESS, data_writer.append_row(multi_row)); } OK(data_writer.build_micro_block()); OK(row_generate_.get_next_row(10, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row)); data_writer.dump_block_and_writer_buffer(); data_writer.micro_writer_->dump_diagnose_info(); } TEST_F(TestIndexTree, test_estimate_meta_block_size) { int ret = OB_SUCCESS; ObDataStoreDesc index_desc; ObSSTableIndexBuilder sstable_builder; ObDataStoreDesc data_desc; OK(index_desc.init(index_schema_, ObLSID(1), ObTabletID(1), MINI_MERGE)); OK(sstable_builder.init(index_desc)); OK(data_desc.init(table_schema_, ObLSID(1), ObTabletID(1), MINI_MERGE, 1)); data_desc.sstable_index_builder_ = &sstable_builder; ObMacroDataSeq data_seq(0); ObMacroBlockWriter data_writer; OK(data_writer.open(data_desc, data_seq)); ObDatumRow multi_row; OK(multi_row.init(allocator_, MAX_TEST_COLUMN_CNT)); ObDatumRow row; OK(row.init(allocator_, TEST_COLUMN_CNT)); ObDmlFlag dml = DF_INSERT; for(int64_t i = 0; i < 10; ++i) { OK(row_generate_.get_next_row(i, row)); convert_to_multi_version_row(row, table_schema_.get_rowkey_column_num(), table_schema_.get_column_count(), SNAPSHOT_VERSION, dml, multi_row); OK(data_writer.append_row(multi_row)); } const ObDatumRowkey& last_data_key = data_writer.last_key_; int64_t estimate_meta_block_size = 0; OK(data_writer.builder_->cal_macro_meta_block_size(last_data_key, estimate_meta_block_size)); ObMacroBlock ¯o_block = data_writer.macro_blocks_[0]; ObMacroBlockHandle ¯o_handle = data_writer.macro_handles_[0]; OK(data_writer.build_micro_block()); OK(data_writer.builder_->generate_macro_row(macro_block, macro_handle.get_macro_id())); const ObSSTableMacroBlockHeader ¯o_header_ = macro_block.macro_header_; ASSERT_EQ(macro_header_.fixed_header_.idx_block_offset_ + macro_header_.fixed_header_.idx_block_size_, macro_header_.fixed_header_.meta_block_offset_); ASSERT_GT(macro_header_.fixed_header_.meta_block_size_, 0); ASSERT_GE(estimate_meta_block_size, macro_header_.fixed_header_.meta_block_size_); ObDataMacroBlockMeta *macro_meta = data_writer.builder_->macro_meta_list_->at(0); const int64_t val_max_size = macro_meta->val_.get_max_serialize_size(); macro_meta->val_.macro_id_ = ObIndexBlockRowHeader::DEFAULT_IDX_ROW_MACRO_ID; ASSERT_GE(macro_meta->val_.get_serialize_size() + estimate_meta_block_size - val_max_size, macro_header_.fixed_header_.meta_block_size_); } }//end namespace unittest }//end namespace oceanbase int main(int argc, char **argv) { system("rm -f test_index_tree.log*"); oceanbase::common::ObLogger::get_logger().set_log_level("INFO"); OB_LOGGER.set_file_name("test_index_tree.log"); srand(time(NULL)); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }