summaryrefslogtreecommitdiff
path: root/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc')
-rw-r--r--storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc212
1 files changed, 212 insertions, 0 deletions
diff --git a/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc b/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc
new file mode 100644
index 00000000000..2d4e9ba14ea
--- /dev/null
+++ b/storage/tokudb/PerconaFT/src/tests/test_3529_table_lock.cc
@@ -0,0 +1,212 @@
+/* -*- 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"
+
+// verify that serializable cursor locks deleted keys so that another transaction can not insert into the range being scanned by the cursor
+// we create 2 level tree that looks like
+// root node with pivot key 2
+// left leaf contains keys 0, 1, and 2
+// right leaf contains keys 3 and 4
+// we delete keys 0, 1, and 2 while a snapshot txn exist so that garbage collection does not occur.
+// txn_a walks a cursor through the deleted keys.
+// when txn_a finishes reading the deleted keys, txn_b tries to get a table lock.
+// the table lock should fail since txn_a holds a read lock on the deleted key range.
+
+#include <db.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static DB_ENV *env = NULL;
+static DB_TXN *txn_a = NULL;
+static DB_TXN *txn_b = NULL;
+static DB *db = NULL;
+static uint32_t db_page_size = 4096;
+// static uint32_t db_basement_size = 4096;
+static const char *envdir = TOKU_TEST_FILENAME;
+
+static int
+my_compare(DB *this_db UU(), const DBT *a UU(), const DBT *b UU()) {
+ assert(a->size == b->size);
+ return memcmp(a->data, b->data, a->size);
+}
+
+static int
+my_generate_row(DB *dest_db UU(), DB *src_db UU(), DBT_ARRAY *dest_key_arrays UU(), DBT_ARRAY *dest_val_arrays UU(), const DBT *src_key UU(), const DBT *src_val UU()) {
+ toku_dbt_array_resize(dest_key_arrays, 1);
+ toku_dbt_array_resize(dest_val_arrays, 1);
+ DBT *dest_key = &dest_key_arrays->dbts[0];
+ DBT *dest_val = &dest_val_arrays->dbts[0];
+ assert(dest_key->flags == DB_DBT_REALLOC);
+ dest_key->data = toku_realloc(dest_key->data, src_key->size);
+ memcpy(dest_key->data, src_key->data, src_key->size);
+ dest_key->size = src_key->size;
+ assert(dest_val->flags == DB_DBT_REALLOC);
+ dest_val->data = toku_realloc(dest_val->data, src_val->size);
+ memcpy(dest_val->data, src_val->data, src_val->size);
+ dest_val->size = src_val->size;
+ return 0;
+}
+
+static int
+next_do_nothing(DBT const *UU(a), DBT const *UU(b), void *UU(c)) {
+ return 0;
+}
+
+static ssize_t
+my_pread (int fd, void *buf, size_t count, off_t offset) {
+ static int my_pread_count = 0;
+ if (++my_pread_count == 5) {
+ // try to acquire a table lock, should fail
+ int r = db->pre_acquire_table_lock(db, txn_b);
+ assert(r == DB_LOCK_NOTGRANTED);
+ }
+ return pread(fd, buf, count, offset);
+}
+
+static void
+run_test(void) {
+ int r;
+ r = db_env_create(&env, 0); CKERR(r);
+ env->set_errfile(env, stderr);
+ r = env->set_redzone(env, 0); CKERR(r);
+ r = env->set_generate_row_callback_for_put(env, my_generate_row); CKERR(r);
+ r = env->set_default_bt_compare(env, my_compare); CKERR(r);
+ r = env->open(env, envdir, DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_MPOOL|DB_INIT_TXN|DB_CREATE|DB_PRIVATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ r = db_create(&db, env, 0); CKERR(r);
+ r = db->set_pagesize(db, db_page_size);
+ DB_TXN *txn = NULL;
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // build a tree with 2 leaf nodes
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ DB_LOADER *loader = NULL;
+ r = env->create_loader(env, txn, &loader, db, 1, &db, NULL, NULL, 0); CKERR(r);
+ for (uint64_t i = 0; i < 5; i++) {
+ uint64_t key = i;
+ char val[800]; memset(val, 0, sizeof val);
+ DBT k,v;
+ r = loader->put(loader, dbt_init(&k, &key, sizeof key), dbt_init(&v, val, sizeof val)); CKERR(r);
+ }
+ r = loader->close(loader); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // this transaction ensure that garbage collection does not occur when deleting
+ DB_TXN *bogus_txn = NULL;
+ r = env->txn_begin(env, 0, &bogus_txn, DB_TXN_SNAPSHOT); CKERR(r);
+
+ // delete the keys in the first leaf node
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ for (uint64_t i = 0; i < 3; i++) {
+ uint64_t key = i;
+ DBT k;
+ r = db->del(db, txn, dbt_init(&k, &key, sizeof key), 0); CKERR(r);
+ }
+ r = txn->commit(txn, 0); CKERR(r);
+ r = bogus_txn->commit(bogus_txn, 0); CKERR(r);
+
+ // close and reopen
+ r = db->close(db, 0); CKERR(r);
+ r = db_create(&db, env, 0); CKERR(r);
+ r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
+ r = db->open(db, txn, "foo.db", 0, DB_BTREE, 0, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+ r = txn->commit(txn, 0); CKERR(r);
+
+ // create a txn that will try to acquire a write lock on key 0 in the pread callback
+ r = env->txn_begin(env, 0, &txn_b, 0); CKERR(r);
+
+ // walk a serializable cursor through the tree
+ r = env->txn_begin(env, 0, &txn_a, 0); CKERR(r);
+ DBC *cursor = NULL;
+ r = db->cursor(db, txn_a, &cursor, 0); CKERR(r);
+ db_env_set_func_pread(my_pread);
+ while (1) {
+ r = cursor->c_getf_next(cursor, 0, next_do_nothing, NULL);
+ if (r != 0)
+ break;
+ }
+ db_env_set_func_pread(NULL);
+ r = cursor->c_close(cursor); CKERR(r);
+ r = txn_a->commit(txn_a, 0); CKERR(r);
+
+ r = txn_b->commit(txn_b, 0); CKERR(r);
+
+ r = db->close(db, 0); CKERR(r);
+ r = env->close(env, 0); CKERR(r);
+}
+
+static int
+usage(void) {
+ fprintf(stderr, "-v (verbose)\n");
+ fprintf(stderr, "-q (quiet)\n");
+ fprintf(stderr, "--envdir %s\n", envdir);
+ return 1;
+}
+
+int
+test_main (int argc , char * const argv[]) {
+ for (int i = 1 ; i < argc; i++) {
+ if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
+ verbose++;
+ continue;
+ }
+ if (strcmp(argv[i], "-q") == 0) {
+ if (verbose > 0)
+ verbose--;
+ continue;
+ }
+ if (strcmp(argv[i], "--envdir") == 0 && i+1 < argc) {
+ envdir = argv[++i];
+ continue;
+ }
+ return usage();
+ }
+
+ char rmcmd[32 + strlen(envdir)];
+ snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", envdir);
+ int r;
+ r = system(rmcmd); CKERR(r);
+ r = toku_os_mkdir(envdir, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
+
+ run_test();
+
+ return 0;
+}