1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6
7
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22 ----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39 #include "test.h"
40 #include <stdio.h>
41
42 #include <sys/stat.h>
43 #include <db.h>
44
45 // Tests that the logical row counts are correct and not subject to variance
46 // due to normal insert/delete messages within the tree with the few exceptions
47 // of 1) rollback messages not yet applied; 2) inserts messages turned to
48 // updates on apply; and 3) missing leafentries on delete messages on apply.
49
50 static DB_TXN* const null_txn = 0;
51 static const uint64_t num_records = 4*1024;
52
53 #define CHECK_NUM_ROWS(_expected, _stats) assert(_stats.bt_ndata == _expected)
54
create_db(const char * fname,DB_ENV * env)55 static DB* create_db(const char* fname, DB_ENV* env) {
56 int r;
57 DB* db;
58
59 r = db_create(&db, env, 0);
60 assert(r == 0);
61 db->set_errfile(db, stderr);
62
63 r = db->set_pagesize(db, 8192);
64 assert(r == 0);
65
66 r = db->set_readpagesize(db, 1024);
67 assert(r == 0);
68
69 r = db->set_fanout(db, 4);
70 assert(r == 0);
71
72 r = db->set_compression_method(db, TOKU_NO_COMPRESSION);
73 assert(r == 0);
74
75 r = db->open(db, null_txn, fname, "main", DB_BTREE, DB_CREATE,
76 0666);
77 assert(r == 0);
78
79 return db;
80 }
add_records(DB * db,DB_TXN * txn,uint64_t start_id,uint64_t num)81 static void add_records(DB* db, DB_TXN* txn, uint64_t start_id, uint64_t num) {
82 int r;
83 for (uint64_t i = 0, j=start_id; i < num; i++,j++) {
84 char key[100], val[256];
85 DBT k,v;
86 snprintf(key, 100, "%08" PRIu64, j);
87 snprintf(val, 256, "%*s", 200, key);
88 r =
89 db->put(
90 db,
91 txn,
92 dbt_init(&k, key, 1+strlen(key)),
93 dbt_init(&v, val, 1+strlen(val)),
94 0);
95 assert(r == 0);
96 }
97 }
delete_records(DB * db,DB_TXN * txn,uint64_t start_id,uint64_t num)98 static void delete_records(
99 DB* db,
100 DB_TXN* txn,
101 uint64_t start_id,
102 uint64_t num) {
103
104 int r;
105 for (uint64_t i = 0, j=start_id; i < num; i++,j++) {
106 char key[100];
107 DBT k;
108 snprintf(key, 100, "%08" PRIu64, j);
109 r =
110 db->del(
111 db,
112 txn,
113 dbt_init(&k, key, 1+strlen(key)),
114 0);
115 assert(r == 0);
116 }
117 }
full_optimize(DB * db)118 static void full_optimize(DB* db) {
119 int r;
120 uint64_t loops_run = 0;
121
122 r = db->optimize(db);
123 assert(r == 0);
124
125 r = db->hot_optimize(db, NULL, NULL, NULL, NULL, &loops_run);
126 assert(r == 0);
127 }
test_insert_commit(DB_ENV * env)128 static void test_insert_commit(DB_ENV* env) {
129 int r;
130 DB* db;
131 DB_TXN* txn;
132 DB_BTREE_STAT64 stats;
133
134 db = create_db(__FUNCTION__, env);
135
136 r = env->txn_begin(env, null_txn, &txn, 0);
137 assert(r == 0);
138
139 add_records(db, txn, 0, num_records);
140
141 r = db->stat64(db, null_txn, &stats);
142 assert(r == 0);
143
144 CHECK_NUM_ROWS(num_records, stats);
145 if (verbose)
146 printf("%s : before commit %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
147
148 r = txn->commit(txn, 0);
149 assert(r == 0);
150
151 r = db->stat64(db, null_txn, &stats);
152 assert(r == 0);
153
154 CHECK_NUM_ROWS(num_records, stats);
155 if (verbose)
156 printf("%s : after commit %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
157
158 db->close(db, 0);
159 }
test_insert_delete_commit(DB_ENV * env)160 static void test_insert_delete_commit(DB_ENV* env) {
161 int r;
162 DB* db;
163 DB_TXN* txn;
164 DB_BTREE_STAT64 stats;
165
166 db = create_db(__FUNCTION__, env);
167
168 r = env->txn_begin(env, null_txn, &txn, 0);
169 assert(r == 0);
170
171 add_records(db, txn, 0, num_records);
172
173 r = db->stat64(db, null_txn, &stats);
174 assert(r == 0);
175
176 CHECK_NUM_ROWS(num_records, stats);
177 if (verbose)
178 printf("%s : before delete %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
179
180 delete_records(db, txn, 0, num_records);
181
182 r = db->stat64(db, null_txn, &stats);
183 assert(r == 0);
184
185 CHECK_NUM_ROWS(0, stats);
186 if (verbose)
187 printf("%s : after delete %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
188
189 r = txn->commit(txn, 0);
190 assert(r == 0);
191
192 r = db->stat64(db, null_txn, &stats);
193 assert(r == 0);
194
195 CHECK_NUM_ROWS(0, stats);
196 if (verbose)
197 printf("%s : after commit %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
198
199 db->close(db, 0);
200 }
test_insert_commit_delete_commit(DB_ENV * env)201 static void test_insert_commit_delete_commit(DB_ENV* env) {
202 int r;
203 DB* db;
204 DB_TXN* txn;
205 DB_BTREE_STAT64 stats;
206
207 db = create_db(__FUNCTION__, env);
208
209 r = env->txn_begin(env, null_txn, &txn, 0);
210 assert(r == 0);
211
212 add_records(db, txn, 0, num_records);
213
214 r = db->stat64(db, null_txn, &stats);
215 assert(r == 0);
216
217 CHECK_NUM_ROWS(num_records, stats);
218 if (verbose)
219 printf(
220 "%s : before insert commit %" PRIu64 " rows\n",
221 __FUNCTION__,
222 stats.bt_ndata);
223
224 r = txn->commit(txn, 0);
225 assert(r == 0);
226
227 r = db->stat64(db, null_txn, &stats);
228 assert(r == 0);
229
230 CHECK_NUM_ROWS(num_records, stats);
231 if (verbose)
232 printf(
233 "%s : after insert commit %" PRIu64 " rows\n",
234 __FUNCTION__,
235 stats.bt_ndata);
236
237 r = env->txn_begin(env, null_txn, &txn, 0);
238 assert(r == 0);
239
240 delete_records(db, txn, 0, num_records);
241
242 r = db->stat64(db, null_txn, &stats);
243 assert(r == 0);
244
245 CHECK_NUM_ROWS(0, stats);
246 if (verbose)
247 printf("%s : after delete %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
248
249 r = txn->commit(txn, 0);
250 assert(r == 0);
251
252 r = db->stat64(db, null_txn, &stats);
253 assert(r == 0);
254
255 CHECK_NUM_ROWS(0, stats);
256 if (verbose)
257 printf(
258 "%s : after delete commit %" PRIu64 " rows\n",
259 __FUNCTION__,
260 stats.bt_ndata);
261
262 db->close(db, 0);
263 }
test_insert_rollback(DB_ENV * env)264 static void test_insert_rollback(DB_ENV* env) {
265 int r;
266 DB* db;
267 DB_TXN* txn;
268 DB_BTREE_STAT64 stats;
269
270 db = create_db(__FUNCTION__, env);
271
272 r = env->txn_begin(env, null_txn, &txn, 0);
273 assert(r == 0);
274
275 add_records(db, txn, 0, num_records);
276
277 r = db->stat64(db, null_txn, &stats);
278 assert(r == 0);
279
280 CHECK_NUM_ROWS(num_records, stats);
281 if (verbose)
282 printf("%s : before rollback %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
283
284 r = txn->abort(txn);
285 assert(r == 0);
286
287 r = db->stat64(db, null_txn, &stats);
288 assert(r == 0);
289
290 // CAN NOT TEST stats HERE AS THEY ARE SOMEWHAT NON_DETERMINISTIC UNTIL
291 // optimize + hot_optimize HAVE BEEN RUN DUE TO THE FACT THAT ROLLBACK
292 // MESSAGES ARE "IN-FLIGHT" IN THE TREE AND MUST BE APPLIED IN ORDER TO
293 // CORRECT THE RUNNING LOGICAL COUNT
294 if (verbose)
295 printf("%s : after rollback %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
296
297 full_optimize(db);
298
299 r = db->stat64(db, null_txn, &stats);
300 assert(r == 0);
301
302 CHECK_NUM_ROWS(0, stats);
303 if (verbose)
304 printf(
305 "%s : after rollback optimize %" PRIu64 " rows\n",
306 __FUNCTION__,
307 stats.bt_ndata);
308
309 db->close(db, 0);
310 }
test_insert_delete_rollback(DB_ENV * env)311 static void test_insert_delete_rollback(DB_ENV* env) {
312 int r;
313 DB* db;
314 DB_TXN* txn;
315 DB_BTREE_STAT64 stats;
316
317 db = create_db(__FUNCTION__, env);
318
319 r = env->txn_begin(env, null_txn, &txn, 0);
320 assert(r == 0);
321
322 add_records(db, txn, 0, num_records);
323
324 r = db->stat64(db, null_txn, &stats);
325 assert(r == 0);
326
327 CHECK_NUM_ROWS(num_records, stats);
328 if (verbose)
329 printf("%s : before delete %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
330
331 delete_records(db, txn, 0, num_records);
332
333 r = db->stat64(db, null_txn, &stats);
334 assert(r == 0);
335
336 CHECK_NUM_ROWS(0, stats);
337 if (verbose)
338 printf("%s : after delete %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
339
340 r = txn->abort(txn);
341 assert(r == 0);
342
343 r = db->stat64(db, null_txn, &stats);
344 assert(r == 0);
345
346 CHECK_NUM_ROWS(0, stats);
347 if (verbose)
348 printf("%s : after commit %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
349
350 db->close(db, 0);
351 }
test_insert_commit_delete_rollback(DB_ENV * env)352 static void test_insert_commit_delete_rollback(DB_ENV* env) {
353 int r;
354 DB* db;
355 DB_TXN* txn;
356 DB_BTREE_STAT64 stats;
357
358 db = create_db(__FUNCTION__, env);
359
360 r = env->txn_begin(env, null_txn, &txn, 0);
361 assert(r == 0);
362
363 add_records(db, txn, 0, num_records);
364
365 r = db->stat64(db, null_txn, &stats);
366 assert(r == 0);
367
368 CHECK_NUM_ROWS(num_records, stats);
369 if (verbose)
370 printf(
371 "%s : before insert commit %" PRIu64 " rows\n",
372 __FUNCTION__,
373 stats.bt_ndata);
374
375 r = txn->commit(txn, 0);
376 assert(r == 0);
377
378 r = db->stat64(db, null_txn, &stats);
379 assert(r == 0);
380
381 CHECK_NUM_ROWS(num_records, stats);
382 if (verbose)
383 printf(
384 "%s : after insert commit %" PRIu64 " rows\n",
385 __FUNCTION__,
386 stats.bt_ndata);
387
388 r = env->txn_begin(env, null_txn, &txn, 0);
389 assert(r == 0);
390
391 delete_records(db, txn, 0, num_records);
392
393 r = db->stat64(db, null_txn, &stats);
394 assert(r == 0);
395
396 CHECK_NUM_ROWS(0, stats);
397 if (verbose)
398 printf("%s : after delete %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
399
400 r = txn->abort(txn);
401 assert(r == 0);
402
403 r = db->stat64(db, null_txn, &stats);
404 assert(r == 0);
405
406 // CAN NOT TEST stats HERE AS THEY ARE SOMEWHAT NON_DETERMINISTIC UNTIL
407 // optimize + hot_optimize HAVE BEEN RUN DUE TO THE FACT THAT ROLLBACK
408 // MESSAGES ARE "IN-FLIGHT" IN THE TREE AND MUST BE APPLIED IN ORDER TO
409 // CORRECT THE RUNNING LOGICAL COUNT
410 if (verbose)
411 printf(
412 "%s : after delete rollback %" PRIu64 " rows\n",
413 __FUNCTION__,
414 stats.bt_ndata);
415
416 full_optimize(db);
417
418 r = db->stat64(db, null_txn, &stats);
419 assert(r == 0);
420
421 CHECK_NUM_ROWS(num_records, stats);
422 if (verbose)
423 printf(
424 "%s : after delete rollback optimize %" PRIu64 " rows\n",
425 __FUNCTION__,
426 stats.bt_ndata);
427
428 db->close(db, 0);
429 }
430
test_recount_insert_commit_progress(uint64_t count,uint64_t deleted,void *)431 static int test_recount_insert_commit_progress(
432 uint64_t count,
433 uint64_t deleted,
434 void*) {
435
436 if (verbose)
437 printf(
438 "%s : count[%" PRIu64 "] deleted[%" PRIu64 "]\n",
439 __FUNCTION__,
440 count,
441 deleted);
442 return 0;
443 }
test_recount_cancel_progress(uint64_t,uint64_t,void *)444 static int test_recount_cancel_progress(uint64_t, uint64_t, void*) {
445 return 1;
446 }
447
test_recount_insert_commit(DB_ENV * env)448 static void test_recount_insert_commit(DB_ENV* env) {
449 int r;
450 DB* db;
451 DB_TXN* txn;
452 DB_BTREE_STAT64 stats;
453
454 db = create_db(__FUNCTION__, env);
455
456 r = env->txn_begin(env, null_txn, &txn, 0);
457 assert(r == 0);
458
459 add_records(db, txn, 0, num_records);
460
461 r = db->stat64(db, null_txn, &stats);
462 assert(r == 0);
463
464 CHECK_NUM_ROWS(num_records, stats);
465 if (verbose)
466 printf(
467 "%s : before commit %" PRIu64 " rows\n",
468 __FUNCTION__,
469 stats.bt_ndata);
470
471 r = txn->commit(txn, 0);
472 assert(r == 0);
473
474 r = db->stat64(db, null_txn, &stats);
475 assert(r == 0);
476
477 CHECK_NUM_ROWS(num_records, stats);
478 if (verbose)
479 printf("%s : after commit %" PRIu64 " rows\n", __FUNCTION__, stats.bt_ndata);
480
481 // test that recount counted correct # of rows
482 r = db->recount_rows(db, test_recount_insert_commit_progress, NULL);
483 assert(r == 0);
484 CHECK_NUM_ROWS(num_records, stats);
485
486 // test that recount callback cancel returns
487 r = db->recount_rows(db, test_recount_cancel_progress, NULL);
488 assert(r == 1);
489 CHECK_NUM_ROWS(num_records, stats);
490
491 db->close(db, 0);
492 }
test_main(int UU (argc),char UU (* const argv[]))493 int test_main(int UU(argc), char UU(*const argv[])) {
494 int r;
495 DB_ENV* env;
496
497 toku_os_recursive_delete(TOKU_TEST_FILENAME);
498 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU + S_IRWXG + S_IRWXO);
499
500 r = db_env_create(&env, 0);
501 assert(r == 0);
502
503 r =
504 env->open(
505 env,
506 TOKU_TEST_FILENAME,
507 DB_INIT_MPOOL + DB_INIT_LOG + DB_INIT_TXN + DB_PRIVATE + DB_CREATE,
508 S_IRWXU + S_IRWXG + S_IRWXO);
509 assert(r == 0);
510
511 test_insert_commit(env);
512 test_insert_delete_commit(env);
513 test_insert_commit_delete_commit(env);
514 test_insert_rollback(env);
515 test_insert_delete_rollback(env);
516 test_insert_commit_delete_rollback(env);
517 test_recount_insert_commit(env);
518
519 r = env->close(env, 0);
520 assert(r == 0);
521
522 return 0;
523 }
524