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