inital commit
[c11concurrency-benchmarks.git] / mabain / src / unittest / abnormal_exit_test.cpp
1 /**
2  * Copyright (C) 2017 Cisco Inc.
3  *
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.
7  *
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.
12  *
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/>.
15  */
16
17 // @author Changxue Deng <chadeng@cisco.com>
18
19 #include <iostream>
20
21 #include <gtest/gtest.h>
22
23 #include "../dict.h"
24 #include "../dict_mem.h"
25 #include "./test_key.h"
26 #include "../resource_pool.h"
27 #include "../mb_rc.h"
28
29 using namespace mabain;
30
31 namespace {
32
33 class AbnormalExitTest : public ::testing::Test
34 {
35 public:
36     AbnormalExitTest() {
37         remove_index = -1;
38         key_type = MABAIN_TEST_KEY_TYPE_INT;
39     }
40     virtual ~AbnormalExitTest() {
41     }
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) {
46         }
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);
51         if(!db->is_open()) {
52             std::cerr << "failed to open db: " << db_dir << " " << db->StatusStr() << "\n";
53             exit(0);
54         }
55     }
56     virtual void TearDown() {
57         if(db != NULL) {
58             db->Close();
59             delete db;
60             db = NULL;
61         }
62         ResourcePool::getInstance().RemoveAll();
63     }
64
65     void Populate(int count) {
66         db->RemoveAll();
67         std::string key_str;
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);
72         }
73     }
74
75     void SimulateAbnormalExit(int exception_type) {
76         Dict *dict = db->GetDictPtr();
77         IndexHeader *header = dict->GetHeaderPtr();
78         DictMem *dmm = dict->GetMM();
79         std::string key_str;
80         TestKey tkey(key_type);
81         int rval;
82
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);
87                 break;
88             case EXCEP_STATUS_ADD_NODE:
89                 key_str = "***abc1";
90                 db->Add(key_str, key_str);
91                 key_str = "***abd1";
92                 db->Add(key_str, key_str);
93                 key_str = "***abe1";
94                 db->Add(key_str, key_str);
95                 key_str = "***ab";
96                 db->Add(key_str, key_str);
97                 break;
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);
102                 break;
103             case EXCEP_STATUS_REMOVE_EDGE:
104                 break;
105         }
106
107         header->excep_updating_status = exception_type;
108
109         // Writer random data to simulate the db inconsistency
110         srand(time(NULL));
111         uint8_t buffer[4*4];
112         int *ptr = (int *) buffer;
113         for(int i = 0; i < 4; i++) {
114             ptr[i] = (int) rand();
115         }
116         
117         switch(exception_type) {
118             case EXCEP_STATUS_ADD_EDGE:
119                 dmm->WriteData(buffer, EDGE_SIZE, header->excep_lf_offset);
120                 break;
121             case EXCEP_STATUS_ADD_DATA_OFF:
122                 dmm->WriteData(buffer, OFFSET_SIZE, header->excep_lf_offset+EDGE_NODE_LEADING_POS);
123                 break;
124             case EXCEP_STATUS_ADD_NODE:
125                 dmm->WriteData(buffer, NODE_EDGE_KEY_FIRST, header->excep_offset);
126                 break;
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);
130                 break;
131             case EXCEP_STATUS_CLEAR_EDGE:
132                 dmm->WriteData(buffer, EDGE_SIZE, header->excep_lf_offset);
133                 break;
134             default:
135                 break;
136         }
137     }
138
139     int RecoverDB() {
140         Dict *dict = db->GetDictPtr();
141         int rval = dict->ExceptionRecovery();
142         return rval;
143     }
144
145     int CheckDBConcistency(int count) {
146         DB db_r("/var/tmp/mabain_test", CONSTS::ACCESS_MODE_READER);
147         assert(db_r.is_open());
148         std::string key_str;
149         MBData mbd;
150         TestKey tkey(key_type);
151         int rval;
152         int failed_cnt = 0;
153         for(int key = 1; key <= count; key++) {
154             key_str = tkey.get_key(key);
155             rval = db_r.Find(key_str, mbd);
156
157             if(key == remove_index) continue;
158             if(rval != MBError::SUCCESS) {
159                 failed_cnt++;
160                 continue;
161             }
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)) {
165                     failed_cnt++;
166                 }
167             }
168         }
169
170         db_r.Close();
171         return failed_cnt;
172     }
173
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());
177         std::string key_str;
178         MBData mbd;
179         TestKey tkey(key_type);
180         int rval;
181         int failed_cnt = 0;
182         for(int key = 1; key <= count; key++) {
183             key_str = tkey.get_key(key);
184             rval = db_r.Find(key_str, mbd);
185
186             if(check_even) {
187                 if(key % 2 == 1) continue;
188             } else {
189                 if(key % 2 == 0) continue;
190             }
191             if(rval != MBError::SUCCESS) {
192                 failed_cnt++;
193                 continue;
194             }
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)) {
198                     failed_cnt++;
199                 }
200             }
201         }
202
203         db_r.Close();
204         return failed_cnt;
205     }
206
207 protected:
208     DB *db;
209     int remove_index;
210     int key_type;
211 };
212
213 TEST_F(AbnormalExitTest, KEY_TYPE_INT_test)
214 {
215     int count = 32331;
216     int failed_cnt;
217     int rval;
218
219     key_type = MABAIN_TEST_KEY_TYPE_INT;
220
221     Populate(count);
222     SimulateAbnormalExit(EXCEP_STATUS_ADD_EDGE);
223     failed_cnt = CheckDBConcistency(count);
224     std::cout << "failed count before recovery: " << failed_cnt << "\n";
225
226     rval = RecoverDB();
227     EXPECT_EQ(rval, MBError::SUCCESS);
228
229     failed_cnt = CheckDBConcistency(count);
230     EXPECT_EQ(failed_cnt, 0);
231 }
232
233 TEST_F(AbnormalExitTest, KEY_TYPE_SHA1_test)
234 {
235     int count = 18293;
236     int failed_cnt;
237     int rval;
238
239     key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
240
241     Populate(count);
242     SimulateAbnormalExit(EXCEP_STATUS_ADD_EDGE);
243     failed_cnt = CheckDBConcistency(count);
244     std::cout << "failed count before recovery: " << failed_cnt << "\n";
245
246     rval = RecoverDB();
247     EXPECT_EQ(rval, MBError::SUCCESS);
248
249     failed_cnt = CheckDBConcistency(count);
250     EXPECT_EQ(failed_cnt, 0);
251 }
252
253 TEST_F(AbnormalExitTest, KEY_TYPE_SHA256_test)
254 {
255     int count = 5293;
256     int failed_cnt;
257     int rval;
258
259     key_type = MABAIN_TEST_KEY_TYPE_SHA_256;
260
261     Populate(count);
262     SimulateAbnormalExit(EXCEP_STATUS_ADD_EDGE);
263     failed_cnt = CheckDBConcistency(count);
264     std::cout << "failed count before recovery: " << failed_cnt << "\n";
265
266     rval = RecoverDB();
267     EXPECT_EQ(rval, 0);
268
269     failed_cnt = CheckDBConcistency(count);
270     EXPECT_EQ(failed_cnt, 0);
271 }
272
273 TEST_F(AbnormalExitTest, KEY_TYPE_SHA1_ADD_DATA_OFF_test)
274 {
275     int count = 18293;
276     int failed_cnt;
277     int rval;
278
279     key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
280
281     Populate(count);
282     SimulateAbnormalExit(EXCEP_STATUS_ADD_DATA_OFF);
283     failed_cnt = CheckDBConcistency(count);
284     std::cout << "failed count before recovery: " << failed_cnt << "\n";
285
286     rval = RecoverDB();
287     EXPECT_EQ(rval, MBError::SUCCESS);
288
289     failed_cnt = CheckDBConcistency(count);
290     EXPECT_EQ(failed_cnt, 0);
291 }
292
293 TEST_F(AbnormalExitTest, KEY_TYPE_INT_ADD_NODE_test)
294 {
295     int count = 1829;
296     int failed_cnt;
297     int rval;
298
299     key_type = MABAIN_TEST_KEY_TYPE_INT;
300
301     Populate(count);
302     SimulateAbnormalExit(EXCEP_STATUS_ADD_NODE);
303     failed_cnt = CheckDBConcistency(count);
304     std::cout << "failed count before recovery: " << failed_cnt << "\n";
305
306     rval = RecoverDB();
307     EXPECT_EQ(rval, MBError::SUCCESS);
308
309     failed_cnt = CheckDBConcistency(count);
310     EXPECT_EQ(failed_cnt, 0);
311 }
312
313 TEST_F(AbnormalExitTest, KEY_TYPE_INT_REMOVE_test)
314 {
315     int count = 23234;
316     int failed_cnt;
317     int rval;
318
319     key_type = MABAIN_TEST_KEY_TYPE_INT;
320     remove_index = 2345;
321
322     Populate(count);
323     SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
324     failed_cnt = CheckDBConcistency(count);
325     std::cout << "failed count before recovery: " << failed_cnt << "\n";
326
327     rval = RecoverDB();
328     EXPECT_EQ(rval, 0);
329
330     failed_cnt = CheckDBConcistency(count);
331     EXPECT_EQ(failed_cnt, 0);
332 }
333
334 TEST_F(AbnormalExitTest, KEY_TYPE_INT_REMOVE_test_1)
335 {
336     int count = 23234;
337     int failed_cnt;
338     int rval;
339
340     key_type = MABAIN_TEST_KEY_TYPE_INT;
341     remove_index = 1;
342
343     Populate(count);
344     SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
345     failed_cnt = CheckDBConcistency(count);
346     std::cout << "failed count before recovery: " << failed_cnt << "\n";
347
348     rval = RecoverDB();
349     EXPECT_EQ(rval, MBError::SUCCESS);
350
351     failed_cnt = CheckDBConcistency(count);
352     EXPECT_EQ(failed_cnt, 0);
353 }
354
355 TEST_F(AbnormalExitTest, KEY_TYPE_SHA_128_REMOVE_test)
356 {
357     int count = 3934;
358     int failed_cnt;
359     int rval;
360
361     key_type = MABAIN_TEST_KEY_TYPE_INT;
362     remove_index = 1021;
363
364     Populate(count);
365     SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
366     failed_cnt = CheckDBConcistency(count);
367     std::cout << "failed count before recovery: " << failed_cnt << "\n";
368
369     rval = RecoverDB();
370     EXPECT_EQ(rval, MBError::SUCCESS);
371
372     failed_cnt = CheckDBConcistency(count);
373     EXPECT_EQ(failed_cnt, 0);
374 }
375
376 TEST_F(AbnormalExitTest, KEY_TYPE_INT_REMOVE_ODD_test)
377 {
378     int count = 13234;
379     int failed_cnt;
380     int rval;
381
382     key_type = MABAIN_TEST_KEY_TYPE_INT;
383
384     Populate(count);
385     for(int k = 1; k <= count; k++) {
386         if(k % 2 == 0) continue;
387
388         remove_index = k;
389         SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
390         rval = RecoverDB();
391         EXPECT_EQ(rval, MBError::SUCCESS);
392     } 
393
394     failed_cnt = CheckHalfDBConsistency(count, true);
395     EXPECT_EQ(failed_cnt, 0);
396 }
397
398 TEST_F(AbnormalExitTest, KEY_TYPE_SHA_256_REMOVE_EVEN_test)
399 {
400     int count = 13234;
401     int failed_cnt;
402     int rval;
403
404     key_type = MABAIN_TEST_KEY_TYPE_SHA_256;
405
406     Populate(count); 
407     for(int k = 1; k <= count; k++) {
408         if(k % 2 == 1) continue;
409
410         remove_index = k;
411         SimulateAbnormalExit(EXCEP_STATUS_CLEAR_EDGE);
412         rval = RecoverDB();
413         EXPECT_EQ(rval, MBError::SUCCESS);
414     }
415
416     failed_cnt = CheckHalfDBConsistency(count, false);
417     EXPECT_EQ(failed_cnt, 0);
418 }
419
420 TEST_F(AbnormalExitTest, RC_RECOVERY_test)
421 {
422     int count = 1123;
423     key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
424     Populate(count);
425
426     Dict *dict = db->GetDictPtr();
427     IndexHeader *header = dict->GetHeaderPtr();
428
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;
434     }
435     if(header->pending_data_buff_size == 0) {
436         header->pending_data_buff_size = 100U;
437     }
438     header->m_index_offset += 150LL*1024*1024;
439     header->m_data_offset += 92LL*1024*1024;
440     // Now add some more
441     int count1 = 15846;
442     TestKey tkey(key_type);
443     std::string key_str;
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";
449         }
450     }
451
452     ResourceCollection rc(*db);
453     rc.ExceptionRecovery();
454
455     // Verify
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);
465 }
466
467 TEST_F(AbnormalExitTest, RC_RECOVERY_RC_ROOT_TREE_test)
468 {
469     int count = 23451;
470     key_type = MABAIN_TEST_KEY_TYPE_SHA_128;
471     Populate(count);
472
473     Dict *dict = db->GetDictPtr();
474     IndexHeader *header = dict->GetHeaderPtr();
475     DictMem *dmm = dict->GetMM();
476
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;
482     }
483     if(header->pending_data_buff_size == 0) {
484         header->pending_data_buff_size = 100U;
485     }
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);
490
491     // Now add some more
492     int count1 = 2233;
493     TestKey tkey(key_type);
494     std::string key_str;
495     MBData mbd;
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();
501
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";
505         }
506         mbd.buff = NULL;
507     }
508
509     ResourceCollection rc(*db);
510     rc.ExceptionRecovery();
511
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);
522 }
523
524 }