diff options
Diffstat (limited to 'storage/tokudb/PerconaFT/src/tests/progress.cc')
-rw-r--r-- | storage/tokudb/PerconaFT/src/tests/progress.cc | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/storage/tokudb/PerconaFT/src/tests/progress.cc b/storage/tokudb/PerconaFT/src/tests/progress.cc new file mode 100644 index 00000000000..561da118146 --- /dev/null +++ b/storage/tokudb/PerconaFT/src/tests/progress.cc @@ -0,0 +1,445 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: +#ident "$Id$" +/*====== +This file is part of PerconaFT. + + +Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. + + PerconaFT is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, + as published by the Free Software Foundation. + + PerconaFT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. + +---------------------------------------- + + PerconaFT is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License, version 3, + as published by the Free Software Foundation. + + PerconaFT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with PerconaFT. If not, see <http://www.gnu.org/licenses/>. +======= */ + +#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." + +#include "test.h" + + +/* + - ydb layer test of progress report on commit, abort. + - test1: + create two txns + perform operations (inserts and deletes) + commit or abort inner txn + if abort, verify progress callback was called with correct args + if commit, verify progress callback was not called + commit or abort outer txn + verify progress callback was called with correct args + + Note: inner loop ends with commit, so when outer loop completes, + it should be called for all operations performed by inner loop. + + perform_ops { + for i = 0 -> 5 { + for j = 0 -> 1023 + if (j & 0x20) insert + else op_delete + } + + verify (n) { + verify that callback was called n times with correct args + } + + test1: + for c0 = 0, 1 { + for c1 = 0, 1 { + begin txn0 + perform_ops (txn0) + begin txn1 + perform ops (tnx1) + if c1 + abort txn1 + verify (n) + else + commit txn1 + verify (0) + } + if c0 + abort txn0 + verify (2n) + else + commit txn0 + verify (2n) + } + + + - test2 + - create empty dictionary + - begin txn + - lock empty dictionary (full range lock) + - abort + - verify that callback was called twice, first with stalled-on-checkpoint true, then with stalled-on-checkpoint false + + +*/ + + +#define DICT_0 "dict_0.db" +static DB_ENV *env = NULL; +static DB_TXN *txn_parent = NULL; +static DB_TXN *txn_child = NULL; +static DB_TXN *txn_hold_dname_lock = NULL; +static DB *db; +static const char *dname = DICT_0; +static DBT key; +static DBT val; + + +static void start_txn(void); +static void commit_txn(int); +static void open_db(void); +static void close_db(void); +static void insert(void); +static void op_delete(void); +static void + +start_env(void) { + assert(env==NULL); + int r; + toku_os_recursive_delete(TOKU_TEST_FILENAME); + toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO); + + r = db_env_create(&env, 0); + CKERR(r); + r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); + CKERR(r); + + dname = DICT_0; + + dbt_init(&key, "key", strlen("key")+1); + dbt_init(&val, "val", strlen("val")+1); + + open_db(); + close_db(); +} + +static void +end_env(void) { + int r; + r=env->close(env, 0); + CKERR(r); + env = NULL; +} + +static void +start_txn_prevent_dname_lock(void) { + assert(env!=NULL); + assert(txn_hold_dname_lock==NULL); + int r; + r=env->txn_begin(env, 0, &txn_hold_dname_lock, 0); + CKERR(r); + DB *db2; + + r = db_create(&db2, env, 0); + CKERR(r); + + r=db2->open(db2, txn_hold_dname_lock, dname, 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); + CKERR(r); + r = db2->close(db2, 0); +} + +static void nopoll(TOKU_TXN_PROGRESS UU(progress), void *UU(extra)) { + assert(false); +} + +static void +commit_txn_prevent_dname_lock(void) { + assert(env!=NULL); + assert(txn_hold_dname_lock!=NULL); + int r; + r = txn_hold_dname_lock->commit_with_progress(txn_hold_dname_lock, 0, nopoll, NULL); + CKERR(r); + txn_hold_dname_lock = NULL; +} + +static void +start_txn(void) { + assert(env!=NULL); + int r; + if (!txn_parent) { + r=env->txn_begin(env, 0, &txn_parent, 0); + } + else { + assert(!txn_child); + r=env->txn_begin(env, txn_parent, &txn_child, 0); + } + CKERR(r); +} + +struct progress_expect { + int num_calls; + uint8_t is_commit_expected; + uint8_t stalled_on_checkpoint_expected; + uint64_t min_entries_total_expected; + uint64_t last_entries_processed; +}; + +static void poll(TOKU_TXN_PROGRESS progress, void *extra) { + struct progress_expect *CAST_FROM_VOIDP(info, extra); + info->num_calls++; + assert(progress->is_commit == info->is_commit_expected); + assert(progress->stalled_on_checkpoint == info->stalled_on_checkpoint_expected); + assert(progress->entries_total >= info->min_entries_total_expected); + assert(progress->entries_processed == 1024 + info->last_entries_processed); + info->last_entries_processed = progress->entries_processed; +} + +//expect_number_polls is number of times polling function should be called. +static void +abort_txn(int expect_number_polls) { + assert(env!=NULL); + DB_TXN *txn; + bool child; + if (txn_child) { + txn = txn_child; + child = true; + } + else { + txn = txn_parent; + child = false; + } + assert(txn); + + struct progress_expect extra = { + .num_calls = 0, + .is_commit_expected = 0, + .stalled_on_checkpoint_expected = 0, + .min_entries_total_expected = (uint64_t) expect_number_polls * 1024, + .last_entries_processed = 0 + }; + + int r; + r=txn->abort_with_progress(txn, poll, &extra); + CKERR(r); + assert(extra.num_calls == expect_number_polls); + if (child) + txn_child = NULL; + else + txn_parent = NULL; +} + +static void +commit_txn(int expect_number_polls) { + assert(env!=NULL); + DB_TXN *txn; + bool child; + if (txn_child) { + txn = txn_child; + child = true; + } + else { + txn = txn_parent; + child = false; + } + assert(txn); + if (child) + assert(expect_number_polls == 0); + + struct progress_expect extra = { + .num_calls = 0, + .is_commit_expected = 1, + .stalled_on_checkpoint_expected = 0, + .min_entries_total_expected = (uint64_t) expect_number_polls * 1024, + .last_entries_processed = 0 + }; + + int r; + r=txn->commit_with_progress(txn, 0, poll, &extra); + CKERR(r); + assert(extra.num_calls == expect_number_polls); + if (child) + txn_child = NULL; + else + txn_parent = NULL; +} + +static void +open_db(void) { + assert(env!=NULL); + assert(db == NULL); + + int r; + + r = db_create(&db, env, 0); + CKERR(r); + + r=db->open(db, NULL, dname, 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); + CKERR(r); +} + +static void +close_db(void) { + assert(env!=NULL); + assert(db != NULL); + + int r; + r = db->close(db, 0); + CKERR(r); + db = NULL; +} + +static void +insert(void) { + assert(env!=NULL); + assert(db!=NULL); + DB_TXN *txn = txn_child ? txn_child : txn_parent; + assert(txn); + + int r=db->put(db, txn, + &key, + &val, + 0); + CKERR(r); +} + +static void +op_delete(void) { + assert(env!=NULL); + assert(db!=NULL); + DB_TXN *txn = txn_child ? txn_child : txn_parent; + assert(txn); + + int r=db->del(db, txn, + &key, + DB_DELETE_ANY); + CKERR(r); +} + +static void +perform_ops(int n) { + int i; + int j; + for (i = 0; i < n; i++) { + for (j = 0; j < 1024; j++) { + if (j & 0x20) + op_delete(); + else + insert(); + } + } +} + +static void +progress_test_1(int n, int commit) { + start_env(); + open_db(); + { + start_txn(); + { + start_txn(); + perform_ops(n); + abort_txn(n); + } + { + start_txn(); + perform_ops(n); + commit_txn(0); + } + perform_ops(n); + if (commit) + commit_txn(2*n); + else + abort_txn(2*n); + } + close_db(); + end_env(); +} + +static void +abort_txn_stall_checkpoint(void) { + //We have disabled the norollback log fallback optimization. + //Checkpoint will not stall + assert(env!=NULL); + assert(txn_parent); + assert(!txn_child); + + int r; + r=txn_parent->abort_with_progress(txn_parent, nopoll, NULL); + CKERR(r); + txn_parent = NULL; +} + +static void +abort_txn_nostall_checkpoint(void) { + assert(env!=NULL); + assert(txn_parent); + assert(!txn_child); + + int r; + r=txn_parent->abort_with_progress(txn_parent, nopoll, NULL); + CKERR(r); + txn_parent = NULL; +} + + +static void +lock(void) { + assert(env!=NULL); + assert(db!=NULL); + assert(txn_parent); + assert(!txn_child); + + int r=db->pre_acquire_table_lock(db, txn_parent); + CKERR(r); +} + +static void +progress_test_2(void) { + start_env(); + open_db(); + start_txn(); + start_txn_prevent_dname_lock(); + lock(); + commit_txn_prevent_dname_lock(); + abort_txn_stall_checkpoint(); + close_db(); + end_env(); +} + +static void +progress_test_3(void) { + start_env(); + open_db(); + start_txn(); + lock(); + abort_txn_nostall_checkpoint(); + close_db(); + end_env(); +} + +int +test_main (int argc, char * const argv[]) +{ + parse_args(argc, argv); + int commit; + for (commit = 0; commit <= 1; commit++) { + progress_test_1(4, commit); + } + progress_test_2(); + progress_test_3(); + return 0; +} |