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 }
67
recursively_flush_should_not_happen(FTNODE UU (child),void * UU (extra))68 static bool recursively_flush_should_not_happen(FTNODE UU(child), void* UU(extra)) {
69 assert(false);
70 }
71
child_to_flush(FT UU (h),FTNODE parent,void * UU (extra))72 static int child_to_flush(FT UU(h), FTNODE parent, void* UU(extra)) {
73 assert(parent->height == 1);
74 assert(parent->n_children == 2);
75 return 0;
76 }
77
dummy_update_status(FTNODE UU (child),int UU (dirtied),void * UU (extra))78 static void dummy_update_status(FTNODE UU(child), int UU(dirtied), void* UU(extra)) {
79 }
80
81
checkpoint_callback(void * UU (extra))82 static void checkpoint_callback(void* UU(extra)) {
83 usleep(1*1024*1024);
84 checkpoint_callback_called = true;
85 }
86
87
do_checkpoint(void * arg)88 static void *do_checkpoint(void *arg) {
89 // first verify that checkpointed_data is correct;
90 if (verbose) printf("starting a checkpoint\n");
91 CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
92 int r = toku_checkpoint(cp, NULL, checkpoint_callback, NULL, NULL, NULL, CLIENT_CHECKPOINT);
93 assert_zero(r);
94 if (verbose) printf("completed a checkpoint\n");
95 return arg;
96 }
97
98
flusher_callback(int state,void * extra)99 static void flusher_callback(int state, void* extra) {
100 int desired_state = *(int *)extra;
101 if (verbose) {
102 printf("state %d\n", state);
103 }
104 if (state == desired_state) {
105 checkpoint_called = true;
106 int r = toku_pthread_create(toku_uninstrumented,
107 &checkpoint_tid,
108 nullptr,
109 do_checkpoint,
110 nullptr);
111 assert_zero(r);
112 while (!checkpoint_callback_called) {
113 usleep(1 * 1024 * 1024);
114 }
115 }
116 }
117
118 static void
doit(int state)119 doit (int state) {
120 BLOCKNUM node_root;
121 BLOCKNUM node_leaves[2];
122
123 int r;
124 checkpoint_called = false;
125 checkpoint_callback_called = false;
126
127 toku_flusher_thread_set_callback(flusher_callback, &state);
128
129 toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
130 unlink("foo2.ft_handle");
131 unlink("bar2.ft_handle");
132 // note the basement node size is 5 times the node size
133 // this is done to avoid rebalancing when writing a leaf
134 // node to disk
135 r = toku_open_ft_handle("foo2.ft_handle", 1, &t, NODESIZE, 5*NODESIZE, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
136 assert(r==0);
137
138 toku_testsetup_initialize(); // must precede any other toku_testsetup calls
139
140 r = toku_testsetup_leaf(t, &node_leaves[0], 1, NULL, NULL);
141 assert(r==0);
142
143 r = toku_testsetup_leaf(t, &node_leaves[1], 1, NULL, NULL);
144 assert(r==0);
145
146 char* pivots[1];
147 pivots[0] = toku_strdup("kkkkk");
148 int pivot_len = 6;
149
150 r = toku_testsetup_nonleaf(t, 1, &node_root, 2, node_leaves, pivots, &pivot_len);
151 assert(r==0);
152
153 r = toku_testsetup_root(t, node_root);
154 assert(r==0);
155
156 r = toku_testsetup_insert_to_leaf(
157 t,
158 node_leaves[0],
159 "a",
160 2,
161 NULL,
162 0
163 );
164 assert_zero(r);
165 r = toku_testsetup_insert_to_leaf(
166 t,
167 node_leaves[1],
168 "z",
169 2,
170 NULL,
171 0
172 );
173 assert_zero(r);
174
175
176 // at this point, we have inserted two leafentries,
177 // one in each leaf node. A flush should invoke a merge
178 struct flusher_advice fa;
179 flusher_advice_init(
180 &fa,
181 child_to_flush,
182 dont_destroy_bn,
183 recursively_flush_should_not_happen,
184 default_merge_child,
185 dummy_update_status,
186 default_pick_child_after_split,
187 NULL
188 );
189
190 // hack to get merge going
191 FTNODE node = NULL;
192 toku_pin_node_with_min_bfe(&node, node_leaves[0], t);
193 BLB_SEQINSERT(node, node->n_children-1) = false;
194 toku_unpin_ftnode(t->ft, node);
195 toku_pin_node_with_min_bfe(&node, node_leaves[1], t);
196 BLB_SEQINSERT(node, node->n_children-1) = false;
197 toku_unpin_ftnode(t->ft, node);
198
199
200 ftnode_fetch_extra bfe;
201 bfe.create_for_min_read(t->ft);
202 toku_pin_ftnode_with_dep_nodes(
203 t->ft,
204 node_root,
205 toku_cachetable_hash(t->ft->cf, node_root),
206 &bfe,
207 PL_WRITE_EXPENSIVE,
208 0,
209 NULL,
210 &node,
211 true
212 );
213 assert(node->height == 1);
214 assert(node->n_children == 2);
215
216 // do the flush
217 toku_ft_flush_some_child(t->ft, node, &fa);
218 assert(checkpoint_callback_called);
219
220 // now let's pin the root again and make sure it is has merged
221 toku_pin_ftnode_with_dep_nodes(
222 t->ft,
223 node_root,
224 toku_cachetable_hash(t->ft->cf, node_root),
225 &bfe,
226 PL_WRITE_EXPENSIVE,
227 0,
228 NULL,
229 &node,
230 true
231 );
232 assert(node->height == 1);
233 assert(node->n_children == 1);
234 toku_unpin_ftnode(t->ft, node);
235
236 void *ret;
237 r = toku_pthread_join(checkpoint_tid, &ret);
238 assert_zero(r);
239
240 //
241 // now the dictionary has been checkpointed
242 // copy the file to something with a new name,
243 // open it, and verify that the state of what is
244 // checkpointed is what we expect
245 //
246
247 r = system("cp foo2.ft_handle bar2.ft_handle ");
248 assert_zero(r);
249
250 FT_HANDLE c_ft;
251 // note the basement node size is 5 times the node size
252 // this is done to avoid rebalancing when writing a leaf
253 // node to disk
254 r = toku_open_ft_handle("bar2.ft_handle", 0, &c_ft, NODESIZE, 5*NODESIZE, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
255 assert(r==0);
256
257 //
258 // now pin the root, verify that the state is what we expect
259 //
260 bfe.create_for_full_read(c_ft->ft);
261 toku_pin_ftnode_with_dep_nodes(
262 c_ft->ft,
263 node_root,
264 toku_cachetable_hash(c_ft->ft->cf, node_root),
265 &bfe,
266 PL_WRITE_EXPENSIVE,
267 0,
268 NULL,
269 &node,
270 true
271 );
272 assert(node->height == 1);
273 assert(!node->dirty());
274 BLOCKNUM left_child, right_child;
275 // cases where we expect the checkpoint to contain the merge
276 if (state == ft_flush_aflter_merge || state == flt_flush_before_unpin_remove) {
277 assert(node->n_children == 1);
278 left_child = BP_BLOCKNUM(node,0);
279 }
280 else if (state == flt_flush_before_merge || state == flt_flush_before_pin_second_node_for_merge) {
281 assert(node->n_children == 2);
282 left_child = BP_BLOCKNUM(node,0);
283 right_child = BP_BLOCKNUM(node,1);
284 }
285 else {
286 assert(false);
287 }
288 toku_unpin_ftnode(c_ft->ft, node);
289
290 // now let's verify the leaves are what we expect
291 if (state == flt_flush_before_merge || state == flt_flush_before_pin_second_node_for_merge) {
292 toku_pin_ftnode_with_dep_nodes(
293 c_ft->ft,
294 left_child,
295 toku_cachetable_hash(c_ft->ft->cf, left_child),
296 &bfe,
297 PL_WRITE_EXPENSIVE,
298 0,
299 NULL,
300 &node,
301 true
302 );
303 assert(node->height == 0);
304 assert(!node->dirty());
305 assert(node->n_children == 1);
306 assert(BLB_DATA(node, 0)->num_klpairs() == 1);
307 toku_unpin_ftnode(c_ft->ft, node);
308
309 toku_pin_ftnode_with_dep_nodes(
310 c_ft->ft,
311 right_child,
312 toku_cachetable_hash(c_ft->ft->cf, right_child),
313 &bfe,
314 PL_WRITE_EXPENSIVE,
315 0,
316 NULL,
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() == 1);
324 toku_unpin_ftnode(c_ft->ft, node);
325 }
326 else if (state == ft_flush_aflter_merge || state == flt_flush_before_unpin_remove) {
327 toku_pin_ftnode_with_dep_nodes(
328 c_ft->ft,
329 left_child,
330 toku_cachetable_hash(c_ft->ft->cf, left_child),
331 &bfe,
332 PL_WRITE_EXPENSIVE,
333 0,
334 NULL,
335 &node,
336 true
337 );
338 assert(node->height == 0);
339 assert(!node->dirty());
340 assert(node->n_children == 1);
341 assert(BLB_DATA(node, 0)->num_klpairs() == 2);
342 toku_unpin_ftnode(c_ft->ft, node);
343 }
344 else {
345 assert(false);
346 }
347
348
349 DBT k;
350 struct check_pair pair1 = {2, "a", 0, NULL, 0};
351 r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1);
352 assert(r==0);
353 struct check_pair pair2 = {2, "z", 0, NULL, 0};
354 r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "z", 2), lookup_checkf, &pair2);
355 assert(r==0);
356
357
358 r = toku_close_ft_handle_nolsn(t, 0); assert(r==0);
359 r = toku_close_ft_handle_nolsn(c_ft, 0); assert(r==0);
360 toku_cachetable_close(&ct);
361 toku_free(pivots[0]);
362 }
363
364 int
test_main(int argc,const char * argv[])365 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
366 default_parse_args(argc, argv);
367 doit(flt_flush_before_merge);
368 doit(flt_flush_before_pin_second_node_for_merge);
369 doit(flt_flush_before_unpin_remove);
370 doit(ft_flush_aflter_merge);
371 return 0;
372 }
373