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