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 <stdlib.h>
43 #include <unistd.h>
44 #include <memory.h>
45 #include <sys/stat.h>
46 #include <db.h>
47 #include <ft/txn/xids.h>
48 #define MAX_NEST MAX_NESTED_TRANSACTIONS
49
50
51 /*********************
52 *
53 * Purpose of this test is to exercise nested transactions in a basic way:
54 * Create MAX nested transactions, inserting a value at each level, verify:
55 *
56 * for i = 1 to MAX
57 * - txnid = begin()
58 * - txns[i] = txnid
59 * - insert, query
60 *
61 * for i = 1 to MAX
62 * - txnid = txns[MAX - i - 1]
63 * - commit or abort(txnid), query
64 *
65 */
66
67 static DB *db;
68 static DB_ENV *env;
69
70 static void
setup_db(void)71 setup_db (void) {
72 int r;
73 toku_os_recursive_delete(TOKU_TEST_FILENAME);
74 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
75
76 r = db_env_create(&env, 0); CKERR(r);
77 r = env->set_default_bt_compare(env, int_dbt_cmp); CKERR(r);
78 r = env->open(env, TOKU_TEST_FILENAME, DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_LOCK | DB_INIT_TXN | DB_PRIVATE | DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO);
79 CKERR(r);
80
81 {
82 DB_TXN *txn = 0;
83 r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
84
85 r = db_create(&db, env, 0); CKERR(r);
86 r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
87 r = txn->commit(txn, 0); CKERR(r);
88 }
89 }
90
91
92 static void
close_db(void)93 close_db (void) {
94 int r;
95 r=db->close(db, 0); CKERR(r);
96 r=env->close(env, 0); CKERR(r);
97 }
98
99 static void
test_txn_nesting(int depth)100 test_txn_nesting (int depth) {
101 int r;
102 if (verbose) { fprintf(stderr, "%s (%s):%d [depth = %d]\n", __FILE__, __FUNCTION__, __LINE__, depth); fflush(stderr); }
103
104 DBT key, val, observed_val;
105 dbt_init(&observed_val, NULL, 0);
106 int i;
107
108 DB_TXN * txns[depth];
109 DB_TXN * parent = NULL;
110
111 int vals[depth];
112
113 int mykey = 42;
114 dbt_init(&key, &mykey, sizeof mykey);
115
116
117 for (i = 0; i < depth; i++){
118 DB_TXN * this_txn;
119
120 if (verbose)
121 printf("Begin txn at level %d\n", i);
122 vals[i] = i;
123 dbt_init(&val, &vals[i], sizeof i);
124 r = env->txn_begin(env, parent, &this_txn, 0); CKERR(r);
125 txns[i] = this_txn;
126 parent = this_txn; // will be parent in next iteration
127 r = db->put(db, this_txn, &key, &val, 0); CKERR(r);
128
129 r = db->get(db, this_txn, &key, &observed_val, 0); CKERR(r);
130 assert(int_dbt_cmp(db, &val, &observed_val) == 0);
131 }
132
133 int which_val = depth-1;
134 for (i = depth-1; i >= 0; i--) {
135 //Query, verify the correct value is stored.
136 //Close (abort/commit) innermost transaction
137
138 if (verbose)
139 printf("Commit txn at level %d\n", i);
140
141 dbt_init(&observed_val, NULL, 0);
142 r = db->get(db, txns[i], &key, &observed_val, 0); CKERR(r);
143 dbt_init(&val, &vals[which_val], sizeof i);
144 assert(int_dbt_cmp(db, &val, &observed_val) == 0);
145
146 if (i % 2) {
147 r = txns[i]->commit(txns[i], DB_TXN_NOSYNC); CKERR(r);
148 //which_val does not change (it gets promoted)
149 }
150 else {
151 r = txns[i]->abort(txns[i]); CKERR(r);
152 which_val = i - 1;
153 }
154 txns[i] = NULL;
155 }
156 //Query, verify the correct value is stored.
157 r = db->get(db, NULL, &key, &observed_val, 0);
158 if (which_val == -1) CKERR2(r, DB_NOTFOUND);
159 else {
160 CKERR(r);
161 dbt_init(&val, &vals[which_val], sizeof i);
162 assert(int_dbt_cmp(db, &val, &observed_val) == 0);
163 }
164 }
165
166 int
test_main(int argc,char * const argv[])167 test_main(int argc, char *const argv[]) {
168 parse_args(argc, argv);
169 setup_db();
170 test_txn_nesting(MAX_NEST);
171 close_db();
172 return 0;
173 }
174