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 
46 #include "ft-flusher.h"
47 #include "ft-flusher-internal.h"
48 #include "cachetable/checkpoint.h"
49 
50 static TOKUTXN const null_txn = 0;
51 
52 enum { NODESIZE = 1024, KSIZE=NODESIZE-100, TOKU_PSIZE=20 };
53 
54 CACHETABLE ct;
55 FT_HANDLE t;
56 const char *fname = TOKU_TEST_FILENAME;
57 
58 int curr_child_to_flush;
59 int num_flushes_called;
60 
child_to_flush(FT UU (h),FTNODE parent,void * UU (extra))61 static int child_to_flush(FT UU(h), FTNODE parent, void* UU(extra)) {
62     // internal node has 2 children
63     if (parent->height == 1) {
64         assert(parent->n_children == 2);
65         return curr_child_to_flush;
66     }
67     // root has 1 child
68     else if (parent->height == 2) {
69         assert(parent->n_children == 1);
70         return 0;
71     }
72     else {
73         assert(false);
74     }
75     return curr_child_to_flush;
76 }
77 
update_status(FTNODE UU (child),int UU (dirtied),void * UU (extra))78 static void update_status(FTNODE UU(child), int UU(dirtied), void* UU(extra)) {
79     num_flushes_called++;
80 }
81 
82 
83 
84 static bool
dont_destroy_bn(void * UU (extra))85 dont_destroy_bn(void* UU(extra))
86 {
87     return false;
88 }
89 
merge_should_not_happen(struct flusher_advice * UU (fa),FT UU (h),FTNODE UU (parent),int UU (childnum),FTNODE UU (child),void * UU (extra))90 static void merge_should_not_happen(struct flusher_advice* UU(fa),
91                               FT UU(h),
92                               FTNODE UU(parent),
93                               int UU(childnum),
94                               FTNODE UU(child),
95                               void* UU(extra))
96 {
97     assert(false);
98 }
99 
recursively_flush_should_not_happen(FTNODE UU (child),void * UU (extra))100 static bool recursively_flush_should_not_happen(FTNODE UU(child), void* UU(extra)) {
101     assert(false);
102 }
103 
always_flush(FTNODE UU (child),void * UU (extra))104 static bool always_flush(FTNODE UU(child), void* UU(extra)) {
105     return true;
106 }
107 
108 
109 static void
doit(void)110 doit (void) {
111     BLOCKNUM node_internal, node_root;
112     BLOCKNUM node_leaf[2];
113     int r;
114 
115     toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
116     unlink(fname);
117     r = toku_open_ft_handle(fname, 1, &t, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
118     assert(r==0);
119 
120     toku_testsetup_initialize();  // must precede any other toku_testsetup calls
121 
122     r = toku_testsetup_leaf(t, &node_leaf[0], 1, NULL, NULL);
123     assert(r==0);
124     r = toku_testsetup_leaf(t, &node_leaf[1], 1, NULL, NULL);
125     assert(r==0);
126 
127     char* pivots[1];
128     pivots[0] = toku_strdup("kkkkk");
129     int pivot_len = 6;
130     r = toku_testsetup_nonleaf(t, 1, &node_internal, 2, node_leaf, pivots, &pivot_len);
131     assert(r==0);
132 
133     r = toku_testsetup_nonleaf(t, 2, &node_root, 1, &node_internal, 0, 0);
134     assert(r==0);
135 
136     r = toku_testsetup_root(t, node_root);
137     assert(r==0);
138 
139     char filler[900-2*bn_data::HEADER_LENGTH];
140     memset(filler, 0, sizeof(filler));
141     // now we insert filler data so that a merge does not happen
142     r = toku_testsetup_insert_to_leaf (
143         t,
144         node_leaf[0],
145         "b", // key
146         2, // keylen
147         filler,
148         sizeof(filler)
149         );
150     assert(r==0);
151     r = toku_testsetup_insert_to_leaf (
152         t,
153         node_leaf[1],
154         "y", // key
155         2, // keylen
156         filler,
157         sizeof(filler)
158         );
159     assert(r==0);
160 
161     // make buffers in internal node non-empty
162     r = toku_testsetup_insert_to_nonleaf(
163         t,
164         node_internal,
165         FT_INSERT,
166         "a",
167         2,
168         NULL,
169         0
170         );
171     assert_zero(r);
172     r = toku_testsetup_insert_to_nonleaf(
173         t,
174         node_internal,
175         FT_INSERT,
176         "z",
177         2,
178         NULL,
179         0
180         );
181     assert_zero(r);
182 
183     //
184     // now run a checkpoint to get everything clean
185     //
186     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
187     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
188     assert_zero(r);
189 
190     // now with setup done, start the test
191     // test that if toku_ft_flush_some_child properly honors
192     // what we say and flushes the child we pick
193     FTNODE node = NULL;
194     toku_pin_node_with_min_bfe(&node, node_internal, t);
195     toku_ftnode_assert_fully_in_memory(node);
196     assert(node->n_children == 2);
197     assert(!node->dirty());
198     assert(toku_bnc_n_entries(node->bp[0].ptr.u.nonleaf) > 0);
199     assert(toku_bnc_n_entries(node->bp[1].ptr.u.nonleaf) > 0);
200 
201     struct flusher_advice fa;
202     flusher_advice_init(
203         &fa,
204         child_to_flush,
205         dont_destroy_bn,
206         recursively_flush_should_not_happen,
207         merge_should_not_happen,
208         update_status,
209 	default_pick_child_after_split,
210         NULL
211         );
212     curr_child_to_flush = 0;
213     num_flushes_called = 0;
214     toku_ft_flush_some_child(t->ft, node, &fa);
215     assert(num_flushes_called == 1);
216 
217     toku_pin_node_with_min_bfe(&node, node_internal, t);
218     toku_ftnode_assert_fully_in_memory(node);
219     assert(node->dirty());
220     assert(node->n_children == 2);
221     // child 0 should have empty buffer because it flushed
222     // child 1 should still have message in buffer
223     assert(toku_bnc_n_entries(node->bp[0].ptr.u.nonleaf) == 0);
224     assert(toku_bnc_n_entries(node->bp[1].ptr.u.nonleaf) > 0);
225     toku_unpin_ftnode(t->ft, node);
226     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
227     assert_zero(r);
228     toku_pin_node_with_min_bfe(&node, node_internal, t);
229     assert(!node->dirty());
230     curr_child_to_flush = 1;
231     num_flushes_called = 0;
232     toku_ft_flush_some_child(t->ft, node, &fa);
233     assert(num_flushes_called == 1);
234 
235     toku_pin_node_with_min_bfe(&node, node_internal, t);
236     assert(node->dirty());
237     toku_ftnode_assert_fully_in_memory(node);
238     assert(node->n_children == 2);
239     // both buffers should be empty now
240     assert(toku_bnc_n_entries(node->bp[0].ptr.u.nonleaf) == 0);
241     assert(toku_bnc_n_entries(node->bp[1].ptr.u.nonleaf) == 0);
242     // now let's do a flush with an empty buffer, make sure it is ok
243     toku_unpin_ftnode(t->ft, node);
244     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
245     assert_zero(r);
246     toku_pin_node_with_min_bfe(&node, node_internal, t);
247     assert(!node->dirty());
248     curr_child_to_flush = 0;
249     num_flushes_called = 0;
250     toku_ft_flush_some_child(t->ft, node, &fa);
251     assert(num_flushes_called == 1);
252 
253     toku_pin_node_with_min_bfe(&node, node_internal, t);
254     assert(node->dirty()); // nothing was flushed, but since we were trying to flush to a leaf, both become dirty
255     toku_ftnode_assert_fully_in_memory(node);
256     assert(node->n_children == 2);
257     // both buffers should be empty now
258     assert(toku_bnc_n_entries(node->bp[0].ptr.u.nonleaf) == 0);
259     assert(toku_bnc_n_entries(node->bp[1].ptr.u.nonleaf) == 0);
260     toku_unpin_ftnode(t->ft, node);
261 
262     // now let's start a flush from the root, that always recursively flushes
263     flusher_advice_init(
264         &fa,
265         child_to_flush,
266         dont_destroy_bn,
267         always_flush,
268         merge_should_not_happen,
269         update_status,
270 	default_pick_child_after_split,
271         NULL
272         );
273     // use a for loop so to get us down both paths
274     for (int i = 0; i < 2; i++) {
275         toku_pin_node_with_min_bfe(&node, node_root, t);
276         toku_ftnode_assert_fully_in_memory(node); // entire root is in memory
277         curr_child_to_flush = i;
278         num_flushes_called = 0;
279         toku_ft_flush_some_child(t->ft, node, &fa);
280         assert(num_flushes_called == 2);
281 
282         toku_pin_node_with_min_bfe(&node, node_internal, t);
283         assert(node->dirty());
284         toku_unpin_ftnode(t->ft, node);
285         toku_pin_node_with_min_bfe(&node, node_leaf[0], t);
286         assert(node->dirty());
287         toku_unpin_ftnode(t->ft, node);
288         toku_pin_node_with_min_bfe(&node, node_leaf[1], t);
289         if (i == 0) {
290             assert(!node->dirty());
291         }
292         else {
293             assert(node->dirty());
294         }
295         toku_unpin_ftnode(t->ft, node);
296     }
297 
298     // now one more test to show a bug was fixed
299     // if there is nothing to flush from parent to child,
300     // and child is not fully in memory, we used to crash
301     // so, to make sure that is fixed, let's get internal to not
302     // be fully in memory, and make sure the above test works
303 
304     // a hack to get internal compressed
305     r = toku_testsetup_insert_to_nonleaf(
306         t,
307         node_internal,
308         FT_INSERT,
309         "c",
310         2,
311         NULL,
312         0
313         );
314     assert_zero(r);
315     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
316     assert_zero(r);
317     toku_pin_node_with_min_bfe(&node, node_internal, t);
318     for (int i = 0; i < 20; i++) {
319         toku_ftnode_pe_callback(node, make_pair_attr(0xffffffff), t->ft, def_pe_finalize_impl, nullptr);
320     }
321     assert(BP_STATE(node,0) == PT_COMPRESSED);
322     toku_unpin_ftnode(t->ft, node);
323 
324     //now let's do the same test as above
325     toku_pin_node_with_min_bfe(&node, node_root, t);
326     toku_ftnode_assert_fully_in_memory(node); // entire root is in memory
327     curr_child_to_flush = 0;
328     num_flushes_called = 0;
329     toku_ft_flush_some_child(t->ft, node, &fa);
330     assert(num_flushes_called == 2);
331 
332     r = toku_close_ft_handle_nolsn(t, 0);    assert(r==0);
333     toku_cachetable_close(&ct);
334 
335     toku_free(pivots[0]);
336 }
337 
338 int
test_main(int argc,const char * argv[])339 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
340     doit();
341     return 0;
342 }
343