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