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 "cachetable/checkpoint.h"
47 
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 ft;
55 const char *fname = TOKU_TEST_FILENAME;
56 
update_func(DB * UU (db),const DBT * key,const DBT * old_val,const DBT * UU (extra),void (* set_val)(const DBT * new_val,void * set_extra),void * set_extra)57 static int update_func(
58     DB* UU(db),
59     const DBT* key,
60     const DBT* old_val,
61     const DBT* UU(extra),
62     void (*set_val)(const DBT *new_val, void *set_extra),
63     void *set_extra)
64 {
65     DBT new_val;
66     assert(old_val->size > 0);
67     if (verbose) {
68         printf("applying update to %s\n", (char *)key->data);
69     }
70     toku_init_dbt(&new_val);
71     set_val(&new_val, set_extra);
72     return 0;
73 }
74 
75 
76 static void
doit(bool keep_other_bn_in_memory)77 doit (bool keep_other_bn_in_memory) {
78     BLOCKNUM node_leaf;
79     BLOCKNUM node_internal, node_root;
80 
81     int r;
82 
83     toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
84     unlink(fname);
85     r = toku_open_ft_handle(fname, 1, &ft, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
86     assert(r==0);
87 
88     ft->options.update_fun = update_func;
89     ft->ft->update_fun = update_func;
90 
91     toku_testsetup_initialize();  // must precede any other toku_testsetup calls
92 
93     char* pivots[1];
94     pivots[0] = toku_strdup("kkkkk");
95     int pivot_len = 6;
96 
97     r = toku_testsetup_leaf(ft, &node_leaf, 2, pivots, &pivot_len);
98     assert(r==0);
99 
100     r = toku_testsetup_nonleaf(ft, 1, &node_internal, 1, &node_leaf, 0, 0);
101     assert(r==0);
102 
103     r = toku_testsetup_nonleaf(ft, 2, &node_root, 1, &node_internal, 0, 0);
104     assert(r==0);
105 
106     r = toku_testsetup_root(ft, node_root);
107     assert(r==0);
108 
109     //
110     // at this point we have created a tree with a root, an internal node,
111     // and two leaf nodes, the pivot being "kkkkk"
112     //
113 
114     // now we insert a row into each leaf node
115     r = toku_testsetup_insert_to_leaf (
116         ft,
117         node_leaf,
118         "a", // key
119         2, // keylen
120         "aa",
121         3
122         );
123     assert(r==0);
124     r = toku_testsetup_insert_to_leaf (
125         ft,
126         node_leaf,
127         "z", // key
128         2, // keylen
129         "zz",
130         3
131         );
132     assert(r==0);
133     char filler[400];
134     memset(filler, 0, sizeof(filler));
135     // now we insert filler data so that the rebalance
136     // keeps it at two nodes
137     r = toku_testsetup_insert_to_leaf (
138         ft,
139         node_leaf,
140         "b", // key
141         2, // keylen
142         filler,
143         sizeof(filler)
144         );
145     assert(r==0);
146     r = toku_testsetup_insert_to_leaf (
147         ft,
148         node_leaf,
149         "y", // key
150         2, // keylen
151         filler,
152         sizeof(filler)
153         );
154     assert(r==0);
155 
156     //
157     // now insert a bunch of dummy delete messages
158     // into the internal node, to get its cachepressure size up
159     //
160     for (int i = 0; i < 100000; i++) {
161         r = toku_testsetup_insert_to_nonleaf (
162             ft,
163             node_internal,
164             FT_DELETE_ANY,
165             "jj", // this key does not exist, so its message application should be a no-op
166             3,
167             NULL,
168             0
169             );
170         assert(r==0);
171     }
172 
173     //
174     // now insert a broadcast message into the root
175     //
176     r = toku_testsetup_insert_to_nonleaf (
177         ft,
178         node_root,
179         FT_UPDATE_BROADCAST_ALL,
180         NULL,
181         0,
182         NULL,
183         0
184         );
185     assert(r==0);
186 
187     //
188     // now run a checkpoint to get everything clean
189     //
190     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
191     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
192     assert_zero(r);
193     // now lock and release the leaf node to make sure it is what we expect it to be.
194     FTNODE node = NULL;
195     ftnode_fetch_extra bfe;
196     bfe.create_for_min_read(ft->ft);
197     toku_pin_ftnode(
198         ft->ft,
199         node_leaf,
200         toku_cachetable_hash(ft->ft->cf, node_leaf),
201         &bfe,
202         PL_WRITE_EXPENSIVE,
203         &node,
204         true
205         );
206     assert(!node->dirty());
207     assert(node->n_children == 2);
208     // a hack to get the basement nodes evicted
209     for (int i = 0; i < 20; i++) {
210         toku_ftnode_pe_callback(node, make_pair_attr(0xffffffff), ft->ft, def_pe_finalize_impl, nullptr);
211     }
212     // this ensures that when we do the lookups below,
213     // that the data is read off disk
214     assert(BP_STATE(node,0) == PT_ON_DISK);
215     assert(BP_STATE(node,1) == PT_ON_DISK);
216     toku_unpin_ftnode(ft->ft, node);
217 
218     // now do a lookup on one of the keys, this should bring a leaf node up to date
219     DBT k;
220     struct check_pair pair = {2, "a", 0, NULL, 0};
221     r = toku_ft_lookup(ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair);
222     assert(r==0);
223 
224     if (keep_other_bn_in_memory) {
225         //
226         // pin the leaf one more time
227         // and make sure that one basement
228         // both basement nodes are in memory,
229         // but only one should have broadcast message
230         // applied.
231         //
232         bfe.create_for_full_read(ft->ft);
233     }
234     else {
235         //
236         // pin the leaf one more time
237         // and make sure that one basement
238         // node is in memory and another is
239         // on disk
240         //
241         bfe.create_for_min_read(ft->ft);
242     }
243     toku_pin_ftnode(
244         ft->ft,
245         node_leaf,
246         toku_cachetable_hash(ft->ft->cf, node_leaf),
247         &bfe,
248         PL_WRITE_EXPENSIVE,
249         &node,
250         true
251         );
252     assert(!node->dirty());
253     assert(node->n_children == 2);
254     assert(BP_STATE(node,0) == PT_AVAIL);
255     if (keep_other_bn_in_memory) {
256         assert(BP_STATE(node,1) == PT_AVAIL);
257     }
258     else {
259         assert(BP_STATE(node,1) == PT_ON_DISK);
260     }
261     toku_unpin_ftnode(ft->ft, node);
262 
263     //
264     // now let us induce a clean on the internal node
265     //
266     bfe.create_for_min_read(ft->ft);
267     toku_pin_ftnode(
268         ft->ft,
269         node_internal,
270         toku_cachetable_hash(ft->ft->cf, node_internal),
271         &bfe,
272         PL_WRITE_EXPENSIVE,
273         &node,
274         true
275         );
276     assert(!node->dirty());
277 
278     // we expect that this flushes its buffer, that
279     // a merge is not done, and that the lookup
280     // of values "a" and "z" still works
281     r = toku_ftnode_cleaner_callback(
282         node,
283         node_internal,
284         toku_cachetable_hash(ft->ft->cf, node_internal),
285         ft->ft
286         );
287 
288     // verify that node_internal's buffer is empty
289     bfe.create_for_min_read(ft->ft);
290     toku_pin_ftnode(
291         ft->ft,
292         node_internal,
293         toku_cachetable_hash(ft->ft->cf, node_internal),
294         &bfe,
295         PL_WRITE_EXPENSIVE,
296         &node,
297         true
298         );
299     // check that buffers are empty
300     assert(toku_bnc_nbytesinbuf(BNC(node, 0)) == 0);
301     toku_unpin_ftnode(ft->ft, node);
302 
303     //
304     // now run a checkpoint to get everything clean,
305     // and to get the rebalancing to happen
306     //
307     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
308     assert_zero(r);
309 
310     // check that lookups on the two keys is still good
311     struct check_pair pair1 = {2, "a", 0, NULL, 0};
312     r = toku_ft_lookup(ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1);
313     assert(r==0);
314     struct check_pair pair2 = {2, "z", 0, NULL, 0};
315     r = toku_ft_lookup(ft, toku_fill_dbt(&k, "z", 2), lookup_checkf, &pair2);
316     assert(r==0);
317 
318 
319     r = toku_close_ft_handle_nolsn(ft, 0);    assert(r==0);
320     toku_cachetable_close(&ct);
321 
322     toku_free(pivots[0]);
323 }
324 
325 int
test_main(int argc,const char * argv[])326 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
327     default_parse_args(argc, argv);
328     doit(false);
329     doit(true);
330     return 0;
331 }
332