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_TRANSACTION_RECORDS
49 #define MAX_SIZE MAX_TRANSACTION_RECORDS
50
51 /*********************
52 *
53 * Purpose of this test is to verify nested transactions (support right number of possible values)
54 create empty db
55 for test = 1 to MAX
56 for nesting level 0
57 - randomly insert or not
58 for nesting_level = 1 to MAX
59 - begin txn
60 - randomly one of (insert, delete, do nothing)
61 - if insert, use a value/len unique to this txn
62 - query to verify
63 for nesting level = MAX to 1
64 - randomly abort or commit each transaction
65 - query to verify
66 delete db
67 */
68
69
70 enum { TYPE_DELETE = 1, TYPE_INSERT, TYPE_PLACEHOLDER };
71
72 uint8_t valbufs[MAX_NEST][MAX_SIZE];
73 DBT vals [MAX_NEST];
74 uint8_t keybuf [MAX_SIZE];
75 DBT key;
76 uint8_t types [MAX_NEST];
77 DB_TXN *txns [MAX_NEST];
78 DB_TXN *txn_query;
79 int which_expected;
80
81 static void
fillrandom(uint8_t buf[MAX_SIZE],uint32_t length)82 fillrandom(uint8_t buf[MAX_SIZE], uint32_t length) {
83 assert(length < MAX_SIZE);
84 uint32_t i;
85 for (i = 0; i < length; i++) {
86 buf[i] = random() & 0xFF;
87 }
88 }
89
90 static void
initialize_values(void)91 initialize_values (void) {
92 int nest_level;
93 for (nest_level = 0; nest_level < MAX_NEST; nest_level++) {
94 fillrandom(valbufs[nest_level], nest_level);
95 dbt_init(&vals[nest_level], &valbufs[nest_level][0], nest_level);
96 }
97 uint32_t len = random() % MAX_SIZE;
98 fillrandom(keybuf, len);
99 dbt_init(&key, &keybuf[0], len);
100 }
101
102
103 static DB *db;
104 static DB_ENV *env;
105
106 static void
setup_db(void)107 setup_db (void) {
108 int r;
109 toku_os_recursive_delete(TOKU_TEST_FILENAME);
110 toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU+S_IRWXG+S_IRWXO);
111
112 r = db_env_create(&env, 0); CKERR(r);
113 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);
114 CKERR(r);
115
116 {
117 DB_TXN *txn = 0;
118 r = env->txn_begin(env, 0, &txn, 0); CKERR(r);
119
120 r = db_create(&db, env, 0); CKERR(r);
121 r = db->open(db, txn, "test.db", 0, DB_BTREE, DB_CREATE, S_IRWXU+S_IRWXG+S_IRWXO); CKERR(r);
122 r = txn->commit(txn, 0); CKERR(r);
123 }
124 r = env->txn_begin(env, NULL, &txn_query, DB_READ_UNCOMMITTED);
125 CKERR(r);
126 }
127
128
129 static void
close_db(void)130 close_db (void) {
131 int r;
132 r = txn_query->commit(txn_query, 0);
133 CKERR(r);
134 r=db->close(db, 0); CKERR(r);
135 r=env->close(env, 0); CKERR(r);
136 }
137
138 static void
verify_val(uint8_t nest_level)139 verify_val(uint8_t nest_level) {
140 assert(nest_level < MAX_NEST);
141 if (types[nest_level] == TYPE_PLACEHOLDER) {
142 assert(nest_level > 0);
143 return verify_val(nest_level - 1);
144 }
145 int r;
146 DBT observed_val;
147 dbt_init(&observed_val, NULL, 0);
148 r = db->get(db, txn_query, &key, &observed_val, 0);
149 if (types[nest_level] == TYPE_INSERT) {
150 CKERR(r);
151 assert(observed_val.size == vals[nest_level].size);
152 assert(memcmp(observed_val.data, vals[nest_level].data, vals[nest_level].size) == 0);
153 }
154 else {
155 assert(types[nest_level] == TYPE_DELETE);
156 CKERR2(r, DB_NOTFOUND);
157 }
158 }
159
160 static uint8_t
randomize_no_placeholder_type(void)161 randomize_no_placeholder_type(void) {
162 int r;
163 r = random() % 2;
164 switch (r) {
165 case 0:
166 return TYPE_INSERT;
167 case 1:
168 return TYPE_DELETE;
169 default:
170 assert(false);
171 return 0;
172 }
173 }
174
175 static uint8_t
randomize_type(void)176 randomize_type(void) {
177 int r;
178 do {
179 r = random() % 4;
180 } while (r >= 3); //Generate uniformly random 0-2
181 switch (r) {
182 case 0:
183 return TYPE_INSERT;
184 case 1:
185 return TYPE_DELETE;
186 case 2:
187 return TYPE_PLACEHOLDER;
188 default:
189 assert(false);
190 return 0;
191 }
192 }
193
194 static void
start_txn_and_maybe_insert_or_delete(uint8_t nest)195 start_txn_and_maybe_insert_or_delete(uint8_t nest) {
196 int r;
197 if (nest == 0) {
198 types[nest] = randomize_no_placeholder_type();
199 assert(types[nest] != TYPE_PLACEHOLDER);
200 //Committed entry is autocommitted by not providing the txn
201 txns[nest] = NULL;
202 }
203 else {
204 types[nest] = randomize_type();
205 r = env->txn_begin(env, txns[nest-1], &txns[nest], 0);
206 CKERR(r);
207 }
208 switch (types[nest]) {
209 case TYPE_INSERT:
210 r = db->put(db, txns[nest], &key, &vals[nest], 0);
211 CKERR(r);
212 break;
213 case TYPE_DELETE:
214 r = db->del(db, txns[nest], &key, DB_DELETE_ANY);
215 CKERR(r);
216 break;
217 case TYPE_PLACEHOLDER:
218 //Do Nothing.
219 break;
220 default:
221 assert(false);
222 }
223 verify_val(nest);
224 }
225
226 static void
initialize_db(void)227 initialize_db(void) {
228 types[0] = TYPE_DELETE; //Not yet inserted
229 verify_val(0);
230 int i;
231 for (i = 0; i < MAX_NEST; i++) {
232 start_txn_and_maybe_insert_or_delete(i);
233 }
234 }
235
236 static void
test_txn_nested_jumble(int iteration)237 test_txn_nested_jumble (int iteration) {
238 int r;
239 if (verbose) { fprintf(stderr, "%s (%s):%d [iteration # %d]\n", __FILE__, __FUNCTION__, __LINE__, iteration); fflush(stderr); }
240
241 initialize_db();
242
243 //BELOW IS OLD CODE
244 int index_of_expected_value = MAX_NEST - 1;
245 int nest_level;
246 for (nest_level = MAX_NEST - 1; nest_level > 0; nest_level--) {
247 int do_abort = random() & 0x1;
248 if (do_abort) {
249 r = txns[nest_level]->abort(txns[nest_level]);
250 CKERR(r);
251 index_of_expected_value = nest_level - 1;
252 }
253 else {
254 r = txns[nest_level]->commit(txns[nest_level], DB_TXN_NOSYNC);
255 CKERR(r);
256 //index of expected value unchanged
257 }
258 txns[nest_level] = NULL;
259 verify_val(index_of_expected_value);
260 }
261 //Clean out dictionary
262
263 types[0] = TYPE_DELETE;
264 r = db->del(db, NULL, &key, DB_DELETE_ANY);
265 CKERR(r);
266 verify_val(0);
267 }
268
269 int
test_main(int argc,char * const argv[])270 test_main(int argc, char *const argv[]) {
271 parse_args(argc, argv);
272 initialize_values();
273 int i;
274 setup_db();
275 for (i = 0; i < 64; i++) {
276 test_txn_nested_jumble(i);
277 }
278 close_db();
279 return 0;
280 }
281
282