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>
19 // Multiple writers/readers access the DB concurrently.
29 #include <openssl/sha.h>
32 #include <mabain/db.h>
35 #define REMOVE_ONE_BY_ONE 1
39 #define ITERATOR_TEST 5
40 #define KEY_TYPE_INT 0
41 #define KEY_TYPE_SHA256 1
43 const char* mb_dir = "/var/tmp/mabain_test";
45 using namespace mabain;
47 static int stop_processing = 0;
48 static int num_keys = 1000000;
49 static int test_type = 0;
50 static int64_t memcap_index = 256*1024*1024LL;
51 static int64_t memcap_data = 256*1024*1024LL;
52 static int key_type = KEY_TYPE_SHA256;
53 //static int key_type = KEY_TYPE_INT;
54 static std::map<std::string,int> checked;
56 static const char* get_sha256_str(int key);
58 static void SetTestStatus(bool success, bool stop_test = true)
62 cmd = std::string("touch ") + mb_dir + "/_success";
64 cmd = std::string("rm ") + mb_dir + "/_success";
66 if(system(cmd.c_str()) != 0) {
73 static void RemoveRandom(int rm_cnt, DB &db)
78 while(cnt <= rm_cnt) {
81 ikey = rand() % num_keys + 1;
84 kv = get_sha256_str(ikey);
88 kv = std::string("key") + std::to_string(ikey);
94 if(cnt % 1000000 == 0)
95 std::cout << "Removed " << cnt << "\n";
99 static void PopulateDB(DB &db)
104 while(nkeys < num_keys) {
108 case KEY_TYPE_SHA256:
109 kv = get_sha256_str(nkeys);
113 kv = std::string("key") + std::to_string(nkeys);
117 rval = db.Add(kv, kv);
118 if(rval != MBError::SUCCESS)
120 std::cout << "failed to add " << kv << " " << rval << "\n";
121 SetTestStatus(false);
125 if(nkeys % 1000000 == 0)
126 std::cout << "populate db: " << " inserted " << nkeys << " keys\n";
130 static void RemoveHalf(DB &db)
135 while(nkeys < num_keys) {
137 if(nkeys % 1000000 == 0)
138 std::cout << "Removed " << nkeys/2 << "\n";
143 case KEY_TYPE_SHA256:
144 kv = get_sha256_str(nkeys);
148 kv = std::string("key") + std::to_string(nkeys);
151 rval = db.Remove(kv.c_str(), kv.length());
153 if(rval != MBError::SUCCESS)
155 std::cout << "failed to remove " << nkeys << " " << kv << " " << rval << "\n";
156 SetTestStatus(false);
162 static void Writer(int id)
164 DB db(mb_dir, CONSTS::WriterOptions(), memcap_index, memcap_data);
166 std::cerr << "writer " << id << " failed to open mabain db: "
167 << db.StatusStr() << "\n";
171 if(test_type == SHRINK_TEST) {
172 db.CollectResource(0, 0);
182 while(!stop_processing) {
186 case KEY_TYPE_SHA256:
187 kv = get_sha256_str(nkeys);
191 kv = std::string("key") + std::to_string(nkeys);
195 if(test_type == REMOVE_ONE_BY_ONE) {
196 rval = db.Remove(kv.c_str(), kv.length());
197 if(rval != MBError::SUCCESS && rval != MBError::NOT_EXIST) {
198 std::cout << "failed to remove " << kv << "\n";
199 SetTestStatus(false);
203 } else if(test_type == REMOVE_ALL) {
204 if(nkeys == int(num_keys * 0.666666)) {
208 rval = db.Add(kv, kv);
209 if(rval != MBError::SUCCESS && rval != MBError::IN_DICT) {
210 std::cout << "failed to add " << kv << " " << rval << "\n";
211 SetTestStatus(false);
212 } else if(rval == MBError::SUCCESS) {
217 if(nkeys % 1000000 == 0)
218 std::cout << "writer " << id << " looped over " << nkeys << " keys\n";
219 if(nkeys >= num_keys) break;
227 static void Reader(int id)
229 DB db(mb_dir, CONSTS::ReaderOptions(), memcap_index, memcap_data);
231 std::cerr << "reader " << id << " failed to open mabain db: "
232 << db.StatusStr() << "\n";
233 SetTestStatus(false);
237 if(test_type == ITERATOR_TEST) {
239 for(DB::iterator iter = db.begin(); iter != db.end(); ++iter) {
241 if(iter.key != std::string((char*)iter.value.buff, iter.value.data_len)) {
242 std::cout << "VALUE NOT MATCH " << iter.key << ": "
243 << std::string((char*)iter.value.buff, iter.value.data_len) << "\n";
244 SetTestStatus(false);
246 if(checked[iter.key] == 1)
251 for(int i = 1; i <= num_keys; i++) {
253 case KEY_TYPE_SHA256:
254 key = get_sha256_str(i);
258 key = std::string("key") + std::to_string(i);
261 if(!(checked[key] == 0 || checked[key] == 2)) {
262 std::cerr << i << ": " << key << " " << checked[key] << " for id " << id << "\n";
263 SetTestStatus(false);
273 while(!stop_processing) {
275 case KEY_TYPE_SHA256:
276 key = get_sha256_str(ikey);
280 key = std::string("key") + std::to_string(ikey);
283 rval = db.Find(key, mb_data);
285 if(rval == MBError::SUCCESS) {
286 // check the value read from DB
287 if(memcmp(key.c_str(), mb_data.buff, mb_data.data_len)) {
288 std::cout << "READER " << id << " VALUE DOES NOT MATCH: "
289 << key << ":" << mb_data.match_len << " "
290 << std::string((const char *)mb_data.buff, mb_data.data_len) << "\n";
291 SetTestStatus(false);
297 else if(rval == MBError::NOT_EXIST) {
300 std::cerr << "ERROR: " << MBError::get_error_str(rval) << "\n";
301 SetTestStatus(false);
304 if(ikey % 1000000 == 0)
305 std::cout << "reader " << id << " looked up " << ikey << " keys\n";
307 if(ikey > num_keys) break;
315 static char sha256_str[65];
316 static const char* get_sha256_str(int key)
318 unsigned char hash[SHA256_DIGEST_LENGTH];
320 SHA256_Init(&sha256);
321 SHA256_Update(&sha256, (unsigned char*)&key, 4);
322 SHA256_Final(hash, &sha256);
324 for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
326 sprintf(sha256_str + (i * 2), "%02x", hash[i]);
329 return (const char*)sha256_str;
332 int main(int argc, char *argv[])
334 int num_writer = 1; // There can be one writer at most.
339 mabain::DB::SetLogFile("/var/tmp/mabain_test/mabain.log");
341 const char *test_tp = argv[1];
342 if(strcmp(test_tp, "add") == 0)
343 test_type = ADD_TEST;
344 else if(strcmp(test_tp, "remove") == 0)
345 test_type = REMOVE_ONE_BY_ONE;
346 else if(strcmp(test_tp, "remove-all") == 0)
347 test_type = REMOVE_ALL;
348 else if(strcmp(test_tp, "shrink") == 0)
349 test_type = SHRINK_TEST;
350 else if(strcmp(test_tp, "lookup") == 0)
351 test_type = LOOKUP_TEST;
352 else if(strcmp(test_tp, "iterator") == 0)
353 test_type = ITERATOR_TEST;
354 num_keys = atoi(argv[2]);
355 num_reader = atoi(argv[3]);
361 SetTestStatus(false, false);
363 test_type = ADD_TEST;
365 // Delete all keys from last run.
366 DB db(mb_dir, CONSTS::WriterOptions(), memcap_index, memcap_data);
368 std::cerr << " failed to open mabain db: "
369 << db.StatusStr() << "\n";
377 RemoveRandom(int(num_keys * 0.7777777), db);
384 case REMOVE_ONE_BY_ONE:
389 RemoveRandom(int(num_keys * 0.666666), db);
397 std::vector<pid_t> pid_arr;
398 pid_t test_pid = getpid();
400 if(test_type == LOOKUP_TEST)
402 assert(num_writer < 2);
403 struct timeval start_tm;
404 gettimeofday(&start_tm,NULL);
405 for(int i = 0; i < num_writer; i++) {
406 if(getpid() != test_pid) continue;
411 if(getpid() == test_pid) {
412 pid_arr.push_back(pid);
416 // Start child/writer process
420 for(int i = 0; i < num_reader; i++) {
421 if(getpid() != test_pid) continue;
426 if(getpid() == test_pid) {
427 pid_arr.push_back(pid);
431 // Start child/reader process
435 if(test_pid == getpid()) {
438 while(!stop_processing) {
439 // Check if all children exited
440 for(i = 0; i < (int) pid_arr.size(); i++) {
442 waitpid(pid_arr[i], &status, WUNTRACED | WCONTINUED);
445 if(i == (int) pid_arr.size()-1) {
447 struct timeval stop_tm;
448 gettimeofday(&stop_tm,NULL);
449 //std::cout << "All children have exited!\n";
450 //std::cout << "time: " << ((stop_tm.tv_sec-start_tm.tv_sec)*1000000.0 +
451 // (stop_tm.tv_usec-start_tm.tv_usec))/num_keys << "\n";
460 mabain::DB::CloseLogFile();
461 SetTestStatus(true, false);