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