init push
This commit is contained in:
470
unittest/storage/memtable/mvcc/test_keybtree.cpp
Normal file
470
unittest/storage/memtable/mvcc/test_keybtree.cpp
Normal file
@ -0,0 +1,470 @@
|
||||
/**
|
||||
* 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 "storage/memtable/mvcc/ob_keybtree.h"
|
||||
|
||||
#include "common/object/ob_object.h"
|
||||
#include "common/rowkey/ob_store_rowkey.h"
|
||||
#include "lib/allocator/ob_malloc.h"
|
||||
#include "lib/random/ob_random.h"
|
||||
#include "storage/memtable/ob_memtable_key.h"
|
||||
#include "storage/memtable/mvcc/ob_mvcc_row.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <thread>
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::keybtree;
|
||||
using namespace oceanbase::memtable;
|
||||
|
||||
//#define IS_EQ(x, y) ASSERT_EQ(x, y)
|
||||
//#define IS_EQ(x, y) EXPECT_EQ(x, y)
|
||||
#define DUMP_BTREE \
|
||||
{ \
|
||||
FILE* file = fopen("dump_btree.txt", "w+"); \
|
||||
btree.dump(file); \
|
||||
fclose(file); \
|
||||
}
|
||||
|
||||
#define IS_EQ(x, y) \
|
||||
if ((x) != (y)) { \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
#define judge(key, val) \
|
||||
{ \
|
||||
if ((int64_t)(val) >> 3 != get_v(key)) { \
|
||||
abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define MAX_CPU_NUM 8
|
||||
#define CPU_NUM 8
|
||||
|
||||
cpu_set_t get_cpu_set(pthread_t thread)
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
int start_id = ObRandom::rand(0, MAX_CPU_NUM - 1);
|
||||
int cpu_id = start_id + 1;
|
||||
int count = 0;
|
||||
while (count < CPU_NUM && cpu_id != start_id) {
|
||||
CPU_SET(cpu_id, &cpuset);
|
||||
if (0 == pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset)) {
|
||||
++count;
|
||||
} else {
|
||||
CPU_CLR(cpu_id, &cpuset);
|
||||
}
|
||||
cpu_id = (cpu_id + 1) % MAX_CPU_NUM;
|
||||
}
|
||||
return cpuset;
|
||||
}
|
||||
|
||||
int BIND_CPU(pthread_t thread)
|
||||
{
|
||||
static cpu_set_t cpuset = get_cpu_set(thread);
|
||||
return pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
|
||||
}
|
||||
|
||||
const char* attr = ObModIds::TEST;
|
||||
|
||||
void init_key(BtreeKey* ptr, int64_t key)
|
||||
{
|
||||
ptr->get_rowkey()->get_rowkey().get_obj_ptr()[0].set_int(key);
|
||||
}
|
||||
|
||||
int alloc_key(BtreeKey*& ret_key, int64_t key)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObObj* obj_ptr = nullptr;
|
||||
ObStoreRowkey* storerowkey = nullptr;
|
||||
if (OB_ISNULL(obj_ptr = (ObObj*)ob_malloc(sizeof(ObObj), attr)) || OB_ISNULL(new (obj_ptr) ObObj(key))) {
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
} else if (OB_ISNULL(storerowkey = (ObStoreRowkey*)ob_malloc(sizeof(ObStoreRowkey), attr)) ||
|
||||
OB_ISNULL(new (storerowkey) ObStoreRowkey(obj_ptr, 1))) {
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
} else if (OB_ISNULL(ret_key = (BtreeKey*)ob_malloc(sizeof(BtreeKey), attr)) ||
|
||||
OB_ISNULL(new (ret_key) BtreeKey(storerowkey))) {
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class FakeAllocator : public ObIAllocator {
|
||||
public:
|
||||
void* alloc(int64_t size)
|
||||
{
|
||||
return ob_malloc(size, attr);
|
||||
}
|
||||
void free(void* ptr)
|
||||
{
|
||||
ob_free(ptr);
|
||||
}
|
||||
static FakeAllocator* get_instance()
|
||||
{
|
||||
static FakeAllocator allocator;
|
||||
return &allocator;
|
||||
}
|
||||
};
|
||||
|
||||
int64_t get_v(BtreeKey* ptr)
|
||||
{
|
||||
int64_t tmp = 0;
|
||||
IS_EQ(OB_SUCCESS, ptr->get_rowkey()->get_rowkey().get_obj_ptr()[0].get_int(tmp));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
typedef ObKeyBtree Btree;
|
||||
|
||||
constexpr int64_t THREAD_COUNT = (1 << 6);
|
||||
|
||||
constexpr int64_t ORDER_INSERT_THREAD_COUNT = THREAD_COUNT;
|
||||
constexpr int64_t RANDOM_INSERT_THREAD_COUNT = THREAD_COUNT >> 3;
|
||||
constexpr int64_t INSERT_COUNT_PER_THREAD = (1 << 20);
|
||||
|
||||
constexpr int64_t DELETE_THREAD_COUNT = 2; // THREAD_COUNT;
|
||||
|
||||
constexpr int64_t REINSERT_THREAD_COUNT = 2; // THREAD_COUNT;
|
||||
|
||||
constexpr int64_t SCAN_THREAD_COUNT = THREAD_COUNT;
|
||||
|
||||
constexpr int64_t MAX_INSERT_NUM = ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD * 4;
|
||||
|
||||
TEST(TestKeyBtree, smoke_test)
|
||||
{
|
||||
constexpr int64_t THREAD_COUNT = (1 << 2);
|
||||
|
||||
constexpr int64_t INSERT_THREAD_COUNT = THREAD_COUNT;
|
||||
constexpr int64_t INSERT_COUNT_PER_THREAD = (1 << 16);
|
||||
|
||||
constexpr int64_t SCAN_THREAD_COUNT = THREAD_COUNT;
|
||||
|
||||
constexpr int64_t DELETE_THREAD_COUNT = THREAD_COUNT;
|
||||
constexpr int64_t DELETE_RANGE_COUNT_PER_THREAD = (1 << 4);
|
||||
constexpr int64_t DELETE_RANGE_SIZE = (1 << 10);
|
||||
|
||||
lib::set_memory_limit(200 * 1024 * 1024 * 1024L);
|
||||
|
||||
IS_EQ(INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD >=
|
||||
DELETE_THREAD_COUNT * DELETE_RANGE_COUNT_PER_THREAD * DELETE_RANGE_SIZE,
|
||||
true);
|
||||
BtreeNodeAllocator allocator(*FakeAllocator::get_instance());
|
||||
Btree btree(allocator);
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
IS_EQ(OB_SUCCESS, btree.init());
|
||||
|
||||
// naughty thread
|
||||
std::thread normal_threads[2];
|
||||
CACHE_ALIGNED bool should_stop = false;
|
||||
for (int64_t i = 0; i < 2; ++i) {
|
||||
normal_threads[i] = std::thread([&]() {
|
||||
BtreeNodeAllocator allocator(*FakeAllocator::get_instance());
|
||||
Btree btree(allocator);
|
||||
IS_EQ(OB_SUCCESS, btree.init());
|
||||
BtreeKey* key = nullptr;
|
||||
for (int64_t j = 0; !ATOMIC_LOAD(&should_stop); ++j) {
|
||||
auto v = (BtreeVal)(j << 3);
|
||||
IS_EQ(OB_SUCCESS, alloc_key(key, j));
|
||||
IS_EQ(OB_SUCCESS, btree.insert(*key, v));
|
||||
}
|
||||
btree.destroy();
|
||||
});
|
||||
}
|
||||
// keep inserting at left bound
|
||||
std::thread head_insert_thread[2];
|
||||
CACHE_ALIGNED int64_t head_num = -1;
|
||||
for (int64_t i = 0; i < 2; ++i) {
|
||||
head_insert_thread[i] = std::thread([&]() {
|
||||
BtreeKey* key = nullptr;
|
||||
while (!ATOMIC_LOAD(&should_stop)) {
|
||||
int64_t j = ATOMIC_FAA(&head_num, -1);
|
||||
auto v = (BtreeVal)(j << 3);
|
||||
IS_EQ(OB_SUCCESS, alloc_key(key, j));
|
||||
IS_EQ(OB_SUCCESS, btree.insert(*key, v));
|
||||
}
|
||||
});
|
||||
}
|
||||
// scan with terrible range, there should be nothing returned.
|
||||
std::thread bad_scan_threads[2];
|
||||
for (int64_t i = 0; i < 2; ++i) {
|
||||
bad_scan_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
BtreeKey* start_key = nullptr;
|
||||
BtreeKey* end_key = nullptr;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(start_key, 0));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(end_key, 0));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
while (!ATOMIC_LOAD(&should_stop)) {
|
||||
TScanHandle iter;
|
||||
init_key(start_key, MAX_INSERT_NUM);
|
||||
init_key(end_key, INT64_MAX);
|
||||
ret = btree.set_key_range(iter, *start_key, false, *end_key, false, 2);
|
||||
IS_EQ(OB_SUCCESS, ret);
|
||||
ret = iter.get_next(*tmp_key, tmp_value);
|
||||
IS_EQ(OB_ITER_END, ret);
|
||||
iter.reset();
|
||||
init_key(start_key, INT64_MIN);
|
||||
init_key(end_key, INT64_MIN + 1);
|
||||
ret = btree.set_key_range(iter, *start_key, false, *end_key, false, 2);
|
||||
IS_EQ(OB_SUCCESS, ret);
|
||||
ret = iter.get_next(*tmp_key, tmp_value);
|
||||
IS_EQ(OB_ITER_END, ret);
|
||||
iter.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
// scan with normal range, there should be a incresing sequence returned.
|
||||
std::thread scan_all_threads[2];
|
||||
for (int64_t i = 0; i < 2; ++i) {
|
||||
scan_all_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
BtreeKey* start_key = nullptr;
|
||||
BtreeKey* end_key = nullptr;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
BtreeKey* last = nullptr;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(start_key, INT64_MIN));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(end_key, INT64_MAX));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(last, 0));
|
||||
while (!ATOMIC_LOAD(&should_stop)) {
|
||||
TScanHandle iter;
|
||||
init_key(last, INT64_MIN);
|
||||
ret = btree.set_key_range(iter, *start_key, false, *end_key, false, 2);
|
||||
IS_EQ(OB_SUCCESS, ret);
|
||||
while (OB_SUCC(iter.get_next(*tmp_key, tmp_value))) {
|
||||
int cmp = 0;
|
||||
judge(tmp_key, tmp_value);
|
||||
IS_EQ(OB_SUCCESS, tmp_key->compare(*last, cmp));
|
||||
IS_EQ(true, cmp > 0);
|
||||
init_key(last, get_v(tmp_key));
|
||||
}
|
||||
IS_EQ(OB_ITER_END, ret);
|
||||
iter.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_OB_LOG(INFO, "insert with increment key");
|
||||
std::thread order_insert_threads[ORDER_INSERT_THREAD_COUNT];
|
||||
CACHE_ALIGNED int64_t global_key = 0;
|
||||
for (int64_t i = 0; i < ORDER_INSERT_THREAD_COUNT; ++i) {
|
||||
order_insert_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
for (int64_t j = 0; j < INSERT_COUNT_PER_THREAD; ++j) {
|
||||
int64_t key = ATOMIC_FAA(&global_key, 1);
|
||||
auto v = (BtreeVal)(key << 3);
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, key));
|
||||
if (OB_SUCC(btree.get(*tmp_key, tmp_value))) {
|
||||
// do nothing
|
||||
} else if (OB_FAIL(btree.insert(*tmp_key, v))) {
|
||||
IS_EQ(OB_ENTRY_EXIST, ret);
|
||||
}
|
||||
IS_EQ(OB_SUCCESS, btree.get(*tmp_key, tmp_value));
|
||||
judge(tmp_key, tmp_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_OB_LOG(INFO, "insert with random key");
|
||||
std::thread random_insert_threads[RANDOM_INSERT_THREAD_COUNT];
|
||||
CACHE_ALIGNED int64_t random_sum = 0;
|
||||
for (int64_t i = 0; i < RANDOM_INSERT_THREAD_COUNT; ++i) {
|
||||
random_insert_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
for (int64_t j = 0; j < INSERT_COUNT_PER_THREAD * 4; ++j) {
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
int64_t key = ObRandom::rand(0, MAX_INSERT_NUM - 1);
|
||||
auto v = (BtreeVal)(key << 3);
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, key));
|
||||
if (OB_SUCC(btree.get(*tmp_key, tmp_value))) {
|
||||
// do nothing
|
||||
} else if (OB_FAIL(btree.insert(*tmp_key, v))) {
|
||||
IS_EQ(OB_ENTRY_EXIST, ret);
|
||||
} else if (get_v(tmp_key) >= ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD) {
|
||||
ATOMIC_FAA(&random_sum, get_v(tmp_key));
|
||||
}
|
||||
IS_EQ(OB_SUCCESS, btree.get(*tmp_key, tmp_value));
|
||||
judge(tmp_key, tmp_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_OB_LOG(INFO, "del inorder");
|
||||
std::thread delete_threads[DELETE_THREAD_COUNT];
|
||||
CACHE_ALIGNED int64_t del_key = 0;
|
||||
for (int64_t i = 0; i < DELETE_THREAD_COUNT; ++i) {
|
||||
delete_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
int64_t key = 0;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
while ((key = ATOMIC_FAA(&del_key, 1)) < ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD) {
|
||||
init_key(tmp_key, key);
|
||||
while (OB_FAIL(btree.del(*tmp_key, tmp_value, 3))) {
|
||||
IS_EQ(OB_ENTRY_NOT_EXIST, ret);
|
||||
}
|
||||
judge(tmp_key, tmp_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_OB_LOG(INFO, "reinsert inorder");
|
||||
std::thread reinsert_threads[REINSERT_THREAD_COUNT];
|
||||
CACHE_ALIGNED int64_t reinsert_key = 0;
|
||||
for (int64_t i = 0; i < REINSERT_THREAD_COUNT; ++i) {
|
||||
reinsert_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
int64_t key = 0;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
while ((key = ATOMIC_FAA(&reinsert_key, 1)) < ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD) {
|
||||
init_key(tmp_key, key);
|
||||
while (OB_FAIL(btree.re_insert(*tmp_key, (BtreeVal)(key << 3)))) {
|
||||
IS_EQ(OB_ENTRY_NOT_EXIST, ret);
|
||||
}
|
||||
ret = btree.del(*tmp_key, tmp_value, 3);
|
||||
IS_EQ(OB_SUCCESS, ret);
|
||||
judge(tmp_key, tmp_value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int64_t i = 0; i < RANDOM_INSERT_THREAD_COUNT; ++i) {
|
||||
random_insert_threads[i].join();
|
||||
_OB_LOG(INFO, "random insert end");
|
||||
}
|
||||
for (int64_t i = 0; i < ORDER_INSERT_THREAD_COUNT; ++i) {
|
||||
order_insert_threads[i].join();
|
||||
_OB_LOG(INFO, "order insert end");
|
||||
}
|
||||
for (int64_t i = 0; i < DELETE_THREAD_COUNT; ++i) {
|
||||
delete_threads[i].join();
|
||||
_OB_LOG(INFO, "delete end");
|
||||
}
|
||||
for (int64_t i = 0; i < REINSERT_THREAD_COUNT; ++i) {
|
||||
reinsert_threads[i].join();
|
||||
_OB_LOG(INFO, "reinsert end");
|
||||
}
|
||||
|
||||
_OB_LOG(INFO, "cal sum");
|
||||
std::thread scan_threads[SCAN_THREAD_COUNT];
|
||||
CACHE_ALIGNED int64_t sum = 0;
|
||||
for (int64_t i = 0; i < SCAN_THREAD_COUNT; ++i) {
|
||||
scan_threads[i] = std::thread([&, i]() {
|
||||
TScanHandle iter1, iter2;
|
||||
BtreeKey* start_key = nullptr;
|
||||
BtreeKey* end_key = nullptr;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
int64_t len = ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD / SCAN_THREAD_COUNT;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(start_key, i * len));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(end_key, (i + 1) * len));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
IS_EQ(OB_SUCCESS, btree.set_key_range(iter1, *start_key, false, *end_key, true, 2));
|
||||
IS_EQ(OB_SUCCESS, btree.set_key_range(iter2, *start_key, false, *end_key, true, 4));
|
||||
for (int64_t j = 0; j < len; ++j) {
|
||||
IS_EQ(OB_SUCCESS, iter1.get_next(*tmp_key, tmp_value));
|
||||
IS_EQ((uint64_t)tmp_value & 1, 0);
|
||||
IS_EQ(get_v(tmp_key), i * len + j);
|
||||
judge(tmp_key, tmp_value);
|
||||
IS_EQ(OB_SUCCESS, iter2.get_next(*tmp_key, tmp_value));
|
||||
IS_EQ((uint64_t)tmp_value & 1, 1);
|
||||
IS_EQ(get_v(tmp_key), i * len + j);
|
||||
judge(tmp_key, tmp_value);
|
||||
ATOMIC_AAF(&sum, get_v(tmp_key));
|
||||
}
|
||||
IS_EQ(OB_ITER_END, iter1.get_next(*tmp_key, tmp_value));
|
||||
IS_EQ(OB_ITER_END, iter2.get_next(*tmp_key, tmp_value));
|
||||
});
|
||||
}
|
||||
for (int64_t i = 0; i < SCAN_THREAD_COUNT; ++i) {
|
||||
scan_threads[i].join();
|
||||
}
|
||||
IS_EQ((ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD - 1) * ORDER_INSERT_THREAD_COUNT *
|
||||
INSERT_COUNT_PER_THREAD / 2,
|
||||
sum);
|
||||
_OB_LOG(INFO, "cal sum end");
|
||||
|
||||
TScanHandle iter;
|
||||
BtreeKey* start_key = nullptr;
|
||||
BtreeKey* end_key = nullptr;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
BtreeVal tmp_value = nullptr;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(start_key, 0));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(end_key, INT64_MAX));
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
IS_EQ(OB_SUCCESS, btree.set_key_range(iter, *start_key, false, *end_key, true, 5));
|
||||
for (int64_t key = 0; OB_SUCC(iter.get_next(*tmp_key, tmp_value)); ++key) {
|
||||
judge(tmp_key, tmp_value);
|
||||
if (get_v(tmp_key) < ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD) {
|
||||
IS_EQ(get_v(tmp_key), key);
|
||||
} else {
|
||||
random_sum -= get_v(tmp_key);
|
||||
}
|
||||
}
|
||||
IS_EQ(OB_ITER_END, ret);
|
||||
IS_EQ(random_sum, 0);
|
||||
|
||||
_OB_LOG(INFO, "reinsert");
|
||||
ATOMIC_STORE(&global_key, 0);
|
||||
for (int64_t i = 0; i < SCAN_THREAD_COUNT; ++i) {
|
||||
scan_threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
BtreeKey* tmp_key = nullptr;
|
||||
int64_t key = 0;
|
||||
IS_EQ(OB_SUCCESS, alloc_key(tmp_key, 0));
|
||||
while ((key = ATOMIC_FAA(&global_key, 1)) < ORDER_INSERT_THREAD_COUNT * INSERT_COUNT_PER_THREAD) {
|
||||
init_key(tmp_key, key);
|
||||
while (OB_FAIL(btree.re_insert(*tmp_key, (BtreeVal)(key << 3)))) {
|
||||
IS_EQ(OB_ENTRY_NOT_EXIST, ret);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
for (int64_t i = 0; i < SCAN_THREAD_COUNT; ++i) {
|
||||
scan_threads[i].join();
|
||||
}
|
||||
_OB_LOG(INFO, "reinsert end");
|
||||
|
||||
IS_EQ(OB_SUCCESS, btree.destroy());
|
||||
|
||||
ATOMIC_STORE(&should_stop, true);
|
||||
for (int64_t i = 0; i < 2; ++i) {
|
||||
normal_threads[i].join();
|
||||
head_insert_thread[i].join();
|
||||
bad_scan_threads[i].join();
|
||||
scan_all_threads[i].join();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// oceanbase::unittest::BIND_CPU(pthread_self());
|
||||
oceanbase::common::ObLogger::get_logger().set_file_name("test_keybtree.log", true);
|
||||
oceanbase::common::ObLogger::get_logger().set_log_level("INFO");
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
106
unittest/storage/memtable/mvcc/test_mvcc_callback.cpp
Normal file
106
unittest/storage/memtable/mvcc/test_mvcc_callback.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* 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>
|
||||
|
||||
#define private public
|
||||
|
||||
#include "storage/memtable/mvcc/ob_mvcc_trans_ctx.h"
|
||||
#include "storage/memtable/ob_memtable_context.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::memtable;
|
||||
|
||||
class ObMockTransCallback : public ObITransCallback {
|
||||
public:
|
||||
ObMockTransCallback(ObMemtable* mt) : fake_mt_(mt)
|
||||
{}
|
||||
|
||||
int callback(const int type, const bool for_replay, const bool need_lock_for_write, ObMemtable* memtable) override
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(for_replay);
|
||||
UNUSED(need_lock_for_write);
|
||||
|
||||
if (TCB_REMOVE_CALLBACK == type) {
|
||||
if (memtable == fake_mt_) {
|
||||
del();
|
||||
} else {
|
||||
ret = OB_ITEM_NOT_MATCH;
|
||||
}
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int del() override
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
ObITransCallback* cur = this;
|
||||
|
||||
if (NULL == cur) {
|
||||
ret = common::OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
ObITransCallback* prev = cur->prev_;
|
||||
ObITransCallback* next = cur->next_;
|
||||
if (NULL == prev || NULL == next) {
|
||||
ret = common::OB_INVALID_ARGUMENT;
|
||||
} else {
|
||||
prev->next_ = next;
|
||||
next->prev_ = prev;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ObMemtable* fake_mt_;
|
||||
};
|
||||
|
||||
TEST(TestObMvccCallback, remove_callback_test)
|
||||
{
|
||||
uint64_t addr1 = 3;
|
||||
ObMockTransCallback cb1((ObMemtable*)(addr1));
|
||||
uint64_t addr2 = 1;
|
||||
ObMockTransCallback cb2((ObMemtable*)(addr2));
|
||||
uint64_t addr3 = 2;
|
||||
int64_t cnt = 0;
|
||||
|
||||
ObMemtableCtx mt_ctx;
|
||||
ObTransCallbackMgr mgr(mt_ctx);
|
||||
mgr.append(&cb1);
|
||||
mgr.append(&cb2);
|
||||
|
||||
ASSERT_EQ(2, mgr.count());
|
||||
mgr.remove_callback_for_uncommited_txn((ObMemtable*)(addr1), cnt);
|
||||
ASSERT_EQ(1, mgr.count());
|
||||
mgr.remove_callback_for_uncommited_txn((ObMemtable*)(addr3), cnt);
|
||||
ASSERT_EQ(1, mgr.count());
|
||||
mgr.remove_callback_for_uncommited_txn((ObMemtable*)(addr2), cnt);
|
||||
ASSERT_EQ(0, mgr.count());
|
||||
}
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
oceanbase::common::ObLogger::get_logger().set_file_name("test_mvcc_trans_ctx.log", true);
|
||||
oceanbase::common::ObLogger::get_logger().set_log_level("INFO");
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
214
unittest/storage/memtable/mvcc/test_query_engine.cpp
Normal file
214
unittest/storage/memtable/mvcc/test_query_engine.cpp
Normal file
@ -0,0 +1,214 @@
|
||||
/**
|
||||
* 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 "storage/memtable/mvcc/ob_query_engine.h"
|
||||
|
||||
#include "storage/memtable/ob_memtable_key.h"
|
||||
#include "lib/atomic/ob_atomic.h"
|
||||
|
||||
#include "../utils_rowkey_builder.h"
|
||||
#include "../utils_mod_allocator.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <thread>
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::keybtree;
|
||||
using namespace oceanbase::memtable;
|
||||
using ObQueryEngineIterator = ObQueryEngine::Iterator<TScanHandle>;
|
||||
|
||||
TEST(TestObQueryEngine, get_and_set_table_index_node)
|
||||
{
|
||||
// This is a concurrency scene, use multi-thread test.
|
||||
constexpr uint64_t TABLE_COUNT_LIMIT = (1 << 14);
|
||||
constexpr int64_t THREAD_COUNT = 20;
|
||||
ObModAllocator allocator;
|
||||
ObQueryEngine qe(allocator);
|
||||
ObQueryEngine::TableIndexNode* table_index_nodes[TABLE_COUNT_LIMIT];
|
||||
uint64_t counter = 0;
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
memset(table_index_nodes, 0, sizeof(table_index_nodes));
|
||||
ret = qe.init(1);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
std::thread threads[THREAD_COUNT];
|
||||
for (int64_t i = 0; i < THREAD_COUNT; ++i) {
|
||||
threads[i] = std::thread([&]() {
|
||||
int ret = OB_SUCCESS;
|
||||
ObQueryEngine::TableIndexNode* tmp_ptr = nullptr;
|
||||
for (int64_t j = 0; j < TABLE_COUNT_LIMIT; ++j) {
|
||||
if (OB_FAIL(qe.get_table_index_node(j, tmp_ptr))) {
|
||||
EXPECT_EQ(OB_TABLE_NOT_EXIST, ret);
|
||||
ret = qe.set_table_index_node(j, 1, tmp_ptr);
|
||||
}
|
||||
// select or update must succeed.
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_TRUE(OB_NOT_NULL(tmp_ptr));
|
||||
if (ATOMIC_BCAS(table_index_nodes + j, nullptr, tmp_ptr)) {
|
||||
// if update address successfully, then counter it.
|
||||
ATOMIC_INC(&counter);
|
||||
} else {
|
||||
// else it must be equal with old_val.
|
||||
EXPECT_EQ(ATOMIC_LOAD(table_index_nodes + j), tmp_ptr);
|
||||
}
|
||||
tmp_ptr = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int64_t i = 0; i < THREAD_COUNT; ++i) {
|
||||
threads[i].join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(counter, TABLE_COUNT_LIMIT);
|
||||
}
|
||||
|
||||
TEST(TestObQueryEngine, smoke_test)
|
||||
{
|
||||
static const int64_t R_COUNT = 6;
|
||||
|
||||
int ret = OB_SUCCESS;
|
||||
ObModAllocator allocator;
|
||||
ObQueryEngine qe(allocator);
|
||||
ObMemtableKey* mtk[R_COUNT];
|
||||
ObMvccTransNode tdn[R_COUNT];
|
||||
ObMvccRow mtv[R_COUNT];
|
||||
|
||||
auto init_mtv = [&](ObMvccRow& mtv, ObMvccTransNode& node) { mtv.list_head_ = &node; };
|
||||
auto test_set_and_get = [&](ObMemtableKey* mtk, ObMvccRow& mtv) {
|
||||
int ret = OB_SUCCESS;
|
||||
ret = qe.set(mtk, &mtv);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ObMemtableKey t;
|
||||
ObMvccRow* v = nullptr;
|
||||
ret = qe.get(mtk, v, &t);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(v, &mtv);
|
||||
};
|
||||
auto test_ensure = [&](ObMemtableKey* mtk, ObMvccRow& mtv) {
|
||||
int ret = OB_SUCCESS;
|
||||
ret = qe.ensure(mtk, &mtv);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
};
|
||||
auto test_scan = [&](int64_t start, bool include_start, int64_t end, bool include_end) {
|
||||
ObIQueryEngineIterator* iter = nullptr;
|
||||
ret = qe.scan(mtk[start], !include_start, mtk[end], !include_end, 1, iter);
|
||||
bool skip_purge_memtable = false;
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
if (start <= end) {
|
||||
for (int64_t i = (include_start ? start : (start + 1)); i <= (include_end ? end : (end - 1)); i++) {
|
||||
ret = iter->next(skip_purge_memtable);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
assert(0 == mtk[i]->compare(*iter->get_key()));
|
||||
EXPECT_EQ(&mtv[i], iter->get_value());
|
||||
}
|
||||
} else {
|
||||
for (int64_t i = (include_start ? start : (start - 1)); i >= (include_end ? end : (end + 1)); i--) {
|
||||
ret = iter->next(skip_purge_memtable);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
assert(0 == mtk[i]->compare(*iter->get_key()));
|
||||
EXPECT_EQ(&mtv[i], iter->get_value());
|
||||
}
|
||||
}
|
||||
ret = iter->next(skip_purge_memtable);
|
||||
EXPECT_EQ(OB_ITER_END, ret);
|
||||
// if QueryEngine::Iterator returnes ITER_END, inner iter will be freed.
|
||||
ret = iter->next(skip_purge_memtable);
|
||||
EXPECT_EQ(OB_ITER_END, ret);
|
||||
};
|
||||
|
||||
ret = qe.init(1);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
INIT_MTK(allocator, mtk[0], 1000, V("aaaa", 4), I(1024), N("1234567890.01234567890"));
|
||||
INIT_MTK(allocator, mtk[1], 1000, V("aaaa", 4), I(1024), N("1234567890.01234567891"));
|
||||
INIT_MTK(allocator, mtk[2], 1000, V("aaaa", 4), I(2048), N("1234567890.01234567890"));
|
||||
INIT_MTK(allocator, mtk[3], 1000, V("aaaa", 4), I(2048), N("1234567890.01234567891"));
|
||||
INIT_MTK(allocator, mtk[4], 1000, V("bbbb", 4), I(2048), N("1234567890.01234567890"));
|
||||
INIT_MTK(allocator, mtk[5], 1000, V("bbbb", 4), I(2048), N("1234567890.01234567891"));
|
||||
|
||||
init_mtv(mtv[0], tdn[0]);
|
||||
init_mtv(mtv[1], tdn[1]);
|
||||
init_mtv(mtv[2], tdn[2]);
|
||||
init_mtv(mtv[3], tdn[3]);
|
||||
init_mtv(mtv[4], tdn[4]);
|
||||
init_mtv(mtv[5], tdn[5]);
|
||||
|
||||
test_set_and_get(mtk[0], mtv[0]);
|
||||
test_set_and_get(mtk[1], mtv[1]);
|
||||
test_set_and_get(mtk[2], mtv[2]);
|
||||
test_set_and_get(mtk[3], mtv[3]);
|
||||
test_set_and_get(mtk[4], mtv[4]);
|
||||
test_set_and_get(mtk[5], mtv[5]);
|
||||
|
||||
// every hashtable will be inited with 128, every set has 1/1024 percent of expanding(1024).
|
||||
// keys with different table_id will be inserted into different btree.
|
||||
assert(128 == qe.hash_size() % (1 << 10));
|
||||
assert(R_COUNT >= (qe.hash_size() - 128) / (1 << 10));
|
||||
|
||||
test_ensure(mtk[0], mtv[0]);
|
||||
test_ensure(mtk[1], mtv[1]);
|
||||
test_ensure(mtk[2], mtv[2]);
|
||||
test_ensure(mtk[3], mtv[3]);
|
||||
test_ensure(mtk[4], mtv[4]);
|
||||
test_ensure(mtk[5], mtv[5]);
|
||||
|
||||
EXPECT_EQ(R_COUNT, qe.btree_size());
|
||||
|
||||
test_scan(0, true, 5, true);
|
||||
test_scan(0, false, 5, true);
|
||||
test_scan(0, true, 5, false);
|
||||
test_scan(0, false, 5, false);
|
||||
|
||||
test_scan(5, true, 0, true);
|
||||
test_scan(5, false, 0, true);
|
||||
test_scan(5, true, 0, false);
|
||||
test_scan(5, false, 0, false);
|
||||
|
||||
test_scan(1, true, 4, true);
|
||||
test_scan(1, false, 4, true);
|
||||
test_scan(1, true, 4, false);
|
||||
test_scan(1, false, 4, false);
|
||||
|
||||
test_scan(4, true, 1, true);
|
||||
test_scan(4, false, 1, true);
|
||||
test_scan(4, true, 1, false);
|
||||
test_scan(4, false, 1, false);
|
||||
|
||||
test_scan(0, true, 0, true);
|
||||
test_scan(1, true, 1, true);
|
||||
test_scan(2, true, 2, true);
|
||||
test_scan(3, true, 3, true);
|
||||
test_scan(4, true, 4, true);
|
||||
test_scan(5, true, 5, true);
|
||||
|
||||
test_scan(0, false, 0, false);
|
||||
test_scan(1, false, 1, false);
|
||||
test_scan(2, false, 2, false);
|
||||
test_scan(3, false, 3, false);
|
||||
test_scan(4, false, 4, false);
|
||||
test_scan(5, false, 5, false);
|
||||
}
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
oceanbase::common::ObLogger::get_logger().set_file_name("test_query_engine.log", true);
|
||||
oceanbase::common::ObLogger::get_logger().set_log_level("INFO");
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
88
unittest/storage/memtable/ob_row_builder.h
Normal file
88
unittest/storage/memtable/ob_row_builder.h
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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 "lib/container/ob_se_array.h"
|
||||
#include "share/schema/ob_table_schema.h"
|
||||
#include "storage/memtable/ob_memtable_interface.h"
|
||||
#include "storage/ob_i_store.h"
|
||||
#include "lib/allocator/page_arena.h"
|
||||
#include "common/object/ob_object.h"
|
||||
#include "common/rowkey/ob_rowkey.h"
|
||||
|
||||
namespace oceanbase {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::memtable;
|
||||
using namespace oceanbase::storage;
|
||||
namespace unittest {
|
||||
#include "strutils.h"
|
||||
typedef ObSEArray<share::schema::ObColDesc, 64> ColDescArray;
|
||||
class ColDescBuilder {
|
||||
public:
|
||||
const ColDescArray& get_columns() const
|
||||
{
|
||||
return columns_;
|
||||
}
|
||||
void add(uint64_t col_id, ObObjType col_type, ObCollationType col_collation)
|
||||
{
|
||||
share::schema::ObColDesc col_desc;
|
||||
col_desc.col_id_ = col_id;
|
||||
col_desc.col_type_.set_type(col_type);
|
||||
col_desc.col_type_.set_collation_type(col_collation);
|
||||
columns_.push_back(col_desc);
|
||||
}
|
||||
|
||||
private:
|
||||
ColDescArray columns_;
|
||||
};
|
||||
|
||||
class RowIterBuilder {
|
||||
public:
|
||||
enum { MAX_ROWKEY_OBJ = 64 };
|
||||
RowIterBuilder(const ColDescArray& cols) : cols_(cols)
|
||||
{}
|
||||
~RowIterBuilder()
|
||||
{}
|
||||
ObMtRowIterator& build(const char* str)
|
||||
{
|
||||
ObStoreRow row;
|
||||
row.row_val_.cells_ = build_obj_array(str);
|
||||
row.row_val_.count_ = cols_.count();
|
||||
row.set_dml(T_DML_UPDATE);
|
||||
iter_.add_row(row);
|
||||
return iter_;
|
||||
}
|
||||
ObObj* build_obj_array(const char* str)
|
||||
{
|
||||
char buf[4096];
|
||||
Tokenizer tok(strcpy(buf, str), " ");
|
||||
for (int64_t i = 0; i < cols_.count(); i++) {
|
||||
parse_obj(obj_array_[i], tok.next());
|
||||
}
|
||||
return obj_array_;
|
||||
}
|
||||
|
||||
private:
|
||||
static int parse_obj(ObObj& obj, const char* val)
|
||||
{
|
||||
int err = OB_SUCCESS;
|
||||
obj.set_int(atoi(val));
|
||||
return err;
|
||||
}
|
||||
|
||||
private:
|
||||
const ColDescArray& cols_;
|
||||
ObObj obj_array_[MAX_ROWKEY_OBJ];
|
||||
ObMtRowIterator iter_;
|
||||
};
|
||||
|
||||
}; // namespace unittest
|
||||
}; // end namespace oceanbase
|
||||
90
unittest/storage/memtable/strutils.h
Normal file
90
unittest/storage/memtable/strutils.h
Normal file
@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class Printer {
|
||||
public:
|
||||
enum { MAX_BUF_SIZE = 4096 };
|
||||
Printer() : limit_(MAX_BUF_SIZE), pos_(0)
|
||||
{
|
||||
memset(buf_, 0, MAX_BUF_SIZE);
|
||||
}
|
||||
|
||||
~Printer()
|
||||
{
|
||||
pos_ = 0;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
pos_ = 0;
|
||||
*buf_ = 0;
|
||||
}
|
||||
char* get_str()
|
||||
{
|
||||
return NULL != buf_ && limit_ > 0 ? buf_ : NULL;
|
||||
}
|
||||
char* append(const char* format, ...)
|
||||
{
|
||||
char* src = NULL;
|
||||
int64_t count = 0;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
if (NULL != buf_ && limit_ > 0 && pos_ < limit_ &&
|
||||
pos_ + (count = vsnprintf(buf_ + pos_, limit_ - pos_, format, ap)) < limit_) {
|
||||
src = buf_ + pos_;
|
||||
pos_ += count;
|
||||
}
|
||||
va_end(ap);
|
||||
return src;
|
||||
}
|
||||
char* new_str(const char* format, ...)
|
||||
{
|
||||
char* src = NULL;
|
||||
int64_t count = 0;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
if (NULL != buf_ && limit_ > 0 && pos_ < limit_ &&
|
||||
pos_ + (count = vsnprintf(buf_ + pos_, limit_ - pos_, format, ap)) + 1 < limit_) {
|
||||
src = buf_ + pos_;
|
||||
pos_ += count + 1;
|
||||
}
|
||||
va_end(ap);
|
||||
return src;
|
||||
}
|
||||
|
||||
private:
|
||||
char buf_[MAX_BUF_SIZE];
|
||||
int64_t limit_;
|
||||
int64_t pos_;
|
||||
};
|
||||
|
||||
class Tokenizer {
|
||||
public:
|
||||
Tokenizer(char* str, const char* delim) : str_(str), delim_(delim), saveptr_(NULL)
|
||||
{}
|
||||
~Tokenizer()
|
||||
{}
|
||||
const char* next()
|
||||
{
|
||||
char* ret = NULL;
|
||||
if (NULL == saveptr_) {
|
||||
ret = strtok_r(str_, delim_, &saveptr_);
|
||||
} else {
|
||||
ret = strtok_r(NULL, delim_, &saveptr_);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
char* str_;
|
||||
const char* delim_;
|
||||
char* saveptr_;
|
||||
};
|
||||
29
unittest/storage/memtable/test_base.cpp
Normal file
29
unittest/storage/memtable/test_base.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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/allocator/ob_malloc.h"
|
||||
#include <locale.h>
|
||||
|
||||
using namespace oceanbase::common;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int err = OB_SUCCESS;
|
||||
setlocale(LC_ALL, "");
|
||||
oceanbase::common::ObLogger::get_logger().set_log_level(getenv("log_level") ?: "INFO");
|
||||
oceanbase::unittest::init_tenant_mgr();
|
||||
return err;
|
||||
}
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
390
unittest/storage/memtable/test_base.h
Normal file
390
unittest/storage/memtable/test_base.h
Normal file
@ -0,0 +1,390 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __OB_COMMON_TEST_BASE_H__
|
||||
#define __OB_COMMON_TEST_BASE_H__
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
inline uint64_t rand64(uint64_t h)
|
||||
{
|
||||
if (0 == h)
|
||||
return 1;
|
||||
h ^= h >> 33;
|
||||
h *= 0xff51afd7ed558ccd;
|
||||
h ^= h >> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53;
|
||||
h ^= h >> 33;
|
||||
// h &= ~(1ULL<<63);
|
||||
return h;
|
||||
}
|
||||
|
||||
int64_t get_us()
|
||||
{
|
||||
struct timeval time_val;
|
||||
gettimeofday(&time_val, NULL);
|
||||
return time_val.tv_sec * 1000000 + time_val.tv_usec;
|
||||
}
|
||||
|
||||
#define profile(expr, n) \
|
||||
{ \
|
||||
int64_t start = get_usec(); \
|
||||
expr; \
|
||||
int64_t end = get_usec(); \
|
||||
printf("%s: 1000000*%ld/%ld=%ld\n", #expr, n, end - start, 1000000 * n / (end - start)); \
|
||||
}
|
||||
|
||||
struct Callable {
|
||||
Callable() : stop_(false)
|
||||
{}
|
||||
virtual ~Callable()
|
||||
{}
|
||||
virtual int call(pthread_t thread, int64_t idx) = 0;
|
||||
volatile bool stop_;
|
||||
};
|
||||
|
||||
typedef void* (*pthread_handler_t)(void*);
|
||||
class BaseWorker {
|
||||
public:
|
||||
static const int64_t MAX_N_THREAD = 16;
|
||||
struct WorkContext {
|
||||
WorkContext() : callable_(NULL), idx_(0)
|
||||
{}
|
||||
~WorkContext()
|
||||
{}
|
||||
WorkContext& set(Callable* callable, int64_t idx)
|
||||
{
|
||||
callable_ = callable;
|
||||
idx_ = idx;
|
||||
return *this;
|
||||
}
|
||||
Callable* callable_;
|
||||
pthread_t thread_;
|
||||
int64_t idx_;
|
||||
};
|
||||
|
||||
public:
|
||||
BaseWorker() : n_thread_(0), thread_running_(false)
|
||||
{}
|
||||
~BaseWorker()
|
||||
{
|
||||
wait();
|
||||
}
|
||||
|
||||
public:
|
||||
BaseWorker& set_thread_num(int64_t n)
|
||||
{
|
||||
n_thread_ = n;
|
||||
return *this;
|
||||
}
|
||||
int start(Callable* callable, int64_t idx = -1)
|
||||
{
|
||||
int err = 0;
|
||||
for (int64_t i = 0; i < n_thread_; i++) {
|
||||
if (idx > 0 && idx != i) {
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "worker[%ld] start.\n", i);
|
||||
pthread_create(&ctx_[i].thread_, NULL, (pthread_handler_t)do_work, (void*)(&ctx_[i].set(callable, i)));
|
||||
}
|
||||
thread_running_ = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
int wait(int64_t idx = -1)
|
||||
{
|
||||
int err = 0;
|
||||
int64_t ret = 0;
|
||||
for (int64_t i = 0; thread_running_ && i < n_thread_; i++) {
|
||||
if (idx > 0 && idx != i) {
|
||||
continue;
|
||||
}
|
||||
pthread_join(ctx_[i].thread_, (void**)&ret);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "thread[%ld] => %ld\n", i, ret);
|
||||
} else {
|
||||
fprintf(stderr, "thread[%ld] => OK.\n", i);
|
||||
}
|
||||
}
|
||||
thread_running_ = false;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_work(WorkContext* ctx)
|
||||
{
|
||||
int err = 0;
|
||||
if (NULL == ctx || NULL == ctx->callable_) {
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
err = ctx->callable_->call(ctx->thread_, ctx->idx_);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
int par_do(Callable* callable, int64_t duration)
|
||||
{
|
||||
int err = 0;
|
||||
if (0 != (err = start(callable))) {
|
||||
fprintf(stderr, "start()=>%d\n", err);
|
||||
} else {
|
||||
usleep(static_cast<__useconds_t>(duration));
|
||||
callable->stop_ = true;
|
||||
}
|
||||
if (0 != (err = wait())) {
|
||||
fprintf(stderr, "wait()=>%d\n", err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t n_thread_;
|
||||
bool thread_running_;
|
||||
WorkContext ctx_[MAX_N_THREAD];
|
||||
};
|
||||
|
||||
int PARDO(int64_t thread_num, Callable* call, int64_t duration)
|
||||
{
|
||||
BaseWorker worker;
|
||||
fprintf(stderr, "thread_num=%ld\n", thread_num);
|
||||
return worker.set_thread_num(thread_num).par_do(call, duration);
|
||||
}
|
||||
|
||||
#if 0
|
||||
struct SimpleCallable: public Callable
|
||||
{
|
||||
int64_t n_items_;
|
||||
SimpleCallable &set(int64_t n_items)
|
||||
{
|
||||
n_items_ = n_items;
|
||||
return *this;
|
||||
}
|
||||
int call(pthread_t thread, int64_t idx)
|
||||
{
|
||||
int err = 0;
|
||||
fprintf(stdout, "worker[%ld] run\n", idx);
|
||||
if (idx % 2) {
|
||||
err = -EPERM;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int err = 0;
|
||||
BaseWorker worker;
|
||||
SimpleCallable callable;
|
||||
int64_t n_thread = 0;
|
||||
int64_t n_items = 0;
|
||||
if (argc != 3) {
|
||||
err = -EINVAL;
|
||||
fprintf(stderr, "%s n_thread n_item\n", argv[0]);
|
||||
} else {
|
||||
n_thread = atoll(argv[1]);
|
||||
n_items = atoll(argv[2]);
|
||||
profile(worker.set_thread_num(n_thread).par_do(&callable.set(n_items), 10000000), n_items);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class RWT : public Callable {
|
||||
typedef void* (*pthread_handler_t)(void*);
|
||||
struct Thread {
|
||||
int set(RWT* self, int64_t idx)
|
||||
{
|
||||
self_ = self, idx_ = idx;
|
||||
return 0;
|
||||
}
|
||||
pthread_t thread_;
|
||||
RWT* self_;
|
||||
int64_t idx_;
|
||||
};
|
||||
|
||||
public:
|
||||
RWT() : n_read_thread_(0), n_write_thread_(0), n_admin_thread_(0)
|
||||
{}
|
||||
virtual ~RWT()
|
||||
{}
|
||||
|
||||
public:
|
||||
int64_t get_thread_num()
|
||||
{
|
||||
return 1 + n_read_thread_ + n_write_thread_ + n_admin_thread_;
|
||||
}
|
||||
RWT& set(const int64_t n_read, const int64_t n_write, const int64_t n_admin = 0)
|
||||
{
|
||||
n_read_thread_ = n_read;
|
||||
n_write_thread_ = n_write;
|
||||
n_admin_thread_ = n_admin;
|
||||
return *this;
|
||||
}
|
||||
int report_loop()
|
||||
{
|
||||
int err = 0;
|
||||
int64_t report_interval = 1000 * 1000;
|
||||
while (!stop_ && 0 == err) {
|
||||
usleep(static_cast<__useconds_t>(report_interval));
|
||||
err = report();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
virtual int call(pthread_t thread, const int64_t idx_)
|
||||
{
|
||||
int err = 0;
|
||||
int64_t idx = idx_;
|
||||
(void)(thread);
|
||||
fprintf(stderr, "rwt.start(idx=%ld)\n", idx_);
|
||||
if (idx < 0) {
|
||||
err = -EINVAL;
|
||||
}
|
||||
if (0 == err && idx >= 0) {
|
||||
if (idx == 0) {
|
||||
err = report_loop();
|
||||
}
|
||||
idx -= 1;
|
||||
}
|
||||
if (0 == err && idx >= 0) {
|
||||
if (idx < n_read_thread_) {
|
||||
err = read(idx);
|
||||
}
|
||||
idx -= n_read_thread_;
|
||||
}
|
||||
if (0 == err && idx >= 0) {
|
||||
if (idx < n_write_thread_) {
|
||||
err = write(idx);
|
||||
}
|
||||
idx -= n_write_thread_;
|
||||
}
|
||||
if (0 == err && idx >= 0) {
|
||||
if (idx < n_admin_thread_) {
|
||||
err = admin(idx);
|
||||
}
|
||||
idx -= n_admin_thread_;
|
||||
}
|
||||
if (0 == err && idx >= 0) {
|
||||
err = -EINVAL;
|
||||
}
|
||||
if (0 != err) {
|
||||
stop_ = true;
|
||||
}
|
||||
fprintf(stderr, "rwt.start(idx=%ld)=>%d\n", idx_, err);
|
||||
return err;
|
||||
}
|
||||
virtual int report()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int read(const int64_t idx) = 0;
|
||||
virtual int write(const int64_t idx) = 0;
|
||||
virtual int admin(const int64_t idx)
|
||||
{
|
||||
(void)(idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
int64_t n_read_thread_;
|
||||
int64_t n_write_thread_;
|
||||
int64_t n_admin_thread_;
|
||||
};
|
||||
|
||||
#define _cfg(k, v) getenv(k) ?: v
|
||||
#define _cfgi(k, v) atoll(getenv(k) ?: v)
|
||||
|
||||
inline int64_t rand_range(int64_t s, int64_t e)
|
||||
{
|
||||
return s + random() % (e - s);
|
||||
}
|
||||
|
||||
#define RWT_def(base) \
|
||||
TEST_F(base, Rand) \
|
||||
{ \
|
||||
ASSERT_EQ(0, PARDO(get_thread_num(), this, duration)); \
|
||||
ASSERT_EQ(0, check_error()); \
|
||||
}
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "common/data_buffer.h"
|
||||
#include "lib/allocator/ob_allocator.h"
|
||||
#include "lib/allocator/ob_malloc.h"
|
||||
#include "lib/regex/regex/utils.h"
|
||||
|
||||
using namespace oceanbase::common;
|
||||
|
||||
struct BufHolder {
|
||||
BufHolder(int64_t limit)
|
||||
{
|
||||
buf_ = (char*)ob_malloc(limit, ObModIds::TEST);
|
||||
}
|
||||
~BufHolder()
|
||||
{
|
||||
ob_free((void*)buf_);
|
||||
}
|
||||
char* buf_;
|
||||
};
|
||||
|
||||
struct BaseConfig {
|
||||
static const int64_t buf_limit = 1 << 21;
|
||||
BufHolder buf_holder;
|
||||
ObDataBuffer buf;
|
||||
int64_t duration;
|
||||
const char* schema;
|
||||
int64_t table_id;
|
||||
BaseConfig() : buf_holder(buf_limit)
|
||||
{
|
||||
buf.set_data(buf_holder.buf_, buf_limit);
|
||||
duration = _cfgi("duration", "3000000");
|
||||
schema = "./test.schema";
|
||||
table_id = 1002;
|
||||
}
|
||||
};
|
||||
|
||||
class FixedAllocator : public ObIAllocator {
|
||||
public:
|
||||
FixedAllocator(char* buf, int64_t limit) : buf_(buf), limit_(limit), pos_(0)
|
||||
{}
|
||||
virtual ~FixedAllocator()
|
||||
{}
|
||||
|
||||
public:
|
||||
void reset()
|
||||
{
|
||||
pos_ = 0;
|
||||
}
|
||||
virtual void* alloc(const int64_t sz)
|
||||
{
|
||||
void* ptr = NULL;
|
||||
int64_t pos = 0;
|
||||
if ((pos = __sync_add_and_fetch(&pos_, sz)) > limit_) {
|
||||
__sync_add_and_fetch(&pos_, -sz);
|
||||
} else {
|
||||
ptr = buf_ + pos;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
virtual void free(void* ptr)
|
||||
{
|
||||
UNUSED(ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
char* buf_;
|
||||
int64_t limit_;
|
||||
int64_t pos_;
|
||||
};
|
||||
|
||||
#endif /* __OB_COMMON_TEST_BASE_H__ */
|
||||
815
unittest/storage/memtable/test_memtable.cpp
Normal file
815
unittest/storage/memtable/test_memtable.cpp
Normal file
@ -0,0 +1,815 @@
|
||||
/**
|
||||
* 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 "storage/memtable/ob_memtable.h"
|
||||
#include "storage/memtable/ob_memtable_iterator.h"
|
||||
#include "storage/memtable/ob_memtable_mutator.h"
|
||||
|
||||
#include "common/cell/ob_cell_reader.h"
|
||||
#include "lib/allocator/page_arena.h"
|
||||
#include "lib/container/ob_se_array.h"
|
||||
#include "storage/ob_i_store.h"
|
||||
#include "share/ob_srv_rpc_proxy.h"
|
||||
|
||||
#include "utils_rowkey_builder.h"
|
||||
#include "utils_mod_allocator.h"
|
||||
//#include "utils_mock_ctx.h"
|
||||
#include "utils_mock_row.h"
|
||||
|
||||
#include "../mockcontainer/mock_ob_iterator.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::memtable;
|
||||
|
||||
ObMemtableCtxFactory f;
|
||||
|
||||
static uint64_t MT_TABLE_ID = combine_id(1, 3001);
|
||||
|
||||
void test_redo_log(ObIMemtableCtx& mem_ctx, const uint64_t index_id, const ObStoreRowkey& rowkey,
|
||||
const storage::ObRowDml dml_type, ObMemtable& replayed_mt, const int64_t trans_version)
|
||||
{
|
||||
const int64_t REDO_BUFFER_SIZE = 2L * 1024L * 1024L;
|
||||
char* redo_log_buffer = new char[REDO_BUFFER_SIZE];
|
||||
int64_t sel_pos = 0;
|
||||
int ret = mem_ctx.fill_redo_log(redo_log_buffer, REDO_BUFFER_SIZE, sel_pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
ObArenaAllocator allocator(0);
|
||||
ObMemtableMutatorIterator mmi;
|
||||
|
||||
int64_t des_pos = 0;
|
||||
ret = mmi.deserialize(redo_log_buffer, sel_pos, des_pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
uint64_t res_index_id = 0;
|
||||
ObStoreRowkey res_rowkey;
|
||||
int64_t schema_version = 0;
|
||||
ObRowData new_row;
|
||||
storage::ObRowDml res_dml_type = T_DML_UNKNOWN;
|
||||
uint32_t res_modify_count = 0;
|
||||
|
||||
ret = mmi.get_next_row(res_index_id, res_rowkey, schema_version, new_row, res_dml_type, res_modify_count);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(index_id, res_index_id);
|
||||
EXPECT_EQ(rowkey, res_rowkey);
|
||||
EXPECT_EQ(dml_type, res_dml_type);
|
||||
EXPECT_EQ(trans_version - 1, res_modify_count);
|
||||
|
||||
ret = mmi.get_next_row(res_index_id, res_rowkey, schema_version, new_row, res_dml_type, res_modify_count);
|
||||
EXPECT_EQ(OB_ITER_END, ret);
|
||||
|
||||
storage::ObStoreCtx ctx;
|
||||
ctx.mem_ctx_ = f.alloc();
|
||||
ret = replayed_mt.replay(ctx, redo_log_buffer, sel_pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ctx.mem_ctx_->trans_replay_end(true, trans_version);
|
||||
f.free(ctx.mem_ctx_);
|
||||
free(redo_log_buffer);
|
||||
}
|
||||
|
||||
void test_redo_log_row_pending(ObIMemtableCtx& mem_ctx, const uint64_t index_id, const ObStoreRowkey& rowkey,
|
||||
const storage::ObRowDml dml_type, ObMemtable& replayed_mt, const int64_t trans_version)
|
||||
{
|
||||
const int64_t REDO_BUFFER_SIZE = 2L * 1024L * 1024L;
|
||||
char* redo_log_buffer = new char[REDO_BUFFER_SIZE];
|
||||
int64_t sel_pos = 0;
|
||||
int ret = mem_ctx.fill_redo_log(redo_log_buffer, REDO_BUFFER_SIZE, sel_pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
ObArenaAllocator allocator(0);
|
||||
ObMemtableMutatorIterator mmi;
|
||||
|
||||
int64_t des_pos = 0;
|
||||
ret = mmi.deserialize(redo_log_buffer, sel_pos, des_pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
uint64_t res_index_id = 0;
|
||||
ObStoreRowkey res_rowkey;
|
||||
int64_t schema_version = 0;
|
||||
ObRowData new_row;
|
||||
storage::ObRowDml res_dml_type = T_DML_UNKNOWN;
|
||||
uint32_t res_modify_count = 0;
|
||||
|
||||
ret = mmi.get_next_row(res_index_id, res_rowkey, schema_version, new_row, res_dml_type, res_modify_count);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(index_id, res_index_id);
|
||||
EXPECT_EQ(rowkey, res_rowkey);
|
||||
EXPECT_EQ(dml_type, res_dml_type);
|
||||
EXPECT_EQ(trans_version - 1, res_modify_count);
|
||||
|
||||
ret = mmi.get_next_row(res_index_id, res_rowkey, schema_version, new_row, res_dml_type, res_modify_count);
|
||||
EXPECT_EQ(OB_ITER_END, ret);
|
||||
|
||||
storage::ObStoreCtx ctx;
|
||||
ctx.mem_ctx_ = f.alloc();
|
||||
ret = replayed_mt.replay(ctx, redo_log_buffer, sel_pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
// Replay to commit.
|
||||
ret = ctx.mem_ctx_->replay_to_commit();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = ctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = ctx.mem_ctx_->replay_to_commit();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
// Commit it.
|
||||
ctx.mem_ctx_->trans_end(true, trans_version);
|
||||
f.free(ctx.mem_ctx_);
|
||||
free(redo_log_buffer);
|
||||
}
|
||||
|
||||
void test_mt_query(RK& rk, const ObIArray<share::schema::ObColDesc>& columns, ObMemtable& mt)
|
||||
{
|
||||
const ObStoreRow* got_row = NULL;
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
ObQueryFlag flag;
|
||||
ObStoreCtx ctx;
|
||||
ctx.mem_ctx_ = f.alloc();
|
||||
ctx.mem_ctx_->trans_begin();
|
||||
ctx.mem_ctx_->sub_trans_begin(1000, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.get(ctx, flag, 1001, rk.get_rowkey(), columns, got_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_DEL_ROW, got_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), got_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(got_row->row_val_.cells_, got_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*got_row));
|
||||
mt.revert_row(got_row);
|
||||
|
||||
ObStoreRange range;
|
||||
range.start_key_ = rk.get_rowkey();
|
||||
range.end_key_ = rk.get_rowkey();
|
||||
range.border_flag_.set_inclusive_start();
|
||||
range.border_flag_.set_inclusive_end();
|
||||
storage::ObStoreRowIterator* iter = NULL;
|
||||
ret = mt.scan(ctx, flag, 1001, range, columns, iter);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
const ObStoreRow* scan_row = NULL;
|
||||
ret = iter->get_next_row(scan_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_DEL_ROW, scan_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), scan_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(scan_row->row_val_.cells_, scan_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*scan_row));
|
||||
ret = iter->get_next_row(scan_row);
|
||||
EXPECT_EQ(OB_ITER_END, ret);
|
||||
mt.revert_iter(iter);
|
||||
|
||||
ObSEArray<ObStoreRowkey, 64> rowkeys;
|
||||
rowkeys.push_back(rk.get_rowkey());
|
||||
ret = mt.multi_get(ctx, flag, 1001, rowkeys, columns, iter);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
const ObStoreRow* mget_row = NULL;
|
||||
ret = iter->get_next_row(mget_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_DEL_ROW, mget_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), mget_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(mget_row->row_val_.cells_, mget_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*mget_row));
|
||||
ret = iter->get_next_row(mget_row);
|
||||
EXPECT_EQ(OB_ITER_END, ret);
|
||||
mt.revert_iter(iter);
|
||||
ctx.mem_ctx_->trans_end(true, 1000);
|
||||
f.free(ctx.mem_ctx_);
|
||||
}
|
||||
|
||||
int init_tenant_mgr()
|
||||
{
|
||||
ObTenantManager& tm = ObTenantManager::get_instance();
|
||||
ObAddr self;
|
||||
self.set_ip_addr("127.0.0.1", 8086);
|
||||
ObReqTransport req_transport(NULL, NULL);
|
||||
obrpc::ObSrvRpcProxy rpc_proxy;
|
||||
int ret = tm.init(self, rpc_proxy, &req_transport, &ObServerConfig::get_instance());
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = tm.add_tenant(OB_SYS_TENANT_ID);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
const int64_t ulmt = 16LL << 30;
|
||||
const int64_t llmt = 8LL << 30;
|
||||
ret = tm.set_tenant_mem_limit(OB_SYS_TENANT_ID, ulmt, llmt);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
return OB_SUCCESS;
|
||||
}
|
||||
|
||||
TEST(TestObMemtable, smoke_test)
|
||||
{
|
||||
static const ObPartitionKey PKEY(MT_TABLE_ID, 1, 1);
|
||||
|
||||
ObModAllocator allocator;
|
||||
ObMemtable mt;
|
||||
ObMemtable replayed_mt;
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
ret = mt.init(PKEY);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = replayed_mt.init(PKEY);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
storage::ObStoreCtx wctx;
|
||||
storage::ObStoreCtx rctx;
|
||||
|
||||
CD cd(100,
|
||||
ObVarcharType,
|
||||
CS_TYPE_UTF8MB4_GENERAL_CI,
|
||||
101,
|
||||
ObVarcharType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
102,
|
||||
ObIntType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
103,
|
||||
ObNumberType,
|
||||
CS_TYPE_UTF8MB4_BIN);
|
||||
const ObIArray<share::schema::ObColDesc>& columns = cd.get_columns();
|
||||
|
||||
ObMtRowIterator mri;
|
||||
RK rk(V("hello", 5), V(NULL, 0), I(1024), N("3.14"));
|
||||
ObStoreRow row;
|
||||
row.row_val_.cells_ = const_cast<ObObj*>(rk.get_rowkey().get_obj_ptr());
|
||||
row.row_val_.count_ = rk.get_rowkey().get_obj_cnt();
|
||||
row.set_dml(T_DML_INSERT);
|
||||
mri.add_row(row);
|
||||
|
||||
wctx.mem_ctx_ = f.alloc();
|
||||
wctx.mem_ctx_->trans_begin();
|
||||
wctx.mem_ctx_->sub_trans_begin(0, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.set(wctx, 1001, rk.get_rowkey().get_obj_cnt(), columns, mri);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
test_redo_log(*wctx.mem_ctx_, 1001, rk.get_rowkey(), T_DML_INSERT, replayed_mt, 1);
|
||||
wctx.mem_ctx_->trans_end(true, 1);
|
||||
f.free(wctx.mem_ctx_);
|
||||
|
||||
row.set_dml(T_DML_DELETE);
|
||||
mri.reset();
|
||||
mri.add_row(row);
|
||||
wctx.mem_ctx_ = f.alloc();
|
||||
wctx.mem_ctx_->trans_begin();
|
||||
wctx.mem_ctx_->sub_trans_begin(1, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.set(wctx, 1001, rk.get_rowkey().get_obj_cnt(), columns, mri);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
test_redo_log(*wctx.mem_ctx_, 1001, rk.get_rowkey(), T_DML_DELETE, replayed_mt, 2);
|
||||
wctx.mem_ctx_->trans_end(true, 2);
|
||||
f.free(wctx.mem_ctx_);
|
||||
|
||||
ObQueryFlag flag;
|
||||
const ObStoreRow* got_row = NULL;
|
||||
rctx.mem_ctx_ = f.alloc();
|
||||
rctx.mem_ctx_->trans_begin();
|
||||
rctx.mem_ctx_->sub_trans_begin(1, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.get(rctx, flag, 1001, rk.get_rowkey(), columns, got_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_ROW_EXIST, got_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), got_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(got_row->row_val_.cells_, got_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*got_row));
|
||||
mt.revert_row(got_row);
|
||||
rctx.mem_ctx_->trans_end(true, 1);
|
||||
f.free(rctx.mem_ctx_);
|
||||
|
||||
rctx.mem_ctx_ = f.alloc();
|
||||
rctx.mem_ctx_->trans_begin();
|
||||
rctx.mem_ctx_->sub_trans_begin(0, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.get(rctx, flag, 1001, rk.get_rowkey(), columns, got_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_ROW_DOES_NOT_EXIST, got_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), got_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(got_row->row_val_.cells_, got_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*got_row));
|
||||
mt.revert_row(got_row);
|
||||
rctx.mem_ctx_->trans_end(true, 0);
|
||||
f.free(rctx.mem_ctx_);
|
||||
|
||||
test_mt_query(rk, columns, mt);
|
||||
test_mt_query(rk, columns, replayed_mt);
|
||||
|
||||
replayed_mt.destroy();
|
||||
mt.destroy();
|
||||
}
|
||||
|
||||
TEST(TestObMemtable, rowpendingtest)
|
||||
{
|
||||
static const ObPartitionKey PKEY(MT_TABLE_ID, 1, 1);
|
||||
|
||||
ObModAllocator allocator;
|
||||
ObMemtable mt;
|
||||
ObMemtable replayed_mt;
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
ret = mt.init(PKEY);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = replayed_mt.init(PKEY);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
storage::ObStoreCtx wctx;
|
||||
storage::ObStoreCtx rctx;
|
||||
|
||||
CD cd(100,
|
||||
ObVarcharType,
|
||||
CS_TYPE_UTF8MB4_GENERAL_CI,
|
||||
101,
|
||||
ObVarcharType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
102,
|
||||
ObIntType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
103,
|
||||
ObNumberType,
|
||||
CS_TYPE_UTF8MB4_BIN);
|
||||
const ObIArray<share::schema::ObColDesc>& columns = cd.get_columns();
|
||||
|
||||
ObMtRowIterator mri;
|
||||
RK rk(V("hello", 5), V(NULL, 0), I(1024), N("3.14"));
|
||||
ObStoreRow row;
|
||||
row.row_val_.cells_ = const_cast<ObObj*>(rk.get_rowkey().get_obj_ptr());
|
||||
row.row_val_.count_ = rk.get_rowkey().get_obj_cnt();
|
||||
row.set_dml(T_DML_INSERT);
|
||||
mri.add_row(row);
|
||||
|
||||
wctx.mem_ctx_ = f.alloc();
|
||||
wctx.mem_ctx_->trans_begin();
|
||||
wctx.mem_ctx_->sub_trans_begin(0, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.set(wctx, 1001, rk.get_rowkey().get_obj_cnt(), columns, mri);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
test_redo_log_row_pending(*wctx.mem_ctx_, 1001, rk.get_rowkey(), T_DML_INSERT, replayed_mt, 1);
|
||||
// Commit to replay.
|
||||
ret = wctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = wctx.mem_ctx_->replay_to_commit();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = wctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
// Replay end.
|
||||
wctx.mem_ctx_->trans_replay_end(true, 1);
|
||||
f.free(wctx.mem_ctx_);
|
||||
|
||||
row.set_dml(T_DML_UPDATE);
|
||||
mri.reset();
|
||||
mri.add_row(row);
|
||||
wctx.mem_ctx_ = f.alloc();
|
||||
wctx.mem_ctx_->trans_begin();
|
||||
wctx.mem_ctx_->sub_trans_begin(1, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.set(wctx, 1001, rk.get_rowkey().get_obj_cnt(), columns, mri);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
test_redo_log_row_pending(*wctx.mem_ctx_, 1001, rk.get_rowkey(), T_DML_UPDATE, replayed_mt, 2);
|
||||
// Commit to replay.
|
||||
ret = wctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = wctx.mem_ctx_->replay_to_commit();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = wctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
// Replay end.
|
||||
wctx.mem_ctx_->trans_replay_end(true, 2);
|
||||
f.free(wctx.mem_ctx_);
|
||||
|
||||
row.set_dml(T_DML_DELETE);
|
||||
mri.reset();
|
||||
mri.add_row(row);
|
||||
wctx.mem_ctx_ = f.alloc();
|
||||
wctx.mem_ctx_->trans_begin();
|
||||
wctx.mem_ctx_->sub_trans_begin(2, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.set(wctx, 1001, rk.get_rowkey().get_obj_cnt(), columns, mri);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
test_redo_log_row_pending(*wctx.mem_ctx_, 1001, rk.get_rowkey(), T_DML_DELETE, replayed_mt, 3);
|
||||
// Commit to replay.
|
||||
ret = wctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = wctx.mem_ctx_->replay_to_commit();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = wctx.mem_ctx_->commit_to_replay();
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
// Replay end.
|
||||
wctx.mem_ctx_->trans_replay_end(true, 3);
|
||||
f.free(wctx.mem_ctx_);
|
||||
|
||||
ObQueryFlag flag;
|
||||
const ObStoreRow* got_row = NULL;
|
||||
rctx.mem_ctx_ = f.alloc();
|
||||
rctx.mem_ctx_->trans_begin();
|
||||
rctx.mem_ctx_->sub_trans_begin(1, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.get(rctx, flag, 1001, rk.get_rowkey(), columns, got_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_ROW_EXIST, got_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), got_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(got_row->row_val_.cells_, got_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*got_row));
|
||||
mt.revert_row(got_row);
|
||||
rctx.mem_ctx_->trans_end(true, 1);
|
||||
f.free(rctx.mem_ctx_);
|
||||
|
||||
rctx.mem_ctx_ = f.alloc();
|
||||
rctx.mem_ctx_->trans_begin();
|
||||
rctx.mem_ctx_->sub_trans_begin(0, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
ret = mt.get(rctx, flag, 1001, rk.get_rowkey(), columns, got_row);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(+ObActionFlag::OP_ROW_DOES_NOT_EXIST, got_row->flag_);
|
||||
EXPECT_EQ(rk.get_rowkey().get_obj_cnt(), got_row->row_val_.count_);
|
||||
EXPECT_EQ(rk.get_rowkey(), ObStoreRowkey(got_row->row_val_.cells_, got_row->row_val_.count_));
|
||||
fprintf(stdout, "%s\n", to_cstring(*got_row));
|
||||
mt.revert_row(got_row);
|
||||
rctx.mem_ctx_->trans_end(true, 0);
|
||||
f.free(rctx.mem_ctx_);
|
||||
|
||||
test_mt_query(rk, columns, mt);
|
||||
test_mt_query(rk, columns, replayed_mt);
|
||||
|
||||
replayed_mt.destroy();
|
||||
mt.destroy();
|
||||
}
|
||||
|
||||
void print_storerow(const ObStoreRow* got_row)
|
||||
{
|
||||
static const int64_t BUF_LEN = 16 * 1024;
|
||||
char buf[BUF_LEN];
|
||||
got_row->to_string(buf, BUF_LEN);
|
||||
fprintf(stdout, "Print StoreRow: %s\n", buf);
|
||||
}
|
||||
|
||||
void print_compare_obj(ObObj obj1, ObObj obj2)
|
||||
{
|
||||
static const int64_t PRINT_BUF = 1024;
|
||||
char print_buf[PRINT_BUF];
|
||||
int64_t pos = 0;
|
||||
obj1.print_sql_literal(print_buf, PRINT_BUF, pos);
|
||||
sprintf(print_buf + pos, " == ");
|
||||
pos += 4;
|
||||
obj2.print_sql_literal(print_buf, PRINT_BUF, pos);
|
||||
fprintf(stdout, " COMPARE(%s)COMPARE \n", print_buf);
|
||||
TRANS_LOG(DEBUG, "obj2 = ", K(obj2.get_type()));
|
||||
}
|
||||
|
||||
bool is_row_finsih(const ObObj* value)
|
||||
{
|
||||
return (value->get_type() == ObExtendType) && (value->get_ext() == ObActionFlag::OP_END_FLAG);
|
||||
}
|
||||
|
||||
void test_aborted_sub_trans_redo_node(const char* input_str, int64_t rowkey_len, int64_t total,
|
||||
int64_t abort_idx) // TODO change abort_idx to abort_idx_array
|
||||
{
|
||||
// prepare test input data: ObStoreRowIterator, ObSEArray<ColDesc> columns.
|
||||
int64_t ret = OB_SUCCESS;
|
||||
|
||||
CD cd(1,
|
||||
ObIntType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
2,
|
||||
ObIntType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
3,
|
||||
ObVarcharType,
|
||||
CS_TYPE_UTF8MB4_BIN,
|
||||
4,
|
||||
ObVarcharType,
|
||||
CS_TYPE_UTF8MB4_BIN);
|
||||
const ObIArray<share::schema::ObColDesc>& columns = cd.get_columns();
|
||||
|
||||
ObMockIterator insert_iter;
|
||||
ASSERT_EQ(OB_SUCCESS, insert_iter.from(input_str));
|
||||
ObMockIterator read_iter;
|
||||
ASSERT_EQ(OB_SUCCESS, read_iter.from(input_str));
|
||||
// input data OK.
|
||||
|
||||
TRANS_LOG(DEBUG, "input data prepare ok");
|
||||
static const ObPartitionKey PKEY(MT_TABLE_ID, 1, 1);
|
||||
uint64_t index_id = 1001;
|
||||
ObModAllocator allocator;
|
||||
ObQueryFlag flag;
|
||||
ObMemtable mt;
|
||||
ObStoreRow* store_row = NULL;
|
||||
ret = mt.init(PKEY);
|
||||
ASSERT_EQ(ret, OB_SUCCESS);
|
||||
ObStoreCtx insert_ctx;
|
||||
insert_ctx.mem_ctx_ = f.alloc();
|
||||
|
||||
// Transaction 1: insert 5 rows.
|
||||
insert_ctx.mem_ctx_->trans_begin();
|
||||
|
||||
for (int64_t i = 1; i <= total; i++) {
|
||||
TRANS_LOG(DEBUG, "insert row", K(i));
|
||||
insert_ctx.mem_ctx_->sub_trans_begin(0, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
insert_iter.get_next_row(store_row);
|
||||
store_row->set_dml(T_DML_INSERT);
|
||||
ret = mt.set(insert_ctx, index_id, rowkey_len, columns, *store_row);
|
||||
EXPECT_TRUE(OB_SUCC(ret));
|
||||
if (i == abort_idx) {
|
||||
TRANS_LOG(DEBUG, "abort row", K(abort_idx));
|
||||
insert_ctx.mem_ctx_->sub_trans_abort();
|
||||
}
|
||||
}
|
||||
|
||||
// get redo log buffer from MemCtx. Then parse the buffer and check.
|
||||
ObMemtableMutatorIterator mmi;
|
||||
ObArenaAllocator allocator2(0);
|
||||
const int64_t REDO_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
char* redo_log_buffer = new char[REDO_BUFFER_SIZE];
|
||||
int64_t data_len = 0;
|
||||
int64_t pos = 0;
|
||||
ret = insert_ctx.mem_ctx_->fill_redo_log(redo_log_buffer, REDO_BUFFER_SIZE, data_len);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = mmi.deserialize(redo_log_buffer, data_len, pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
insert_ctx.mem_ctx_->trans_end(true, 1);
|
||||
|
||||
// check redo log buffer.
|
||||
uint64_t res_index_id = 0;
|
||||
ObStoreRowkey res_rowkey;
|
||||
int64_t schema_version = 0;
|
||||
ObRowData new_row;
|
||||
storage::ObRowDml res_dml_type = T_DML_UNKNOWN;
|
||||
uint32_t res_modify_count = 0;
|
||||
|
||||
ObMockIterator check_iter;
|
||||
ASSERT_EQ(OB_SUCCESS, check_iter.from(input_str));
|
||||
ObStoreRow* check_row = NULL;
|
||||
|
||||
for (int64_t i = 1; i <= total; i++) { // for each row
|
||||
ret = check_iter.get_next_row(check_row);
|
||||
EXPECT_EQ(ret, OB_SUCCESS);
|
||||
if (i != abort_idx) {
|
||||
ret = mmi.get_next_row(res_index_id, res_rowkey, schema_version, new_row, res_dml_type, res_modify_count);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(index_id, res_index_id);
|
||||
EXPECT_EQ(res_rowkey, ObStoreRowkey(check_row->row_val_.cells_, rowkey_len));
|
||||
EXPECT_EQ(res_dml_type, T_DML_INSERT);
|
||||
// ObCompactCellIterator cci;
|
||||
// ret = cci.init(data, SPARSE);
|
||||
ObCellReader cell_reader;
|
||||
ret = cell_reader.init(new_row.data_, new_row.size_, SPARSE);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
// check trans_node
|
||||
uint64_t column_id = OB_INVALID_ID;
|
||||
const ObObj* value = NULL;
|
||||
ObObj* value_array = check_row->row_val_.cells_;
|
||||
int64_t value_count = check_row->row_val_.count_;
|
||||
int64_t value_array_index = 0;
|
||||
int64_t iter_ret = OB_SUCCESS;
|
||||
|
||||
while (OB_SUCCESS == (iter_ret = cell_reader.next_cell()) && value_array_index < value_count) {
|
||||
ret = cell_reader.get_cell(column_id, value);
|
||||
EXPECT_FALSE(is_row_finsih(value));
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
print_compare_obj(value_array[value_array_index], *value);
|
||||
EXPECT_TRUE(value_array[value_array_index] == *value);
|
||||
value_array_index++;
|
||||
}
|
||||
cell_reader.get_cell(value);
|
||||
EXPECT_TRUE(is_row_finsih(value));
|
||||
TRANS_LOG(DEBUG, "***********************************");
|
||||
TRANS_LOG(DEBUG, " ");
|
||||
}
|
||||
}
|
||||
|
||||
f.free(insert_ctx.mem_ctx_);
|
||||
|
||||
// Transaction 2 : read 5 rows
|
||||
ObStoreCtx read_ctx;
|
||||
read_ctx.mem_ctx_ = f.alloc();
|
||||
read_ctx.mem_ctx_->trans_begin();
|
||||
|
||||
// read 1
|
||||
const oceanbase::storage::ObStoreRow* got_row = NULL;
|
||||
|
||||
for (int64_t i = 1; i <= total; i++) {
|
||||
TRANS_LOG(DEBUG, "read r1");
|
||||
read_ctx.mem_ctx_->sub_trans_begin(1, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
read_iter.get_next_row(store_row);
|
||||
ret = mt.get(read_ctx, flag, index_id, ObStoreRowkey(store_row->row_val_.cells_, rowkey_len), columns, got_row);
|
||||
print_storerow(got_row);
|
||||
if (i == abort_idx) {
|
||||
EXPECT_EQ(+ObActionFlag::OP_ROW_DOES_NOT_EXIST, got_row->flag_);
|
||||
} else {
|
||||
EXPECT_EQ(+ObActionFlag::OP_ROW_EXIST, got_row->flag_);
|
||||
}
|
||||
mt.revert_row(got_row);
|
||||
}
|
||||
|
||||
read_ctx.mem_ctx_->trans_end(true, 1);
|
||||
f.free(read_ctx.mem_ctx_);
|
||||
|
||||
mt.destroy();
|
||||
free(redo_log_buffer);
|
||||
}
|
||||
|
||||
TEST(TestMemtable, aborted_sub_trans_redo_node_different_row)
|
||||
{
|
||||
const char* input_str = "bigint bigint var var \n"
|
||||
"1 101 songjiang jishiyu \n"
|
||||
"2 102 lujunyi yuqilin \n"
|
||||
"3 103 wuyong zhiduoxing\n"
|
||||
"4 104 gongsunsheng ruyunlong \n"
|
||||
"5 105 guansheng dadao \n";
|
||||
/*
|
||||
const char *input_str =
|
||||
"int int int int \n"
|
||||
"1 401 501 601 \n"
|
||||
"2 402 502 602 \n"
|
||||
"3 403 503 603 \n"
|
||||
"4 404 504 604 \n"
|
||||
"5 405 505 605 \n";
|
||||
int64_t total = 5;
|
||||
int64_t rowkey_len = 1;
|
||||
for(int64_t abort_idx = 1; abort_idx <= total; abort_idx++) {
|
||||
test_aborted_sub_trans_redo_node(input_str, rowkey_len, total, abort_idx);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
*/
|
||||
int64_t abort_idx = 4;
|
||||
int64_t total = 5;
|
||||
int64_t rowkey_len = 1;
|
||||
test_aborted_sub_trans_redo_node(input_str, rowkey_len, total, abort_idx);
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
void test_aborted_sub_trans_redo_node_same_row(
|
||||
const char* input_str_update_same_row, int64_t rowkey_len, int64_t total, int64_t abort_idx)
|
||||
{
|
||||
int64_t ret = OB_SUCCESS;
|
||||
ObSEArray<share::schema::ObColDesc, 64> columns;
|
||||
share::schema::ObColDesc col_desc;
|
||||
col_desc.col_id_ = 1;
|
||||
col_desc.col_type_.set_type(ObIntType);
|
||||
col_desc.col_type_.set_collation_type(CS_TYPE_UTF8MB4_BIN);
|
||||
columns.push_back(col_desc);
|
||||
|
||||
col_desc.col_id_ = 2;
|
||||
columns.push_back(col_desc);
|
||||
|
||||
col_desc.col_id_ = 3;
|
||||
col_desc.col_type_.set_type(ObVarcharType);
|
||||
columns.push_back(col_desc);
|
||||
|
||||
col_desc.col_id_ = 4;
|
||||
columns.push_back(col_desc);
|
||||
|
||||
ObMockIterator update_iter;
|
||||
ASSERT_EQ(OB_SUCCESS, update_iter.from(input_str_update_same_row));
|
||||
ObMockIterator read_iter;
|
||||
ASSERT_EQ(OB_SUCCESS, read_iter.from(input_str_update_same_row));
|
||||
// --- input data OK.
|
||||
|
||||
// Transaction 1: update same row 5 times.
|
||||
TRANS_LOG(DEBUG, "start update");
|
||||
static const ObPartitionKey PKEY(MT_TABLE_ID, 1, 1);
|
||||
uint64_t index_id = 1001;
|
||||
ObModAllocator allocator;
|
||||
ObQueryFlag flag;
|
||||
ObMemtable mt;
|
||||
ObStoreRow* store_row = NULL;
|
||||
|
||||
ret = mt.init(PKEY);
|
||||
ASSERT_EQ(ret, OB_SUCCESS);
|
||||
ObStoreCtx update_ctx;
|
||||
update_ctx.mem_ctx_ = f.alloc();
|
||||
|
||||
// Transaction 1: insert 5 rows.
|
||||
update_ctx.mem_ctx_->trans_begin();
|
||||
|
||||
for (int64_t i = 1; i <= total; i++) {
|
||||
TRANS_LOG(DEBUG, "update row", K(i));
|
||||
update_ctx.mem_ctx_->sub_trans_begin(0, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
update_iter.get_next_row(store_row);
|
||||
if (i == 1) {
|
||||
store_row->set_dml(T_DML_INSERT);
|
||||
} else {
|
||||
store_row->set_dml(T_DML_UPDATE);
|
||||
}
|
||||
ret = mt.set(update_ctx, index_id, rowkey_len, columns, *store_row);
|
||||
EXPECT_TRUE(OB_SUCC(ret));
|
||||
if (i == abort_idx) {
|
||||
TRANS_LOG(DEBUG, "abort row", K(abort_idx));
|
||||
update_ctx.mem_ctx_->sub_trans_abort();
|
||||
}
|
||||
}
|
||||
|
||||
// get redo log buffer from MemCtx. Then parse the buffer and check.
|
||||
ObMemtableMutatorIterator mmi;
|
||||
ObArenaAllocator allocator2(0);
|
||||
const int64_t REDO_BUFFER_SIZE = 2 * 1024 * 1024;
|
||||
char* redo_log_buffer = new char[REDO_BUFFER_SIZE];
|
||||
int64_t data_len = 0;
|
||||
int64_t pos = 0;
|
||||
ret = update_ctx.mem_ctx_->fill_redo_log(redo_log_buffer, REDO_BUFFER_SIZE, data_len);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
ret = mmi.deserialize(redo_log_buffer, data_len, pos);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
update_ctx.mem_ctx_->trans_end(true, 1);
|
||||
// check redo log buffer.
|
||||
uint64_t res_index_id = 0;
|
||||
ObStoreRowkey res_rowkey;
|
||||
int64_t schema_version = 0;
|
||||
ObRowData new_row;
|
||||
storage::ObRowDml res_dml_type = T_DML_UNKNOWN;
|
||||
uint32_t res_modify_count = 0;
|
||||
|
||||
ObMockIterator check_iter;
|
||||
ASSERT_EQ(OB_SUCCESS, check_iter.from(input_str_update_same_row));
|
||||
ObStoreRow* check_row = NULL;
|
||||
|
||||
for (int64_t i = 1; i <= total; i++) { // for each sub_trans
|
||||
ret = check_iter.get_next_row(check_row);
|
||||
EXPECT_EQ(ret, OB_SUCCESS);
|
||||
if (i != abort_idx) {
|
||||
ret = mmi.get_next_row(res_index_id, res_rowkey, schema_version, new_row, res_dml_type, res_modify_count);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_EQ(index_id, res_index_id);
|
||||
EXPECT_EQ(res_rowkey, ObStoreRowkey(check_row->row_val_.cells_, rowkey_len));
|
||||
if (i == 1) {
|
||||
EXPECT_EQ(res_dml_type, T_DML_INSERT);
|
||||
} else {
|
||||
EXPECT_EQ(res_dml_type, T_DML_UPDATE);
|
||||
}
|
||||
|
||||
ObCellReader cell_reader;
|
||||
ret = cell_reader.init(new_row.data_, new_row.size_, SPARSE);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
|
||||
// check trans_node
|
||||
uint64_t column_id = OB_INVALID_ID;
|
||||
const ObObj* value = NULL;
|
||||
ObObj* value_array = check_row->row_val_.cells_;
|
||||
int64_t value_count = check_row->row_val_.count_;
|
||||
int64_t value_array_index = 0;
|
||||
int64_t iter_ret = OB_SUCCESS;
|
||||
|
||||
while (OB_SUCCESS == (iter_ret = cell_reader.next_cell()) && value_array_index < value_count) {
|
||||
ret = cell_reader.get_cell(column_id, value);
|
||||
EXPECT_EQ(OB_SUCCESS, ret);
|
||||
EXPECT_FALSE(is_row_finsih(value));
|
||||
print_compare_obj(value_array[value_array_index], *value);
|
||||
EXPECT_TRUE(value_array[value_array_index] == *value);
|
||||
value_array_index++;
|
||||
}
|
||||
cell_reader.get_cell(value);
|
||||
EXPECT_TRUE(is_row_finsih(value));
|
||||
TRANS_LOG(DEBUG, "***********************************");
|
||||
TRANS_LOG(DEBUG, " ");
|
||||
}
|
||||
}
|
||||
|
||||
f.free(update_ctx.mem_ctx_);
|
||||
|
||||
// read 1
|
||||
const oceanbase::storage::ObStoreRow* got_row = NULL;
|
||||
ObStoreCtx read_ctx;
|
||||
read_ctx.mem_ctx_ = f.alloc();
|
||||
read_ctx.mem_ctx_->trans_begin();
|
||||
TRANS_LOG(DEBUG, "read ");
|
||||
read_ctx.mem_ctx_->sub_trans_begin(1, 1000000 + ::oceanbase::common::ObTimeUtility::current_time());
|
||||
read_iter.get_next_row(store_row);
|
||||
ret = mt.get(read_ctx, flag, index_id, ObStoreRowkey(store_row->row_val_.cells_, rowkey_len), columns, got_row);
|
||||
TRANS_LOG(DEBUG, "print got_row start ***************");
|
||||
print_storerow(got_row);
|
||||
TRANS_LOG(DEBUG, "print got_row end ***************");
|
||||
mt.revert_row(got_row);
|
||||
|
||||
read_ctx.mem_ctx_->trans_end(true, 1);
|
||||
f.free(read_ctx.mem_ctx_);
|
||||
|
||||
mt.destroy();
|
||||
free(redo_log_buffer);
|
||||
}
|
||||
|
||||
TEST(TestMemtable, aborted_sub_trans_redo_node_same_row)
|
||||
{
|
||||
const char* input_str_update_same_row = "bigint bigint var var \n"
|
||||
"1 1 songjiang jishiyu \n"
|
||||
"1 2 lujunyi yuqilin \n"
|
||||
"1 3 wuyong zhiduoxing\n"
|
||||
"1 4 gongsunsheng ruyunlong \n"
|
||||
"1 5 guansheng dadao \n";
|
||||
|
||||
int64_t total = 5;
|
||||
int64_t rowkey_len = 1;
|
||||
int64_t abort_idx = 5;
|
||||
fprintf(stdout, "start test same row\n");
|
||||
test_aborted_sub_trans_redo_node_same_row(input_str_update_same_row, rowkey_len, total, abort_idx);
|
||||
fprintf(stdout, "end test same row\n");
|
||||
}
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// oceanbase::common::ObLogger::get_logger().set_file_name("test_memtable.log", true);
|
||||
oceanbase::common::ObLogger::get_logger().set_log_level("DEBUG");
|
||||
OB_LOGGER.set_file_name("test_memtable.log", true);
|
||||
OB_LOGGER.set_log_level("DEBUG");
|
||||
oceanbase::unittest::init_tenant_mgr();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
135
unittest/storage/memtable/utils_mock_ctx.h
Normal file
135
unittest/storage/memtable/utils_mock_ctx.h
Normal file
@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OCEANBASE_UNITTEST_MEMTABLE_MOCK_CTX_H_
|
||||
#define OCEANBASE_UNITTEST_MEMTABLE_MOCK_CTX_H_
|
||||
|
||||
#include "storage/memtable/ob_memtable_interface.h"
|
||||
#include "lib/allocator/page_arena.h"
|
||||
#include "common/object/ob_object.h"
|
||||
#include "common/rowkey/ob_rowkey.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::memtable;
|
||||
|
||||
struct ObMtCtxMembers {
|
||||
char trace_log_buffer_[65536];
|
||||
int64_t tlb_pos_;
|
||||
CharArena allocator_;
|
||||
ObSEArray<ObIMvccCallback*, 64> scbs_;
|
||||
ObSEArray<ObIMvccCallback*, 64> tcbs_;
|
||||
};
|
||||
|
||||
class ObMtCtx : public ObIMemtableCtx, public ObMtCtxMembers {
|
||||
public:
|
||||
ObMtCtx()
|
||||
{}
|
||||
~ObMtCtx()
|
||||
{}
|
||||
|
||||
public:
|
||||
void fill_trace_log(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
tlb_pos_ += vsnprintf(&trace_log_buffer_[tlb_pos_], sizeof(trace_log_buffer_) - tlb_pos_, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
void* alloc(const int64_t size)
|
||||
{
|
||||
return allocator_.alloc(size);
|
||||
}
|
||||
int register_sub_callback(ObIMvccCallback* callback)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
scbs_.push_back(callback);
|
||||
return ret;
|
||||
}
|
||||
int register_end_callback(ObIMvccCallback* callback)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
tcbs_.push_back(callback);
|
||||
return ret;
|
||||
}
|
||||
int fill_redo_log(const uint64_t table_id, const uint64_t index_id, const int64_t rowkey_len,
|
||||
const common::ObIArray<uint64_t>& column_ids, const storage::ObStoreRow& row)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(table_id);
|
||||
UNUSED(index_id);
|
||||
UNUSED(rowkey_len);
|
||||
UNUSED(column_ids);
|
||||
UNUSED(row);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public:
|
||||
int trans_begin()
|
||||
{
|
||||
return OB_SUCCESS;
|
||||
}
|
||||
int sub_trans_begin(const int64_t snapshot, const int64_t abs_expired_time)
|
||||
{
|
||||
assert(0 == scbs_.count());
|
||||
set_base_snapshot(INT64_MAX);
|
||||
set_start_snapshot(0);
|
||||
set_stop_snapshot(snapshot);
|
||||
set_abs_expired_time(abs_expired_time);
|
||||
return OB_SUCCESS;
|
||||
}
|
||||
void sub_trans_abort()
|
||||
{
|
||||
for (int64_t i = scbs_.count() - 1; i >= 0; i--) {
|
||||
if (NULL != scbs_.at(i)) {
|
||||
scbs_.at(i)->callback(false);
|
||||
}
|
||||
}
|
||||
scbs_.reset();
|
||||
}
|
||||
void trans_end(const bool commit, const int64_t redo_log_id)
|
||||
{
|
||||
set_redo_log_id(redo_log_id);
|
||||
for (int64_t i = scbs_.count() - 1; i >= 0; i--) {
|
||||
if (NULL != scbs_.at(i)) {
|
||||
scbs_.at(i)->callback(commit);
|
||||
}
|
||||
}
|
||||
scbs_.reset();
|
||||
for (int64_t i = tcbs_.count() - 1; i >= 0; i--) {
|
||||
if (NULL != tcbs_.at(i)) {
|
||||
tcbs_.at(i)->callback(commit);
|
||||
}
|
||||
}
|
||||
tcbs_.reset();
|
||||
}
|
||||
void trans_publish()
|
||||
{}
|
||||
void replay_end(const bool commit, const int64_t redo_log_id)
|
||||
{
|
||||
UNUSED(commit);
|
||||
UNUSED(redo_log_id);
|
||||
}
|
||||
int fill_redo_log(char* buf, const int64_t buf_len, const int64_t& buf_pos)
|
||||
{
|
||||
UNUSED(buf);
|
||||
UNUSED(buf_len);
|
||||
UNUSED(buf_pos);
|
||||
return OB_SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
#endif // OCEANBASE_UNITTEST_MEMTABLE_MOCK_CTX_H_
|
||||
66
unittest/storage/memtable/utils_mock_row.h
Normal file
66
unittest/storage/memtable/utils_mock_row.h
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OCEANBASE_UNITTEST_MEMTABLE_MOCK_ROW_H_
|
||||
#define OCEANBASE_UNITTEST_MEMTABLE_MOCK_ROW_H_
|
||||
|
||||
#include "storage/memtable/ob_memtable_interface.h"
|
||||
#include "storage/ob_i_store.h"
|
||||
#include "lib/allocator/page_arena.h"
|
||||
#include "common/object/ob_object.h"
|
||||
#include "common/rowkey/ob_rowkey.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
using namespace oceanbase::memtable;
|
||||
using namespace oceanbase::storage;
|
||||
|
||||
class ObMtRowIterator : public ObStoreRowIterator {
|
||||
public:
|
||||
ObMtRowIterator() : cursor_(0)
|
||||
{}
|
||||
~ObMtRowIterator()
|
||||
{}
|
||||
int get_next_row(const ObStoreRow*& row)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (cursor_ < rows_.count()) {
|
||||
row = &rows_[cursor_++];
|
||||
} else {
|
||||
ret = OB_ITER_END;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
cursor_ = 0;
|
||||
rows_.reset();
|
||||
}
|
||||
void reset_iter()
|
||||
{
|
||||
cursor_ = 0;
|
||||
}
|
||||
void add_row(const ObStoreRow& row)
|
||||
{
|
||||
rows_.push_back(row);
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t cursor_;
|
||||
ObSEArray<ObStoreRow, 64> rows_;
|
||||
};
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
#endif // OCEANBASE_UNITTEST_MEMTABLE_MOCK_ROW_H_
|
||||
34
unittest/storage/memtable/utils_mod_allocator.h
Normal file
34
unittest/storage/memtable/utils_mod_allocator.h
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OCEANBASE_UNITTEST_MEMTABLE_MOD_ALLOCATOR_H_
|
||||
#define OCEANBASE_UNITTEST_MEMTABLE_MOD_ALLOCATOR_H_
|
||||
|
||||
#include "lib/allocator/page_arena.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
|
||||
class ObModAllocator : public DefaultPageAllocator {
|
||||
public:
|
||||
void* mod_alloc(const int64_t size, const char* label)
|
||||
{
|
||||
set_label(label);
|
||||
return alloc(size);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
#endif // OCEANBASE_UNITTEST_MEMTABLE_MOD_ALLOCATOR_H_
|
||||
280
unittest/storage/memtable/utils_rowkey_builder.h
Normal file
280
unittest/storage/memtable/utils_rowkey_builder.h
Normal file
@ -0,0 +1,280 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OCEANBASE_UNITTEST_MEMTABLE_ROWKEY_BUILDER_H_
|
||||
#define OCEANBASE_UNITTEST_MEMTABLE_ROWKEY_BUILDER_H_
|
||||
|
||||
#include "lib/allocator/page_arena.h"
|
||||
#include "lib/container/ob_iarray.h"
|
||||
#include "lib/container/ob_se_array.h"
|
||||
#include "common/object/ob_object.h"
|
||||
#include "common/rowkey/ob_store_rowkey.h"
|
||||
#include "share/schema/ob_table_schema.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace unittest {
|
||||
using namespace oceanbase::common;
|
||||
|
||||
class ObStoreRowkeyWrapper {
|
||||
public:
|
||||
template <class... Args>
|
||||
ObStoreRowkeyWrapper(const Args&... args) : obj_cnt_(0)
|
||||
{
|
||||
fill(args...);
|
||||
}
|
||||
ObStoreRowkeyWrapper(const uint8_t obj_cnt) : obj_cnt_(obj_cnt)
|
||||
{}
|
||||
ObStoreRowkeyWrapper(const int32_t obj_cnt) : obj_cnt_(obj_cnt)
|
||||
{}
|
||||
~ObStoreRowkeyWrapper()
|
||||
{}
|
||||
|
||||
public:
|
||||
CharArena& get_allocator()
|
||||
{
|
||||
return allocator_;
|
||||
}
|
||||
ObObj& get_cur_obj()
|
||||
{
|
||||
return objs_[obj_cnt_];
|
||||
}
|
||||
ObObj& get_obj(int64_t idx)
|
||||
{
|
||||
return objs_[idx];
|
||||
}
|
||||
int64_t get_obj_cnt() const
|
||||
{
|
||||
return obj_cnt_;
|
||||
}
|
||||
void add_obj()
|
||||
{
|
||||
obj_cnt_++;
|
||||
}
|
||||
ObStoreRowkey& get_rowkey()
|
||||
{
|
||||
return rowkey_ = ObStoreRowkey(objs_, obj_cnt_);
|
||||
}
|
||||
const ObStoreRowkey& get_rowkey() const
|
||||
{
|
||||
return rowkey_ = ObStoreRowkey(objs_, obj_cnt_);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T, class... Args>
|
||||
void fill(const T& head, const Args&... args)
|
||||
{
|
||||
head.build(*this);
|
||||
fill(args...);
|
||||
}
|
||||
void fill()
|
||||
{ /*for recursion exit*/
|
||||
}
|
||||
|
||||
private:
|
||||
CharArena allocator_;
|
||||
mutable ObObj objs_[OB_MAX_ROWKEY_COLUMN_NUMBER];
|
||||
int64_t obj_cnt_;
|
||||
mutable ObStoreRowkey rowkey_;
|
||||
};
|
||||
|
||||
class ObColumnDesc {
|
||||
public:
|
||||
template <class... Args>
|
||||
ObColumnDesc(const Args&... args)
|
||||
{
|
||||
fill(args...);
|
||||
}
|
||||
const ObIArray<share::schema::ObColDesc>& get_columns() const
|
||||
{
|
||||
return columns_;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class... Args>
|
||||
void fill(const uint64_t col_id, const ObObjType col_type, const ObCollationType col_collation, const Args&... args)
|
||||
{
|
||||
share::schema::ObColDesc col_desc;
|
||||
col_desc.col_id_ = col_id;
|
||||
col_desc.col_type_.set_type(col_type);
|
||||
col_desc.col_type_.set_collation_type(col_collation);
|
||||
columns_.push_back(col_desc);
|
||||
fill(args...);
|
||||
}
|
||||
void fill()
|
||||
{ /*for recursion exit*/
|
||||
}
|
||||
|
||||
private:
|
||||
ObSEArray<share::schema::ObColDesc, 64> columns_;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define DEFINE_TYPE_OBJ(classname, ctype, obtype) \
|
||||
class classname { \
|
||||
public: \
|
||||
classname(const ctype v) : v_(v) \
|
||||
{} \
|
||||
~classname() \
|
||||
{} \
|
||||
\
|
||||
public: \
|
||||
void build(ObStoreRowkeyWrapper& rowkey_wrapper) const \
|
||||
{ \
|
||||
rowkey_wrapper.get_cur_obj().set_##obtype(v_); \
|
||||
rowkey_wrapper.add_obj(); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
const ctype v_; \
|
||||
};
|
||||
|
||||
#define DEFINE_CHARTYPE_OBJ(classname, obtype, cltype) \
|
||||
class classname { \
|
||||
public: \
|
||||
classname(const char* str, const int64_t len, const ObCollationType cltype = CS_TYPE_UTF8MB4_BIN) \
|
||||
: str_(str), len_(len), cltype_(cltype) \
|
||||
{} \
|
||||
~classname() \
|
||||
{} \
|
||||
\
|
||||
public: \
|
||||
void build(ObStoreRowkeyWrapper& rowkey_wrapper) const \
|
||||
{ \
|
||||
ObString obstr; \
|
||||
obstr.assign_ptr(const_cast<char*>(str_), static_cast<int32_t>(len_)); \
|
||||
rowkey_wrapper.get_cur_obj().set_##obtype(obstr); \
|
||||
rowkey_wrapper.get_cur_obj().set_collation_type(cltype_); \
|
||||
rowkey_wrapper.add_obj(); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
const char* str_; \
|
||||
const int64_t len_; \
|
||||
const ObCollationType cltype_; \
|
||||
};
|
||||
|
||||
#define DEFINE_NMBTYPE_OBJ(classname, obtype) \
|
||||
class classname { \
|
||||
public: \
|
||||
classname(const char* str) : str_(str) \
|
||||
{} \
|
||||
~classname() \
|
||||
{} \
|
||||
\
|
||||
public: \
|
||||
void build(ObStoreRowkeyWrapper& rowkey_wrapper) const \
|
||||
{ \
|
||||
number::ObNumber obnmb; \
|
||||
obnmb.from(str_, rowkey_wrapper.get_allocator()); \
|
||||
rowkey_wrapper.get_cur_obj().set_##obtype(obnmb); \
|
||||
rowkey_wrapper.add_obj(); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
const char* str_; \
|
||||
};
|
||||
|
||||
class U {
|
||||
public:
|
||||
U()
|
||||
{}
|
||||
~U()
|
||||
{}
|
||||
|
||||
public:
|
||||
void build(ObStoreRowkeyWrapper& rowkey_wrapper) const
|
||||
{
|
||||
rowkey_wrapper.get_cur_obj().set_null();
|
||||
rowkey_wrapper.add_obj();
|
||||
}
|
||||
};
|
||||
|
||||
class OBMIN {
|
||||
public:
|
||||
OBMIN()
|
||||
{}
|
||||
~OBMIN()
|
||||
{}
|
||||
|
||||
public:
|
||||
void build(ObStoreRowkeyWrapper& rowkey_wrapper) const
|
||||
{
|
||||
rowkey_wrapper.get_cur_obj().set_ext(ObObj::MIN_OBJECT_VALUE);
|
||||
rowkey_wrapper.add_obj();
|
||||
}
|
||||
};
|
||||
|
||||
class OBMAX {
|
||||
public:
|
||||
OBMAX()
|
||||
{}
|
||||
~OBMAX()
|
||||
{}
|
||||
|
||||
public:
|
||||
void build(ObStoreRowkeyWrapper& rowkey_wrapper) const
|
||||
{
|
||||
rowkey_wrapper.get_cur_obj().set_ext(ObObj::MAX_OBJECT_VALUE);
|
||||
rowkey_wrapper.add_obj();
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPE_OBJ(IT, int8_t, tinyint)
|
||||
DEFINE_TYPE_OBJ(IS, int16_t, smallint)
|
||||
DEFINE_TYPE_OBJ(IM, int32_t, mediumint)
|
||||
DEFINE_TYPE_OBJ(I32, int32_t, int32)
|
||||
DEFINE_TYPE_OBJ(I, int64_t, int)
|
||||
|
||||
DEFINE_TYPE_OBJ(UIT, uint8_t, utinyint)
|
||||
DEFINE_TYPE_OBJ(UIS, uint16_t, usmallint)
|
||||
DEFINE_TYPE_OBJ(UIM, uint32_t, umediumint)
|
||||
DEFINE_TYPE_OBJ(UI32, uint32_t, uint32)
|
||||
DEFINE_TYPE_OBJ(UI, uint64_t, uint64)
|
||||
|
||||
DEFINE_TYPE_OBJ(F, float, float)
|
||||
DEFINE_TYPE_OBJ(D, double, double)
|
||||
|
||||
DEFINE_TYPE_OBJ(UF, float, ufloat)
|
||||
DEFINE_TYPE_OBJ(UD, double, udouble)
|
||||
|
||||
DEFINE_NMBTYPE_OBJ(N, number)
|
||||
DEFINE_NMBTYPE_OBJ(UN, unumber)
|
||||
|
||||
DEFINE_TYPE_OBJ(T, int64_t, datetime)
|
||||
DEFINE_TYPE_OBJ(TS, int64_t, timestamp)
|
||||
DEFINE_TYPE_OBJ(DD, int32_t, date)
|
||||
DEFINE_TYPE_OBJ(TT, int64_t, time)
|
||||
DEFINE_TYPE_OBJ(YY, uint8_t, year)
|
||||
|
||||
DEFINE_CHARTYPE_OBJ(V, varchar, ObCollationType)
|
||||
DEFINE_CHARTYPE_OBJ(C, char, ObCollationType)
|
||||
DEFINE_CHARTYPE_OBJ(VB, varbinary, ObCollationType)
|
||||
DEFINE_CHARTYPE_OBJ(BB, binary, ObCollationType)
|
||||
|
||||
typedef ObStoreRowkeyWrapper RK;
|
||||
typedef ObColumnDesc CD;
|
||||
|
||||
#define INIT_MTK(allocator, mtk, table_id, ...) \
|
||||
{ \
|
||||
RK _rkb_(__VA_ARGS__); \
|
||||
ObMemtableKey tmp_mtk; \
|
||||
int ret = tmp_mtk.encode(table_id, &(_rkb_.get_rowkey())); \
|
||||
EXPECT_EQ(OB_SUCCESS, ret); \
|
||||
ret = tmp_mtk.dup(mtk, allocator); \
|
||||
EXPECT_EQ(OB_SUCCESS, ret); \
|
||||
}
|
||||
|
||||
} // namespace unittest
|
||||
} // namespace oceanbase
|
||||
|
||||
#endif // OCEANBASE_UNITTEST_MEMTABLE_ROWKEY_BUILDER_H_
|
||||
Reference in New Issue
Block a user