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
41 #include "toku_os.h"
42 #include "cachetable/checkpoint.h"
43
44 #include "test-ft-txns.h"
45
txn_child_manager_test_cb(TOKUTXN txn,void * extra)46 static int txn_child_manager_test_cb(TOKUTXN txn, void* extra) {
47 TOKUTXN* ptxn = (TOKUTXN *)extra;
48 assert(txn == *ptxn);
49 *ptxn = txn->child;
50 return 0;
51 }
52
txn_child_manager_test_cb2(TOKUTXN txn,void * extra)53 static int txn_child_manager_test_cb2(TOKUTXN txn, void* extra) {
54 TOKUTXN extra_txn = (TOKUTXN)extra;
55 if (txn == extra_txn) {
56 return -1;
57 }
58 return 0;
59 }
60
61
62 class txn_child_manager_unit_test {
63 public:
64 void run_test();
65 void run_child_txn_test();
66 };
67
68 // simple test that verifies that creating a TXN_CHILD_SNAPSHOT tokutxn
69 // creates its own snapshot
run_child_txn_test()70 void txn_child_manager_unit_test::run_child_txn_test() {
71 TOKULOGGER logger;
72 CACHETABLE ct;
73 int r = 0;
74 test_setup(TOKU_TEST_FILENAME, &logger, &ct);
75 // create the root transaction
76 TOKUTXN root_txn = NULL;
77 r = toku_txn_begin_txn(
78 (DB_TXN *)NULL,
79 NULL,
80 &root_txn,
81 logger,
82 TXN_SNAPSHOT_CHILD,
83 false
84 );
85 CKERR(r);
86 // test starting a child txn
87 TOKUTXN child_txn = NULL;
88 r = toku_txn_begin_txn(
89 NULL,
90 root_txn,
91 &child_txn,
92 logger,
93 TXN_SNAPSHOT_CHILD,
94 false
95 );
96 CKERR(r);
97
98 // assert that the child has a later snapshot
99 assert(child_txn->snapshot_txnid64 > root_txn->snapshot_txnid64);
100
101 r = toku_txn_commit_txn(child_txn, true, NULL, NULL);
102 CKERR(r);
103 toku_txn_close_txn(child_txn);
104 assert(root_txn->child == NULL);
105
106 r = toku_txn_commit_txn(root_txn, true, NULL, NULL);
107 CKERR(r);
108 toku_txn_close_txn(root_txn);
109
110
111 clean_shutdown(&logger, &ct);
112 }
113
run_test()114 void txn_child_manager_unit_test::run_test() {
115 TOKULOGGER logger;
116 CACHETABLE ct;
117 int r = 0;
118 test_setup(TOKU_TEST_FILENAME, &logger, &ct);
119 // create the root transaction
120 TOKUTXN root_txn = NULL;
121 r = toku_txn_begin_txn(
122 (DB_TXN *)NULL,
123 NULL,
124 &root_txn,
125 logger,
126 TXN_SNAPSHOT_ROOT,
127 false
128 );
129 CKERR(r);
130 txn_child_manager* cm = root_txn->child_manager;
131 assert(cm == &root_txn->child_manager_s);
132 assert(cm->m_root == root_txn);
133 assert(cm->m_last_xid == TXNID_NONE);
134 assert(root_txn->child == NULL);
135 // this assumption implies our assumptions of child_id values below,
136 // because the parent id cannot be the child id
137 assert(root_txn->txnid.parent_id64 == 1);
138
139 // test starting a child txn
140 TOKUTXN child_txn = NULL;
141 r = toku_txn_begin_txn(
142 NULL,
143 root_txn,
144 &child_txn,
145 logger,
146 TXN_SNAPSHOT_ROOT,
147 false
148 );
149 CKERR(r);
150 assert(child_txn->child_manager == cm);
151 assert(child_txn->parent == root_txn);
152 assert(root_txn->child == child_txn);
153 assert(child_txn->txnid.parent_id64 == root_txn->txnid.parent_id64);
154 assert(child_txn->txnid.child_id64 == 2);
155 assert(child_txn->live_root_txn_list == root_txn->live_root_txn_list);
156 assert(child_txn->snapshot_txnid64 == root_txn->snapshot_txnid64);
157
158 assert(cm->m_root == root_txn);
159 assert(cm->m_last_xid == child_txn->txnid.child_id64);
160
161 TOKUTXN grandchild_txn = NULL;
162 r = toku_txn_begin_txn(
163 NULL,
164 child_txn,
165 &grandchild_txn,
166 logger,
167 TXN_SNAPSHOT_ROOT,
168 false
169 );
170 CKERR(r);
171 assert(grandchild_txn->child_manager == cm);
172 assert(grandchild_txn->parent == child_txn);
173 assert(child_txn->child == grandchild_txn);
174 assert(grandchild_txn->txnid.parent_id64 == root_txn->txnid.parent_id64);
175 assert(grandchild_txn->txnid.child_id64 == 3);
176 assert(grandchild_txn->live_root_txn_list == root_txn->live_root_txn_list);
177 assert(grandchild_txn->snapshot_txnid64 == root_txn->snapshot_txnid64);
178
179 assert(cm->m_root == root_txn);
180 assert(cm->m_last_xid == grandchild_txn->txnid.child_id64);
181
182 r = toku_txn_commit_txn(grandchild_txn, true, NULL, NULL);
183 CKERR(r);
184 toku_txn_close_txn(grandchild_txn);
185
186
187 // now after closing one grandchild txn, open another one
188 r = toku_txn_begin_txn(
189 NULL,
190 child_txn,
191 &grandchild_txn,
192 logger,
193 TXN_SNAPSHOT_ROOT,
194 false
195 );
196 CKERR(r);
197 assert(grandchild_txn->child_manager == cm);
198 assert(grandchild_txn->parent == child_txn);
199 assert(child_txn->child == grandchild_txn);
200 assert(grandchild_txn->txnid.parent_id64 == root_txn->txnid.parent_id64);
201 assert(grandchild_txn->txnid.child_id64 == 4);
202 assert(grandchild_txn->live_root_txn_list == root_txn->live_root_txn_list);
203 assert(grandchild_txn->snapshot_txnid64 == root_txn->snapshot_txnid64);
204
205 assert(cm->m_root == root_txn);
206 assert(cm->m_last_xid == grandchild_txn->txnid.child_id64);
207
208
209 TXNID_PAIR xid = {.parent_id64 = root_txn->txnid.parent_id64, .child_id64 = 100};
210 TOKUTXN recovery_txn = NULL;
211 r = toku_txn_begin_with_xid(
212 grandchild_txn,
213 &recovery_txn,
214 logger,
215 xid,
216 TXN_SNAPSHOT_NONE,
217 NULL,
218 true, // for recovery
219 false // read_only
220 );
221
222 assert(recovery_txn->child_manager == cm);
223 assert(recovery_txn->parent == grandchild_txn);
224 assert(grandchild_txn->child == recovery_txn);
225 assert(recovery_txn->txnid.parent_id64 == root_txn->txnid.parent_id64);
226 assert(recovery_txn->txnid.child_id64 == 100);
227 // ensure that no snapshot is made
228 assert(recovery_txn->live_root_txn_list == NULL);
229 assert(recovery_txn->snapshot_txnid64 == TXNID_NONE);
230
231 assert(cm->m_root == root_txn);
232 assert(cm->m_last_xid == recovery_txn->txnid.child_id64);
233
234
235 // now ensure that txn_child_manager::find_tokutxn_by_xid_unlocked works
236 TOKUTXN found_txn = NULL;
237 // first ensure that a dummy TXNID_PAIR cannot be found
238 TXNID_PAIR dummy_pair = { .parent_id64 = root_txn->txnid.parent_id64, .child_id64 = 1000};
239 cm->find_tokutxn_by_xid_unlocked(dummy_pair, &found_txn);
240 assert(found_txn == NULL);
241 cm->find_tokutxn_by_xid_unlocked(root_txn->txnid, &found_txn);
242 assert(found_txn == root_txn);
243 cm->find_tokutxn_by_xid_unlocked(child_txn->txnid, &found_txn);
244 assert(found_txn == child_txn);
245 cm->find_tokutxn_by_xid_unlocked(grandchild_txn->txnid, &found_txn);
246 assert(found_txn == grandchild_txn);
247 cm->find_tokutxn_by_xid_unlocked(recovery_txn->txnid, &found_txn);
248 assert(found_txn == recovery_txn);
249
250
251 // now ensure that the iterator works
252 found_txn = root_txn;
253 r = cm->iterate(txn_child_manager_test_cb, &found_txn);
254 CKERR(r);
255 assert(found_txn == NULL);
256
257 // now test that iterator properly stops
258 found_txn = child_txn;
259 r = cm->iterate(txn_child_manager_test_cb2, found_txn);
260 assert(r == -1);
261
262 r = toku_txn_commit_txn(recovery_txn, true, NULL, NULL);
263 CKERR(r);
264 toku_txn_close_txn(recovery_txn);
265 assert(grandchild_txn->child == NULL);
266
267 r = toku_txn_commit_txn(grandchild_txn, true, NULL, NULL);
268 CKERR(r);
269 toku_txn_close_txn(grandchild_txn);
270 assert(child_txn->child == NULL);
271
272 r = toku_txn_commit_txn(child_txn, true, NULL, NULL);
273 CKERR(r);
274 toku_txn_close_txn(child_txn);
275 assert(root_txn->child == NULL);
276
277 r = toku_txn_commit_txn(root_txn, true, NULL, NULL);
278 CKERR(r);
279 toku_txn_close_txn(root_txn);
280
281
282 clean_shutdown(&logger, &ct);
283 }
284
test_main(int argc,const char * argv[])285 int test_main (int argc, const char *argv[]) {
286 default_parse_args(argc, argv);
287 txn_child_manager_unit_test foo;
288 foo.run_test();
289 return 0;
290 }
291