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("foo3.ft_handle");
131     unlink("bar3.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("foo3.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     char dummy_val[NODESIZE/2-50];
157     memset(dummy_val, 0, sizeof(dummy_val));
158     r = toku_testsetup_insert_to_leaf(
159         t,
160         node_leaves[0],
161         "a",
162         2,
163         NULL,
164         0
165         );
166     assert_zero(r);
167     r = toku_testsetup_insert_to_leaf(
168         t,
169         node_leaves[1],
170         "x",
171         2,
172         dummy_val,
173         sizeof(dummy_val)
174         );
175     assert_zero(r);
176     r = toku_testsetup_insert_to_leaf(
177         t,
178         node_leaves[1],
179         "y",
180         2,
181         dummy_val,
182         sizeof(dummy_val)
183         );
184     assert_zero(r);
185     r = toku_testsetup_insert_to_leaf(
186         t,
187         node_leaves[1],
188         "z",
189         2,
190         NULL,
191         0
192         );
193     assert_zero(r);
194 
195 
196     // at this point, we have inserted two leafentries,
197     // one in each leaf node. A flush should invoke a merge
198     struct flusher_advice fa;
199     flusher_advice_init(
200         &fa,
201         child_to_flush,
202         dont_destroy_bn,
203         recursively_flush_should_not_happen,
204         default_merge_child,
205         dummy_update_status,
206         default_pick_child_after_split,
207         NULL
208         );
209 
210     // hack to get merge going
211     FTNODE node = NULL;
212     toku_pin_node_with_min_bfe(&node, node_leaves[0], t);
213     BLB_SEQINSERT(node, node->n_children-1) = false;
214     toku_unpin_ftnode(t->ft, node);
215     toku_pin_node_with_min_bfe(&node, node_leaves[1], t);
216     BLB_SEQINSERT(node, node->n_children-1) = false;
217     toku_unpin_ftnode(t->ft, node);
218 
219 
220     ftnode_fetch_extra bfe;
221     bfe.create_for_min_read(t->ft);
222     toku_pin_ftnode(
223         t->ft,
224         node_root,
225         toku_cachetable_hash(t->ft->cf, node_root),
226         &bfe,
227         PL_WRITE_EXPENSIVE,
228         &node,
229         true
230         );
231     assert(node->height == 1);
232     assert(node->n_children == 2);
233 
234     // do the flush
235     toku_ft_flush_some_child(t->ft, node, &fa);
236     assert(checkpoint_callback_called);
237 
238     // now let's pin the root again and make sure it is has rebalanced
239     toku_pin_ftnode(
240         t->ft,
241         node_root,
242         toku_cachetable_hash(t->ft->cf, node_root),
243         &bfe,
244         PL_WRITE_EXPENSIVE,
245         &node,
246         true
247         );
248     assert(node->height == 1);
249     assert(node->n_children == 2);
250     toku_unpin_ftnode(t->ft, node);
251 
252     void *ret;
253     r = toku_pthread_join(checkpoint_tid, &ret);
254     assert_zero(r);
255 
256     //
257     // now the dictionary has been checkpointed
258     // copy the file to something with a new name,
259     // open it, and verify that the state of what is
260     // checkpointed is what we expect
261     //
262 
263     r = system("cp foo3.ft_handle bar3.ft_handle ");
264     assert_zero(r);
265 
266     FT_HANDLE c_ft;
267     // note the basement node size is 5 times the node size
268     // this is done to avoid rebalancing when writing a leaf
269     // node to disk
270     r = toku_open_ft_handle("bar3.ft_handle", 0, &c_ft, NODESIZE, 5*NODESIZE, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
271     assert(r==0);
272 
273     //
274     // now pin the root, verify that the state is what we expect
275     //
276     bfe.create_for_full_read(c_ft->ft);
277     toku_pin_ftnode(
278         c_ft->ft,
279         node_root,
280         toku_cachetable_hash(c_ft->ft->cf, node_root),
281         &bfe,
282         PL_WRITE_EXPENSIVE,
283         &node,
284         true
285         );
286     assert(node->height == 1);
287     assert(!node->dirty());
288     BLOCKNUM left_child, right_child;
289 
290     assert(node->n_children == 2);
291     left_child = BP_BLOCKNUM(node,0);
292     right_child = BP_BLOCKNUM(node,1);
293 
294     toku_unpin_ftnode(c_ft->ft, node);
295 
296     // now let's verify the leaves are what we expect
297     toku_pin_ftnode(
298         c_ft->ft,
299         left_child,
300         toku_cachetable_hash(c_ft->ft->cf, left_child),
301         &bfe,
302         PL_WRITE_EXPENSIVE,
303         &node,
304         true
305         );
306     assert(node->height == 0);
307     assert(!node->dirty());
308     assert(node->n_children == 1);
309     assert(BLB_DATA(node, 0)->num_klpairs() == 2);
310     toku_unpin_ftnode(c_ft->ft, node);
311 
312     toku_pin_ftnode(
313         c_ft->ft,
314         right_child,
315         toku_cachetable_hash(c_ft->ft->cf, right_child),
316         &bfe,
317         PL_WRITE_EXPENSIVE,
318         &node,
319         true
320         );
321     assert(node->height == 0);
322     assert(!node->dirty());
323     assert(node->n_children == 1);
324     assert(BLB_DATA(node, 0)->num_klpairs() == 2);
325     toku_unpin_ftnode(c_ft->ft, node);
326 
327     DBT k;
328     struct check_pair pair1 = {2, "a", 0, NULL, 0};
329     r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1);
330     assert(r==0);
331     struct check_pair pair2 = {2, "x", sizeof(dummy_val), dummy_val, 0};
332     r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "x", 2), lookup_checkf, &pair2);
333     assert(r==0);
334     struct check_pair pair3 = {2, "y", sizeof(dummy_val), dummy_val, 0};
335     r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "y", 2), lookup_checkf, &pair3);
336     assert(r==0);
337     struct check_pair pair4 = {2, "z", 0, NULL, 0};
338     r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "z", 2), lookup_checkf, &pair4);
339     assert(r==0);
340 
341 
342     r = toku_close_ft_handle_nolsn(t, 0);    assert(r==0);
343     r = toku_close_ft_handle_nolsn(c_ft, 0);    assert(r==0);
344     toku_cachetable_close(&ct);
345     toku_free(pivots[0]);
346 }
347 
348 int
test_main(int argc,const char * argv[])349 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
350     default_parse_args(argc, argv);
351     doit(ft_flush_aflter_rebalance);
352     return 0;
353 }
354