2 * Copyright (C) 2017 Cisco Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // @author Changxue Deng <chadeng@cisco.com>
21 #include <gtest/gtest.h>
24 #include "../dict_mem.h"
25 #include "./test_key.h"
26 #include "../resource_pool.h"
29 using namespace mabain;
33 class AbnormalExitTest : public ::testing::Test
38 key_type = MABAIN_TEST_KEY_TYPE_INT;
40 virtual ~AbnormalExitTest() {
42 virtual void SetUp() {
43 std::string db_dir = "/var/tmp/mabain_test";
44 std::string cmd = std::string("mkdir -p ") + db_dir;
45 if(system(cmd.c_str()) != 0) {
47 unlink((db_dir + "/_mabain_h").c_str());
48 unlink((db_dir + "/_dbfl").c_str());
49 unlink((db_dir + "/_ibfl").c_str());
50 db = new DB(db_dir.c_str(), CONSTS::ACCESS_MODE_WRITER);
52 std::cerr << "failed to open db: " << db_dir << " " << db->StatusStr() << "\n";
56 virtual void TearDown() {
62 ResourcePool::getInstance().RemoveAll();
65 void Populate(int count) {
68 TestKey tkey(key_type);
69 for(int key = 1; key <= count; key++) {
70 key_str = tkey.get_key(key);
71 db->Add(key_str, key_str);
75 void SimulateAbnormalExit(int exception_type) {
76 Dict *dict = db->GetDictPtr();
77 IndexHeader *header = dict->GetHeaderPtr();
78 DictMem *dmm = dict->GetMM();
80 TestKey tkey(key_type);
83 switch(exception_type) {
84 case EXCEP_STATUS_ADD_DATA_OFF:
85 key_str = tkey.get_key(1278);
86 db->Add(key_str, key_str + "_UPDATED", true);
88 case EXCEP_STATUS_ADD_NODE:
90 db->Add(key_str, key_str);
92 db->Add(key_str, key_str);
94 db->Add(key_str, key_str);
96 db->Add(key_str, key_str);
98 case EXCEP_STATUS_CLEAR_EDGE:
99 key_str = tkey.get_key(remove_index);
100 rval = db->Remove(key_str);
101 assert(rval == MBError::SUCCESS);
103 case EXCEP_STATUS_REMOVE_EDGE:
107 header->excep_updating_status = exception_type;
109 // Writer random data to simulate the db inconsistency
112 int *ptr = (int *) buffer;
113 for(int i = 0; i < 4; i++) {
114 ptr[i] = (int) rand();
117 switch(exception_type) {
118 case EXCEP_STATUS_ADD_EDGE:
119 dmm->WriteData(buffer, EDGE_SIZE, header->excep_lf_offset);
121 case EXCEP_STATUS_ADD_DATA_OFF:
122 dmm->WriteData(buffer, OFFSET_SIZE, header->excep_lf_offset+EDGE_NODE_LEADING_POS);
124 case EXCEP_STATUS_ADD_NODE:
125 dmm->WriteData(buffer, NODE_EDGE_KEY_FIRST, header->excep_offset);
127 case EXCEP_STATUS_REMOVE_EDGE:
128 // Currently we cannot simulate this exception.
129 //dmm->WriteData(buffer, OFFSET_SIZE, header->excep_lf_offset+EDGE_NODE_LEADING_POS);
131 case EXCEP_STATUS_CLEAR_EDGE:
132 dmm->WriteData(buffer, EDGE_SIZE, header->excep_lf_offset);
140 Dict *dict = db->GetDictPtr();
141 int rval = dict->ExceptionRecovery();
145 int CheckDBConcistency(int count) {
146 DB db_r("/var/tmp/mabain_test", CONSTS::ACCESS_MODE_READER);
147 assert(db_r.is_open());
150 TestKey tkey(key_type);
153 for(int key = 1; key <= count; key++) {
154 key_str = tkey.get_key(key);
155 rval = db_r.Find(key_str, mbd);
157 if(key == remove_index) continue;
158 if(rval != MBError::SUCCESS) {
162 if(key_str != std::string((char*)mbd.buff, mbd.data_len)) {
163 // Value may be updated
164 if(key_str+"_UPDATED" != std::string((char*)mbd.buff, mbd.data_len)) {
174 int CheckHalfDBConsistency(int count, bool check_even) {
175 DB db_r("/var/tmp/mabain_test", CONSTS::ACCESS_MODE_READER);
176 assert(db_r.is_open());
179 TestKey tkey(key_type);
182 for(int key = 1; key <= count; key++) {
183 key_str = tkey.get_key(key);
184 rval = db_r.Find(key_str, mbd);
187 if(key % 2 == 1) continue;
189 if(key % 2 == 0) continue;
191 if(rval != MBError::SUCCESS) {
195 if(key_str != std::string((char*)mbd.buff, mbd.data_len)) {
196 // Value may be updated
197 if(key_str+"_UPDATED" != std::string((char*)mbd.buff, mbd.data_len)) {
213 TEST_F(AbnormalExitTest, KEY_TYPE_INT_test)
219 key_type = MABAIN_TEST_KEY_TYPE_INT;
222 SimulateAbnormalExit(EXCEP_STATUS_ADD_EDGE);
223 failed_cnt = CheckDBConcistency(count);
224 std::cout << "failed count before recovery: " << failed_cnt << "\n";
227 EXPECT_EQ(rval, MBError::SUCCESS);
229 failed_cnt = CheckDBConcistency(count);
230 EXPECT_EQ(failed_cnt, 0);
233 TEST_F(AbnormalExitTest, KEY_TYPE_SHA1_test)
239 key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
242 SimulateAbnormalExit(EXCEP_STATUS_ADD_EDGE);
243 failed_cnt = CheckDBConcistency(count);
244 std::cout << "failed count before recovery: " << failed_cnt << "\n";
247 EXPECT_EQ(rval, MBError::SUCCESS);
249 failed_cnt = CheckDBConcistency(count);
250 EXPECT_EQ(failed_cnt, 0);
253 TEST_F(AbnormalExitTest, KEY_TYPE_SHA256_test)
259 key_type = MABAIN_TEST_KEY_TYPE_SHA_256;
262 SimulateAbnormalExit(EXCEP_STATUS_ADD_EDGE);
263 failed_cnt = CheckDBConcistency(count);
264 std::cout << "failed count before recovery: " << failed_cnt << "\n";
269 failed_cnt = CheckDBConcistency(count);
270 EXPECT_EQ(failed_cnt, 0);
273 TEST_F(AbnormalExitTest, KEY_TYPE_SHA1_ADD_DATA_OFF_test)
279 key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
282 SimulateAbnormalExit(EXCEP_STATUS_ADD_DATA_OFF);
283 failed_cnt = CheckDBConcistency(count);
284 std::cout << "failed count before recovery: " << failed_cnt << "\n";
287 EXPECT_EQ(rval, MBError::SUCCESS);
289 failed_cnt = CheckDBConcistency(count);
290 EXPECT_EQ(failed_cnt, 0);
293 TEST_F(AbnormalExitTest, KEY_TYPE_INT_ADD_NODE_test)
299 key_type = MABAIN_TEST_KEY_TYPE_INT;
302 SimulateAbnormalExit(EXCEP_STATUS_ADD_NODE);
303 failed_cnt = CheckDBConcistency(count);
304 std::cout << "failed count before recovery: " << failed_cnt << "\n";
307 EXPECT_EQ(rval, MBError::SUCCESS);
309 failed_cnt = CheckDBConcistency(count);
310 EXPECT_EQ(failed_cnt, 0);
313 TEST_F(AbnormalExitTest, KEY_TYPE_INT_REMOVE_test)
319 key_type = MABAIN_TEST_KEY_TYPE_INT;
323 SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
324 failed_cnt = CheckDBConcistency(count);
325 std::cout << "failed count before recovery: " << failed_cnt << "\n";
330 failed_cnt = CheckDBConcistency(count);
331 EXPECT_EQ(failed_cnt, 0);
334 TEST_F(AbnormalExitTest, KEY_TYPE_INT_REMOVE_test_1)
340 key_type = MABAIN_TEST_KEY_TYPE_INT;
344 SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
345 failed_cnt = CheckDBConcistency(count);
346 std::cout << "failed count before recovery: " << failed_cnt << "\n";
349 EXPECT_EQ(rval, MBError::SUCCESS);
351 failed_cnt = CheckDBConcistency(count);
352 EXPECT_EQ(failed_cnt, 0);
355 TEST_F(AbnormalExitTest, KEY_TYPE_SHA_128_REMOVE_test)
361 key_type = MABAIN_TEST_KEY_TYPE_INT;
365 SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
366 failed_cnt = CheckDBConcistency(count);
367 std::cout << "failed count before recovery: " << failed_cnt << "\n";
370 EXPECT_EQ(rval, MBError::SUCCESS);
372 failed_cnt = CheckDBConcistency(count);
373 EXPECT_EQ(failed_cnt, 0);
376 TEST_F(AbnormalExitTest, KEY_TYPE_INT_REMOVE_ODD_test)
382 key_type = MABAIN_TEST_KEY_TYPE_INT;
385 for(int k = 1; k <= count; k++) {
386 if(k % 2 == 0) continue;
389 SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
391 EXPECT_EQ(rval, MBError::SUCCESS);
394 failed_cnt = CheckHalfDBConsistency(count, true);
395 EXPECT_EQ(failed_cnt, 0);
398 TEST_F(AbnormalExitTest, KEY_TYPE_SHA_256_REMOVE_EVEN_test)
404 key_type = MABAIN_TEST_KEY_TYPE_SHA_256;
407 for(int k = 1; k <= count; k++) {
408 if(k % 2 == 1) continue;
411 SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
413 EXPECT_EQ(rval, MBError::SUCCESS);
416 failed_cnt = CheckHalfDBConsistency(count, false);
417 EXPECT_EQ(failed_cnt, 0);
420 TEST_F(AbnormalExitTest, RC_RECOVERY_test)
423 key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
426 Dict *dict = db->GetDictPtr();
427 IndexHeader *header = dict->GetHeaderPtr();
429 //Reset m_index_offset and m_data_offset to simulate RC process
430 header->rc_m_index_off_pre = header->m_index_offset;
431 header->rc_m_data_off_pre = header->m_data_offset;
432 if(header->pending_index_buff_size == 0) {
433 header->pending_index_buff_size = 100U;
435 if(header->pending_data_buff_size == 0) {
436 header->pending_data_buff_size = 100U;
438 header->m_index_offset += 150LL*1024*1024;
439 header->m_data_offset += 92LL*1024*1024;
442 TestKey tkey(key_type);
444 for(int key = 1; key <= count1; key++) {
445 key_str = tkey.get_key(count + key);
446 int rval = db->Add(key_str, key_str);
447 if(rval != MBError::SUCCESS) {
448 std::cout << "failed to add " << key_str << "\n";
452 ResourceCollection rc(*db);
453 rc.ExceptionRecovery();
456 EXPECT_EQ(count+count1, dict->Count());
457 EXPECT_EQ(841614U, header->m_index_offset);
458 EXPECT_EQ(610916U, header->m_data_offset);
459 EXPECT_EQ(0U, header->pending_index_buff_size);
460 EXPECT_EQ(0U, header->pending_data_buff_size);
461 EXPECT_EQ(0U, header->rc_m_index_off_pre);
462 EXPECT_EQ(0U, header->rc_m_data_off_pre);
463 EXPECT_EQ(0U, header->rc_root_offset);
464 EXPECT_EQ(0, header->rc_count);
467 TEST_F(AbnormalExitTest, RC_RECOVERY_RC_ROOT_TREE_test)
470 key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
473 Dict *dict = db->GetDictPtr();
474 IndexHeader *header = dict->GetHeaderPtr();
475 DictMem *dmm = dict->GetMM();
477 // Reset m_index_offset and m_data_offset to simulate RC process
478 header->rc_m_index_off_pre = header->m_index_offset;
479 header->rc_m_data_off_pre = header->m_data_offset;
480 if(header->pending_index_buff_size == 0) {
481 header->pending_index_buff_size = 100U;
483 if(header->pending_data_buff_size == 0) {
484 header->pending_data_buff_size = 100U;
486 header->m_index_offset += 250LL*1024*1024;
487 header->m_data_offset += 292LL*1024*1024;
488 size_t rc_off = dmm->InitRootNode_RC();
489 header->rc_root_offset.store(rc_off, MEMORY_ORDER_WRITER);
493 TestKey tkey(key_type);
496 mbd.options = CONSTS::OPTION_RC_MODE;
497 for(int key = 1; key <= count1; key++) {
498 key_str = tkey.get_key(count + key);
499 mbd.buff = (uint8_t *) key_str.c_str();
500 mbd.data_len = key_str.size();
502 int rval = dict->Add((const uint8_t *)key_str.c_str(), key_str.size(), mbd, false);
503 if(rval != MBError::SUCCESS) {
504 std::cout << "failed to add " << key_str << "\n";
509 ResourceCollection rc(*db);
510 rc.ExceptionRecovery();
512 // Note the new entries added during rc will be ignored is expection occurs.
513 EXPECT_EQ(count, dict->Count());
514 EXPECT_EQ(1149932U, header->m_index_offset);
515 EXPECT_EQ(844268U, header->m_data_offset);
516 EXPECT_EQ(0U, header->pending_index_buff_size);
517 EXPECT_EQ(0U, header->pending_data_buff_size);
518 EXPECT_EQ(0U, header->rc_m_index_off_pre);
519 EXPECT_EQ(0U, header->rc_m_data_off_pre);
520 EXPECT_EQ(0U, header->rc_root_offset);
521 EXPECT_EQ(0, header->rc_count);