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