
Co-authored-by: dimstars <liangjinrongcm@gmail.com> Co-authored-by: pe-99y <315053752@qq.com>
646 lines
20 KiB
C++
646 lines
20 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
* See the Mulan PubL v2 for more details.
|
|
*/
|
|
|
|
#include "storage/memtable/mvcc/ob_keybtree.h"
|
|
#include "lib/allocator/ob_malloc.h"
|
|
#include "lib/oblog/ob_log.h"
|
|
#include "lib/random/ob_random.h"
|
|
#include "common/object/ob_object.h"
|
|
#include <gtest/gtest.h>
|
|
#include <thread>
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <unordered_set>
|
|
#include <atomic>
|
|
|
|
namespace oceanbase {
|
|
namespace unittest {
|
|
using namespace oceanbase::keybtree;
|
|
|
|
#define DUMP_BTREE \
|
|
{ \
|
|
FILE *file = fopen("dump_btree.txt", "w+"); \
|
|
btree.dump(file); \
|
|
fclose(file); \
|
|
}
|
|
|
|
class FakeAllocator : public ObIAllocator {
|
|
public:
|
|
FakeAllocator() : remain_(0), is_limited_(false)
|
|
{}
|
|
const char *attr = ObModIds::TEST;
|
|
void *alloc(int64_t size) override
|
|
{
|
|
void *block = nullptr;
|
|
if (is_limited_) {
|
|
if (remain_ > 0) {
|
|
block = ob_malloc(size, attr);
|
|
remain_ -= size;
|
|
}
|
|
} else {
|
|
block = ob_malloc(size, attr);
|
|
}
|
|
return block;
|
|
}
|
|
void *alloc_key(int64_t size)
|
|
{
|
|
return ob_malloc(size, attr);
|
|
}
|
|
void *alloc(const int64_t size, const ObMemAttr &attr) override
|
|
{
|
|
void *block = nullptr;
|
|
UNUSED(attr);
|
|
if (is_limited_) {
|
|
if (remain_ > 0) {
|
|
block = alloc(size);
|
|
remain_ -= size;
|
|
}
|
|
} else {
|
|
block = alloc(size);
|
|
}
|
|
return block;
|
|
}
|
|
void free(void *ptr) override
|
|
{
|
|
ob_free(ptr);
|
|
}
|
|
void set_remain(int remain)
|
|
{
|
|
remain_ = remain;
|
|
is_limited_ = true;
|
|
}
|
|
void unset_limited()
|
|
{
|
|
is_limited_ = false;
|
|
}
|
|
static FakeAllocator *get_instance()
|
|
{
|
|
static FakeAllocator allocator;
|
|
return &allocator;
|
|
}
|
|
|
|
private:
|
|
std::atomic<int64_t> remain_;
|
|
bool is_limited_;
|
|
};
|
|
|
|
class FakeKey {
|
|
public:
|
|
FakeKey() : obj_(nullptr)
|
|
{}
|
|
FakeKey(ObObj *obj) : obj_(obj)
|
|
{}
|
|
void set_int(int64_t data)
|
|
{
|
|
obj_->set_int(data);
|
|
}
|
|
int compare(FakeKey other, int &cmp) const
|
|
{
|
|
return obj_->compare(*other.obj_, cmp);
|
|
}
|
|
int64_t to_string(char *buf, const int64_t limit) const
|
|
{
|
|
return obj_->to_string(buf, limit);
|
|
}
|
|
|
|
ObObj *get_ptr() const
|
|
{
|
|
return obj_;
|
|
}
|
|
ObObj *obj_;
|
|
};
|
|
|
|
FakeKey build_int_key(int64_t key)
|
|
{
|
|
auto alloc = FakeAllocator::get_instance();
|
|
void *block = alloc->alloc_key(sizeof(ObObj));
|
|
EXPECT_TRUE(OB_NOT_NULL(block));
|
|
ObObj *obj = new (block) ObObj(key);
|
|
return FakeKey(obj);
|
|
}
|
|
|
|
void free_key(FakeKey &key)
|
|
{
|
|
auto alloc = FakeAllocator::get_instance();
|
|
alloc->free((void *)key.obj_);
|
|
}
|
|
|
|
using BtreeNode = BtreeNode<FakeKey, int64_t *>;
|
|
using ObKeyBtree = ObKeyBtree<FakeKey, int64_t *>;
|
|
using BtreeIterator = BtreeIterator<FakeKey, int64_t *>;
|
|
void judge_tree_scan(ObKeyBtree *btree, FakeKey start_key, FakeKey end_key, bool exclude_start_key,
|
|
bool exclude_end_key, bool is_backward, std::vector<int64_t> &answer)
|
|
{
|
|
FakeKey key;
|
|
int64_t *val = nullptr;
|
|
int i = 0;
|
|
|
|
BtreeIterator iter;
|
|
btree->set_key_range(iter, start_key, exclude_start_key, end_key, exclude_end_key);
|
|
while (iter.get_next(key, val) == OB_SUCCESS) {
|
|
ASSERT_EQ(key.get_ptr()->get_int(), answer[i]);
|
|
i++;
|
|
}
|
|
ASSERT_EQ(i, answer.size());
|
|
}
|
|
|
|
void free_btree(ObKeyBtree &btree)
|
|
{
|
|
FakeKey start_key = build_int_key(INT64_MIN);
|
|
FakeKey end_key = build_int_key(INT64_MAX);
|
|
FakeKey key;
|
|
int64_t *val = nullptr;
|
|
FakeAllocator *allocator = FakeAllocator::get_instance();
|
|
BtreeIterator iter;
|
|
btree.set_key_range(iter, start_key, true, end_key, true);
|
|
std::vector<FakeKey> keys;
|
|
while (iter.get_next(key, val) == OB_SUCCESS) {
|
|
keys.push_back(key);
|
|
}
|
|
for (auto &key : keys) {
|
|
allocator->free(key.get_ptr());
|
|
}
|
|
btree.destroy(false /*is_batch_destroy*/);
|
|
}
|
|
|
|
TEST(TestBtree, smoke_test)
|
|
{
|
|
constexpr int64_t KEY_NUM = 100000;
|
|
std::vector<int64_t> data(KEY_NUM);
|
|
|
|
FakeAllocator *allocator = FakeAllocator::get_instance();
|
|
BtreeNodeAllocator<FakeKey, int64_t *> node_allocator(*allocator);
|
|
ObKeyBtree btree(node_allocator);
|
|
FakeKey search_key = build_int_key(0);
|
|
|
|
ASSERT_EQ(btree.init(), OB_SUCCESS);
|
|
|
|
for (int i = 0; i <= KEY_NUM; i++) {
|
|
data[i] = i * 2;
|
|
}
|
|
std::random_shuffle(data.begin(), data.end());
|
|
|
|
// test insert and search
|
|
for (int len = 1; len <= KEY_NUM; len++) {
|
|
int64_t cur = data[len - 1];
|
|
FakeKey key = build_int_key(cur);
|
|
int64_t *val = &data[len - 1];
|
|
btree.insert(key, val);
|
|
if (len % (KEY_NUM / 49) == 0) {
|
|
for (int i = 0; i < len * 2; i++) {
|
|
search_key.set_int(data[i / 2] + i % 2);
|
|
if (i % 2 == 0) {
|
|
ASSERT_EQ(btree.get(search_key, val), OB_SUCCESS);
|
|
} else {
|
|
ASSERT_EQ(btree.get(search_key, val), OB_ENTRY_NOT_EXIST);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(data.begin(), data.end());
|
|
search_key.set_int(-1);
|
|
int64_t *val = nullptr;
|
|
ASSERT_EQ(btree.get(search_key, val), OB_ENTRY_NOT_EXIST);
|
|
|
|
FakeKey start_key = build_int_key(0);
|
|
FakeKey end_key = build_int_key(0);
|
|
|
|
// test scan
|
|
int REPEAT_COUNT = 100;
|
|
|
|
// forward include
|
|
REPEAT_COUNT = 100;
|
|
while (REPEAT_COUNT--) {
|
|
int64_t start_int = ObRandom::rand(-KEY_NUM, KEY_NUM * 3);
|
|
int64_t end_int = ObRandom::rand(start_int + 1, KEY_NUM * 3);
|
|
start_key.set_int(start_int);
|
|
end_key.set_int(end_int);
|
|
std::vector<int64_t> ans;
|
|
for (int i = max(0, (start_int + 1) / 2 * 2); i <= min((KEY_NUM - 1) * 2, min(end_int / 2 * 2, end_int)); i += 2) {
|
|
ans.push_back(i);
|
|
}
|
|
judge_tree_scan(&btree, start_key, end_key, false, false, false, ans);
|
|
}
|
|
|
|
// forward exclude
|
|
REPEAT_COUNT = 100;
|
|
while (REPEAT_COUNT--) {
|
|
int64_t start_int = ObRandom::rand(-KEY_NUM, KEY_NUM * 3);
|
|
int64_t end_int = ObRandom::rand(start_int + 1, KEY_NUM * 3);
|
|
start_key.set_int(start_int);
|
|
end_key.set_int(end_int);
|
|
std::vector<int64_t> ans;
|
|
for (int i = max(0, (start_int + 2) / 2 * 2); i <= min((KEY_NUM - 1) * 2, min((end_int - 1) / 2 * 2, end_int - 1));
|
|
i += 2) {
|
|
ans.push_back(i);
|
|
}
|
|
judge_tree_scan(&btree, start_key, end_key, true, true, false, ans);
|
|
}
|
|
|
|
// backward include
|
|
REPEAT_COUNT = 100;
|
|
while (REPEAT_COUNT--) {
|
|
int64_t start_int = ObRandom::rand(-KEY_NUM, KEY_NUM * 3);
|
|
int64_t end_int = ObRandom::rand(start_int + 1, KEY_NUM * 3);
|
|
start_key.set_int(start_int);
|
|
end_key.set_int(end_int);
|
|
std::vector<int64_t> ans;
|
|
for (int i = min((KEY_NUM - 1) * 2, min(end_int / 2 * 2, end_int)); i >= max(0, (start_int + 1) / 2 * 2); i -= 2) {
|
|
ans.push_back(i);
|
|
}
|
|
judge_tree_scan(&btree, end_key, start_key, false, false, true, ans);
|
|
}
|
|
|
|
// backward exclude
|
|
REPEAT_COUNT = 100;
|
|
while (REPEAT_COUNT--) {
|
|
int64_t start_int = ObRandom::rand(-KEY_NUM, KEY_NUM * 3);
|
|
int64_t end_int = ObRandom::rand(start_int + 1, KEY_NUM * 3);
|
|
start_key.set_int(start_int);
|
|
end_key.set_int(end_int);
|
|
std::vector<int64_t> ans;
|
|
for (int i = min((KEY_NUM - 1) * 2, min((end_int - 1) / 2 * 2, end_int - 1)); i >= max(0, (start_int + 2) / 2 * 2);
|
|
i -= 2) {
|
|
ans.push_back(i);
|
|
}
|
|
judge_tree_scan(&btree, end_key, start_key, true, true, true, ans);
|
|
}
|
|
|
|
free_btree(btree);
|
|
allocator->free(search_key.get_ptr());
|
|
allocator->free(start_key.get_ptr());
|
|
allocator->free(end_key.get_ptr());
|
|
}
|
|
|
|
TEST(TestEventualConsistency, smoke_test)
|
|
{
|
|
constexpr uint64_t KEY_NUM = 6400000;
|
|
constexpr uint64_t THREAD_COUNT = 64;
|
|
constexpr uint64_t PER_THREAD_INSERT_COUNT = KEY_NUM / THREAD_COUNT;
|
|
|
|
FakeAllocator *allocator = FakeAllocator::get_instance();
|
|
BtreeNodeAllocator<FakeKey, int64_t *> node_allocator(*allocator);
|
|
ObKeyBtree btree(node_allocator);
|
|
std::thread threads[THREAD_COUNT];
|
|
|
|
ASSERT_EQ(btree.init(), OB_SUCCESS);
|
|
|
|
// prepare insert keys
|
|
std::vector<std::vector<int64_t>> data(THREAD_COUNT, std::vector<int64_t>(PER_THREAD_INSERT_COUNT));
|
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT; j++) {
|
|
data[i][j] = THREAD_COUNT * j + i;
|
|
}
|
|
std::random_shuffle(data[i].begin(), data[i].end());
|
|
}
|
|
|
|
// concurrent insert
|
|
for (int thread_id = 0; thread_id < THREAD_COUNT; thread_id++) {
|
|
threads[thread_id] = std::thread(
|
|
[&](int i) {
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT; j++) {
|
|
int64_t *val = &(data[i][j]);
|
|
btree.insert(build_int_key(data[i][j]), val);
|
|
}
|
|
},
|
|
thread_id);
|
|
}
|
|
|
|
for (int thread_id = 0; thread_id < THREAD_COUNT; thread_id++) {
|
|
threads[thread_id].join();
|
|
}
|
|
|
|
// evaluate the tree
|
|
FakeKey start_key = build_int_key(0);
|
|
FakeKey end_key = build_int_key(KEY_NUM);
|
|
FakeKey key;
|
|
int64_t *val;
|
|
|
|
BtreeIterator iter;
|
|
btree.set_key_range(iter, start_key, false, end_key, false);
|
|
int i = 0;
|
|
while (iter.get_next(key, val) == OB_SUCCESS) {
|
|
ASSERT_EQ(key.get_ptr()->get_int(), i);
|
|
i++;
|
|
}
|
|
ASSERT_EQ(i, KEY_NUM);
|
|
|
|
free_btree(btree);
|
|
allocator->free(start_key.get_ptr());
|
|
allocator->free(end_key.get_ptr());
|
|
}
|
|
|
|
TEST(TestMonotonicReadWrite, smoke_test)
|
|
{
|
|
constexpr int KEY_NUM = 6400000;
|
|
constexpr int WRITE_THREAD_COUNT = 32;
|
|
constexpr int PER_THREAD_INSERT_COUNT = KEY_NUM / WRITE_THREAD_COUNT;
|
|
constexpr int SCAN_THREAD_COUNT = 32;
|
|
constexpr int PER_THREAD_SCAN_COUNT = 8;
|
|
|
|
FakeAllocator *allocator = FakeAllocator::get_instance();
|
|
BtreeNodeAllocator<FakeKey, int64_t *> node_allocator(*allocator);
|
|
ObKeyBtree btree(node_allocator);
|
|
|
|
ASSERT_EQ(btree.init(), OB_SUCCESS);
|
|
|
|
// constructing insert keys
|
|
std::vector<std::vector<int64_t>> data(WRITE_THREAD_COUNT, std::vector<int64_t>(PER_THREAD_INSERT_COUNT));
|
|
for (int i = 0; i < WRITE_THREAD_COUNT; i++) {
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT; j++) {
|
|
data[i][j] = WRITE_THREAD_COUNT * j + i;
|
|
}
|
|
std::random_shuffle(data[i].begin(), data[i].end());
|
|
}
|
|
|
|
std::thread write_threads[WRITE_THREAD_COUNT];
|
|
for (int thread_id = 0; thread_id < WRITE_THREAD_COUNT; thread_id++) {
|
|
write_threads[thread_id] = std::thread(
|
|
[&](int i) {
|
|
// insert in order
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT; j++) {
|
|
int64_t *val = &(data[i][j]);
|
|
btree.insert(build_int_key(data[i][j]), val);
|
|
usleep(1);
|
|
}
|
|
},
|
|
thread_id);
|
|
}
|
|
|
|
std::thread scan_threads[SCAN_THREAD_COUNT];
|
|
for (int thread_id = 0; thread_id < SCAN_THREAD_COUNT; thread_id++) {
|
|
scan_threads[thread_id] = std::thread(
|
|
[&](int thread_id) {
|
|
FakeKey start_key = build_int_key(-1);
|
|
FakeKey end_key = build_int_key(KEY_NUM + 1);
|
|
int scan_count = PER_THREAD_SCAN_COUNT;
|
|
std::unordered_set<int64_t *> last_results;
|
|
while (scan_count--) {
|
|
std::unordered_set<int64_t *> results;
|
|
FakeKey key;
|
|
int64_t *val;
|
|
if (thread_id % 2 == 0) {
|
|
// scan forward
|
|
BtreeIterator iter;
|
|
btree.set_key_range(iter, start_key, false, end_key, false);
|
|
int64_t last = -1;
|
|
while (iter.get_next(key, val) == OB_SUCCESS) {
|
|
results.insert(val);
|
|
ASSERT_GT(key.get_ptr()->get_int(), last);
|
|
last = key.get_ptr()->get_int();
|
|
}
|
|
} else {
|
|
// scan backward
|
|
BtreeIterator iter;
|
|
btree.set_key_range(iter, end_key, false, start_key, false);
|
|
int64_t last = KEY_NUM + 1;
|
|
while (iter.get_next(key, val) == OB_SUCCESS) {
|
|
results.insert(val);
|
|
ASSERT_LT(key.get_ptr()->get_int(), last);
|
|
last = key.get_ptr()->get_int();
|
|
}
|
|
}
|
|
// test monotonic write, if a thread see a key A, then it should see all keys inserted before A
|
|
for (int i = 0; i < WRITE_THREAD_COUNT; i++) {
|
|
if (thread_id % 2 == 0) {
|
|
int64_t min = KEY_NUM + 1;
|
|
for (int j = PER_THREAD_INSERT_COUNT - 1; j >= 0; j--) {
|
|
ASSERT_TRUE(data[i][j] < min || results.count(&data[i][j]) == 1);
|
|
if (results.count(&data[i][j]) == 1) {
|
|
min = std::min(min, data[i][j]);
|
|
}
|
|
}
|
|
} else {
|
|
int64_t max = -1;
|
|
for (int j = PER_THREAD_INSERT_COUNT - 1; j >= 0; j--) {
|
|
ASSERT_TRUE(data[i][j] > max || results.count(&data[i][j]) == 1);
|
|
if (results.count(&data[i][j]) == 1) {
|
|
max = std::max(max, data[i][j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// test monotonic read, if a thread do two scan, then the frist scan result should be the subset of
|
|
// the second scan result.
|
|
for (auto i : last_results) {
|
|
ASSERT_TRUE(results.count(i) == 1);
|
|
}
|
|
last_results = results;
|
|
}
|
|
allocator->free(start_key.get_ptr());
|
|
allocator->free(end_key.get_ptr());
|
|
},
|
|
thread_id);
|
|
}
|
|
|
|
for (int thread_id = 0; thread_id < WRITE_THREAD_COUNT; thread_id++) {
|
|
write_threads[thread_id].join();
|
|
}
|
|
for (int thread_id = 0; thread_id < SCAN_THREAD_COUNT; thread_id++) {
|
|
scan_threads[thread_id].join();
|
|
}
|
|
|
|
free_btree(btree);
|
|
}
|
|
|
|
TEST(TestSequentialConsistency, smoke_test)
|
|
{
|
|
constexpr int PER_THREAD_INSERT_COUNT = 200000;
|
|
constexpr int READ_THREAD_COUNT = 16;
|
|
|
|
std::atomic<int> progress(-1);
|
|
|
|
FakeAllocator *allocator = FakeAllocator::get_instance();
|
|
BtreeNodeAllocator<FakeKey, int64_t *> node_allocator(*allocator);
|
|
ObKeyBtree btree(node_allocator);
|
|
ASSERT_EQ(btree.init(), OB_SUCCESS);
|
|
|
|
std::thread main_thread([&] {
|
|
for (; progress < PER_THREAD_INSERT_COUNT; progress++) {
|
|
usleep(1);
|
|
}
|
|
});
|
|
|
|
std::vector<int64_t> insert_keys(PER_THREAD_INSERT_COUNT * 2);
|
|
for (int i = 0; i < insert_keys.size(); i++) {
|
|
insert_keys[i] = i;
|
|
}
|
|
std::random_shuffle(insert_keys.begin(), insert_keys.end());
|
|
|
|
std::thread write_threads[2];
|
|
for (int thread_id = 0; thread_id < 2; thread_id++) {
|
|
write_threads[thread_id] = std::thread(
|
|
[&](int thread_id) {
|
|
int last = -1;
|
|
int insert_id = 0;
|
|
while (last < PER_THREAD_INSERT_COUNT) {
|
|
while (last >= progress) {}
|
|
last++;
|
|
insert_id = last * 2 + thread_id;
|
|
int64_t *val = &(insert_keys[insert_id]);
|
|
btree.insert(build_int_key(insert_keys[insert_id]), val);
|
|
}
|
|
},
|
|
thread_id);
|
|
}
|
|
|
|
std::vector<std::vector<bool>> read_results(READ_THREAD_COUNT, std::vector<bool>(PER_THREAD_INSERT_COUNT));
|
|
|
|
std::thread read_threads[READ_THREAD_COUNT];
|
|
for (int thread_id = 0; thread_id < READ_THREAD_COUNT; thread_id++) {
|
|
read_threads[thread_id] = std::thread(
|
|
[&](int thread_id) {
|
|
int64_t *val;
|
|
for (int i = 0; i < PER_THREAD_INSERT_COUNT; i++) {
|
|
FakeKey search_key1 = build_int_key(insert_keys[i * 2]);
|
|
FakeKey search_key2 = build_int_key(insert_keys[i * 2 + 1]);
|
|
if (thread_id % 2 == 0) {
|
|
while (btree.get(search_key1, val) != OB_SUCCESS) {}
|
|
if (btree.get(search_key2, val) == OB_ENTRY_NOT_EXIST) {
|
|
// the order this thread saw is: search_key1 -> search_key2
|
|
read_results[thread_id][i] = true;
|
|
}
|
|
} else {
|
|
while (btree.get(search_key2, val) != OB_SUCCESS) {}
|
|
if (btree.get(search_key1, val) == OB_ENTRY_NOT_EXIST) {
|
|
// the order this thread saw is: search_key2 -> search_key1
|
|
read_results[thread_id][i] = true;
|
|
}
|
|
}
|
|
allocator->free(search_key1.get_ptr());
|
|
allocator->free(search_key2.get_ptr());
|
|
}
|
|
},
|
|
thread_id);
|
|
}
|
|
|
|
main_thread.join();
|
|
write_threads[0].join();
|
|
write_threads[1].join();
|
|
for (int i = 0; i < READ_THREAD_COUNT; i++) {
|
|
read_threads[i].join();
|
|
}
|
|
|
|
int count = 0;
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT; j++) {
|
|
for (int i = 0; i < READ_THREAD_COUNT; i++) {
|
|
read_results[i % 2][j] = read_results[i % 2][j] || read_results[i][j];
|
|
}
|
|
// threads shouldn't see different order
|
|
ASSERT_FALSE(read_results[0][j] && read_results[1][j]);
|
|
}
|
|
|
|
free_btree(btree);
|
|
}
|
|
|
|
void test_memory_not_enough(int max_nodes_cnt)
|
|
{
|
|
constexpr uint64_t KEY_NUM = 6400000;
|
|
constexpr uint64_t THREAD_COUNT = 64;
|
|
constexpr uint64_t PER_THREAD_INSERT_COUNT = KEY_NUM / THREAD_COUNT;
|
|
|
|
FakeAllocator *allocator = FakeAllocator::get_instance();
|
|
BtreeNodeAllocator<FakeKey, int64_t *> node_allocator(*allocator);
|
|
ObKeyBtree btree(node_allocator);
|
|
int64_t max_size = max_nodes_cnt * sizeof(BtreeNode);
|
|
|
|
ASSERT_EQ(btree.init(), OB_SUCCESS);
|
|
|
|
std::thread threads[THREAD_COUNT];
|
|
std::vector<std::vector<int64_t>> data(THREAD_COUNT, std::vector<int64_t>(PER_THREAD_INSERT_COUNT));
|
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT; j++) {
|
|
data[i][j] = THREAD_COUNT * j + i;
|
|
}
|
|
std::random_shuffle(data[i].begin(), data[i].end());
|
|
}
|
|
|
|
allocator->set_remain(max_size);
|
|
|
|
int insert_progress[THREAD_COUNT];
|
|
// concurrent insert
|
|
for (int thread_id = 0; thread_id < THREAD_COUNT; thread_id++) {
|
|
threads[thread_id] = std::thread(
|
|
[&](int i) {
|
|
int ret = OB_SUCCESS;
|
|
int64_t *val = &data[i][0];
|
|
insert_progress[i] = -1;
|
|
for (int j = 0; j < PER_THREAD_INSERT_COUNT && OB_SUCC(btree.insert(build_int_key(data[i][j]), val));
|
|
j++, val = &data[i][j]) {
|
|
insert_progress[i] = j;
|
|
}
|
|
ASSERT_EQ(ret, OB_ALLOCATE_MEMORY_FAILED);
|
|
},
|
|
thread_id);
|
|
}
|
|
|
|
for (int thread_id = 0; thread_id < THREAD_COUNT; thread_id++) {
|
|
threads[thread_id].join();
|
|
}
|
|
|
|
allocator->unset_limited();
|
|
|
|
// evaluate the tree
|
|
FakeKey key = build_int_key(0);
|
|
int64_t *val = nullptr;
|
|
std::unordered_set<int64_t> results;
|
|
for (int i = 0; i < THREAD_COUNT; i++) {
|
|
for (int j = 0; j <= insert_progress[i]; j++) {
|
|
key.set_int(data[i][j]);
|
|
ASSERT_EQ(btree.get(key, val), OB_SUCCESS);
|
|
ASSERT_EQ(*val, data[i][j]);
|
|
results.insert(*val);
|
|
}
|
|
}
|
|
free_key(key);
|
|
|
|
FakeKey start_key = build_int_key(0);
|
|
FakeKey end_key = build_int_key(KEY_NUM);
|
|
|
|
BtreeIterator iter;
|
|
btree.set_key_range(iter, start_key, false, end_key, false);
|
|
int64_t last = -1;
|
|
while (iter.get_next(key, val) == OB_SUCCESS) {
|
|
ASSERT_GT(*val, last);
|
|
last = *val;
|
|
results.erase(*val);
|
|
}
|
|
|
|
ASSERT_EQ(results.size(), 0);
|
|
|
|
free_btree(btree);
|
|
free_key(start_key);
|
|
free_key(end_key);
|
|
}
|
|
|
|
TEST(TestMemoryNotEnough, smoke_test)
|
|
{
|
|
int nodes_cnt[20] = {1, 2, 3, 15, 16, 17, 18, 19, 20, 225, 227, 229, 230, 500, 1000, 2000, 3375, 5000, 8000, 10000};
|
|
for (int i = 0; i < 20; i++) {
|
|
test_memory_not_enough(nodes_cnt[i]);
|
|
}
|
|
}
|
|
|
|
} // 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_keybtreeV2.log", true);
|
|
oceanbase::common::ObLogger::get_logger().set_log_level("INFO");
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|