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 /* The goal of this test. Make sure that inserts stay behind deletes. */
40
41
42 #include "test.h"
43
44 #include <ft-cachetable-wrappers.h>
45 #include "ft-flusher.h"
46 #include "ft-flusher-internal.h"
47 #include "cachetable/checkpoint.h"
48
49 static TOKUTXN const null_txn = 0;
50
51 enum { NODESIZE = 1024, KSIZE=NODESIZE-100, TOKU_PSIZE=20 };
52
53 CACHETABLE ct;
54 FT_HANDLE t;
55
56 bool checkpoint_called;
57 bool checkpoint_callback_called;
58 toku_pthread_t checkpoint_tid;
59
60
61 // callback functions for toku_ft_flush_some_child
62 static bool
dont_destroy_bn(void * UU (extra))63 dont_destroy_bn(void* UU(extra))
64 {
65 return false;
66 }
merge_should_not_happen(struct flusher_advice * UU (fa),FT UU (h),FTNODE UU (parent),int UU (childnum),FTNODE UU (child),void * UU (extra))67 static void merge_should_not_happen(struct flusher_advice* UU(fa),
68 FT UU(h),
69 FTNODE UU(parent),
70 int UU(childnum),
71 FTNODE UU(child),
72 void* UU(extra))
73 {
74 assert(false);
75 }
76
recursively_flush_should_not_happen(FTNODE UU (child),void * UU (extra))77 static bool recursively_flush_should_not_happen(FTNODE UU(child), void* UU(extra)) {
78 assert(false);
79 }
80
child_to_flush(FT UU (h),FTNODE parent,void * UU (extra))81 static int child_to_flush(FT UU(h), FTNODE parent, void* UU(extra)) {
82 assert(parent->height == 1);
83 assert(parent->n_children == 1);
84 return 0;
85 }
86
dummy_update_status(FTNODE UU (child),int UU (dirtied),void * UU (extra))87 static void dummy_update_status(FTNODE UU(child), int UU(dirtied), void* UU(extra)) {
88 }
89
90
checkpoint_callback(void * UU (extra))91 static void checkpoint_callback(void* UU(extra)) {
92 usleep(1*1024*1024);
93 checkpoint_callback_called = true;
94 }
95
96
do_checkpoint(void * arg)97 static void *do_checkpoint(void *arg) {
98 // first verify that checkpointed_data is correct;
99 if (verbose) printf("starting a checkpoint\n");
100 CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
101 int r = toku_checkpoint(cp, NULL, checkpoint_callback, NULL, NULL, NULL, CLIENT_CHECKPOINT);
102 assert_zero(r);
103 if (verbose) printf("completed a checkpoint\n");
104 return arg;
105 }
106
107
flusher_callback(int state,void * extra)108 static void flusher_callback(int state, void* extra) {
109 bool after_split = *(bool *)extra;
110 if (verbose) {
111 printf("state %d\n", state);
112 }
113 if ((state == flt_flush_before_split && !after_split) ||
114 (state == flt_flush_during_split && after_split)) {
115 checkpoint_called = true;
116 int r = toku_pthread_create(toku_uninstrumented,
117 &checkpoint_tid,
118 nullptr,
119 do_checkpoint,
120 nullptr);
121 assert_zero(r);
122 while (!checkpoint_callback_called) {
123 usleep(1 * 1024 * 1024);
124 }
125 }
126 }
127
128 static void
doit(bool after_split)129 doit (bool after_split) {
130 BLOCKNUM node_leaf, node_root;
131
132 int r;
133 checkpoint_called = false;
134 checkpoint_callback_called = false;
135
136 toku_flusher_thread_set_callback(flusher_callback, &after_split);
137
138 toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
139 unlink("foo4.ft_handle");
140 unlink("bar4.ft_handle");
141 // note the basement node size is 5 times the node size
142 // this is done to avoid rebalancing when writing a leaf
143 // node to disk
144 r = toku_open_ft_handle("foo4.ft_handle", 1, &t, NODESIZE, 5*NODESIZE, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
145 assert(r==0);
146
147 toku_testsetup_initialize(); // must precede any other toku_testsetup calls
148
149 r = toku_testsetup_leaf(t, &node_leaf, 1, NULL, NULL);
150 assert(r==0);
151
152 r = toku_testsetup_nonleaf(t, 1, &node_root, 1, &node_leaf, 0, 0);
153 assert(r==0);
154
155 r = toku_testsetup_root(t, node_root);
156 assert(r==0);
157
158 char dummy_val[NODESIZE-50];
159 memset(dummy_val, 0, sizeof(dummy_val));
160 r = toku_testsetup_insert_to_leaf(
161 t,
162 node_leaf,
163 "a",
164 2,
165 dummy_val,
166 sizeof(dummy_val)
167 );
168 assert_zero(r);
169 r = toku_testsetup_insert_to_leaf(
170 t,
171 node_leaf,
172 "z",
173 2,
174 dummy_val,
175 sizeof(dummy_val)
176 );
177 assert_zero(r);
178
179
180 // at this point, we have inserted two leafentries into
181 // the leaf, that should be big enough such that a split
182 // will happen
183 struct flusher_advice fa;
184 flusher_advice_init(
185 &fa,
186 child_to_flush,
187 dont_destroy_bn,
188 recursively_flush_should_not_happen,
189 merge_should_not_happen,
190 dummy_update_status,
191 default_pick_child_after_split,
192 NULL
193 );
194
195 FTNODE node = NULL;
196 ftnode_fetch_extra bfe;
197 bfe.create_for_min_read(t->ft);
198 toku_pin_ftnode(
199 t->ft,
200 node_root,
201 toku_cachetable_hash(t->ft->cf, node_root),
202 &bfe,
203 PL_WRITE_EXPENSIVE,
204 &node,
205 true
206 );
207 assert(node->height == 1);
208 assert(node->n_children == 1);
209
210 // do the flush
211 toku_ft_flush_some_child(t->ft, node, &fa);
212 assert(checkpoint_callback_called);
213
214 // now let's pin the root again and make sure it is has split
215 toku_pin_ftnode(
216 t->ft,
217 node_root,
218 toku_cachetable_hash(t->ft->cf, node_root),
219 &bfe,
220 PL_WRITE_EXPENSIVE,
221 &node,
222 true
223 );
224 assert(node->height == 1);
225 assert(node->n_children == 2);
226 toku_unpin_ftnode(t->ft, node);
227
228 void *ret;
229 r = toku_pthread_join(checkpoint_tid, &ret);
230 assert_zero(r);
231
232 //
233 // now the dictionary has been checkpointed
234 // copy the file to something with a new name,
235 // open it, and verify that the state of what is
236 // checkpointed is what we expect
237 //
238
239 r = system("cp foo4.ft_handle bar4.ft_handle ");
240 assert_zero(r);
241
242 FT_HANDLE c_ft;
243 // note the basement node size is 5 times the node size
244 // this is done to avoid rebalancing when writing a leaf
245 // node to disk
246 r = toku_open_ft_handle("bar4.ft_handle", 0, &c_ft, NODESIZE, 5*NODESIZE, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
247 assert(r==0);
248
249 //
250 // now pin the root, verify that we have a message in there, and that it is clean
251 //
252 bfe.create_for_full_read(c_ft->ft);
253 toku_pin_ftnode(
254 c_ft->ft,
255 node_root,
256 toku_cachetable_hash(c_ft->ft->cf, node_root),
257 &bfe,
258 PL_WRITE_EXPENSIVE,
259 &node,
260 true
261 );
262 assert(node->height == 1);
263 assert(!node->dirty());
264 BLOCKNUM left_child, right_child;
265 if (after_split) {
266 assert(node->n_children == 2);
267 left_child = BP_BLOCKNUM(node,0);
268 assert(left_child.b == node_leaf.b);
269 right_child = BP_BLOCKNUM(node,1);
270 }
271 else {
272 assert(node->n_children == 1);
273 left_child = BP_BLOCKNUM(node,0);
274 assert(left_child.b == node_leaf.b);
275 }
276 toku_unpin_ftnode(c_ft->ft, node);
277
278 // now let's verify the leaves are what we expect
279 if (after_split) {
280 toku_pin_ftnode(
281 c_ft->ft,
282 left_child,
283 toku_cachetable_hash(c_ft->ft->cf, left_child),
284 &bfe,
285 PL_WRITE_EXPENSIVE,
286 &node,
287 true
288 );
289 assert(node->height == 0);
290 assert(!node->dirty());
291 assert(node->n_children == 1);
292 assert(BLB_DATA(node, 0)->num_klpairs() == 1);
293 toku_unpin_ftnode(c_ft->ft, node);
294
295 toku_pin_ftnode(
296 c_ft->ft,
297 right_child,
298 toku_cachetable_hash(c_ft->ft->cf, right_child),
299 &bfe,
300 PL_WRITE_EXPENSIVE,
301 &node,
302 true
303 );
304 assert(node->height == 0);
305 assert(!node->dirty());
306 assert(node->n_children == 1);
307 assert(BLB_DATA(node, 0)->num_klpairs() == 1);
308 toku_unpin_ftnode(c_ft->ft, node);
309 }
310 else {
311 toku_pin_ftnode(
312 c_ft->ft,
313 left_child,
314 toku_cachetable_hash(c_ft->ft->cf, left_child),
315 &bfe,
316 PL_WRITE_EXPENSIVE,
317 &node,
318 true
319 );
320 assert(node->height == 0);
321 assert(!node->dirty());
322 assert(node->n_children == 1);
323 assert(BLB_DATA(node, 0)->num_klpairs() == 2);
324 toku_unpin_ftnode(c_ft->ft, node);
325 }
326
327
328 DBT k;
329 struct check_pair pair1 = {2, "a", sizeof(dummy_val), dummy_val, 0};
330 r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1);
331 assert(r==0);
332 struct check_pair pair2 = {2, "z", sizeof(dummy_val), dummy_val, 0};
333 r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "z", 2), lookup_checkf, &pair2);
334 assert(r==0);
335
336
337 r = toku_close_ft_handle_nolsn(t, 0); assert(r==0);
338 r = toku_close_ft_handle_nolsn(c_ft, 0); assert(r==0);
339 toku_cachetable_close(&ct);
340 }
341
342 int
test_main(int argc,const char * argv[])343 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
344 default_parse_args(argc, argv);
345 doit(false);
346 doit(true);
347 return 0;
348 }
349