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(void)77 doit (void) {
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->ft->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     // now lock and release the leaf node to make sure it is what we expect it to be.
188     FTNODE node = NULL;
189     ftnode_fetch_extra bfe;
190     bfe.create_for_min_read(ft->ft);
191     toku_pin_ftnode_with_dep_nodes(
192         ft->ft,
193         node_leaf,
194         toku_cachetable_hash(ft->ft->cf, node_leaf),
195         &bfe,
196         PL_WRITE_EXPENSIVE,
197         0,
198         NULL,
199         &node,
200         true
201         );
202     assert(node->dirty());
203     assert(node->n_children == 2);
204     assert(BP_STATE(node,0) == PT_AVAIL);
205     assert(BP_STATE(node,1) == PT_AVAIL);
206     toku_unpin_ftnode(ft->ft, node);
207 
208     // now do a lookup on one of the keys, this should bring a leaf node up to date
209     DBT k;
210     struct check_pair pair = {2, "a", 0, NULL, 0};
211     r = toku_ft_lookup(ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair);
212     assert(r==0);
213 
214     //
215     // pin the leaf one more time
216     // and make sure that one basement
217     // node is in memory and another is
218     // on disk
219     //
220     bfe.create_for_min_read(ft->ft);
221     toku_pin_ftnode_with_dep_nodes(
222         ft->ft,
223         node_leaf,
224         toku_cachetable_hash(ft->ft->cf, node_leaf),
225         &bfe,
226         PL_WRITE_EXPENSIVE,
227         0,
228         NULL,
229         &node,
230         true
231         );
232     assert(node->dirty());
233     assert(node->n_children == 2);
234     assert(BP_STATE(node,0) == PT_AVAIL);
235     assert(BP_STATE(node,1) == PT_AVAIL);
236     toku_unpin_ftnode(ft->ft, node);
237 
238     //
239     // now let us induce a clean on the internal node
240     //
241     bfe.create_for_min_read(ft->ft);
242     toku_pin_ftnode_with_dep_nodes(
243         ft->ft,
244         node_internal,
245         toku_cachetable_hash(ft->ft->cf, node_internal),
246         &bfe,
247         PL_WRITE_EXPENSIVE,
248         0,
249         NULL,
250         &node,
251         true
252         );
253     assert(node->dirty());
254 
255     // we expect that this flushes its buffer, that
256     // a merge is not done, and that the lookup
257     // of values "a" and "z" still works
258     r = toku_ftnode_cleaner_callback(
259         node,
260         node_internal,
261         toku_cachetable_hash(ft->ft->cf, node_internal),
262         ft->ft
263         );
264 
265     // verify that node_internal's buffer is empty
266     bfe.create_for_min_read(ft->ft);
267     toku_pin_ftnode_with_dep_nodes(
268         ft->ft,
269         node_internal,
270         toku_cachetable_hash(ft->ft->cf, node_internal),
271         &bfe,
272         PL_WRITE_EXPENSIVE,
273         0,
274         NULL,
275         &node,
276         true
277         );
278     // check that buffers are empty
279     assert(toku_bnc_nbytesinbuf(BNC(node, 0)) == 0);
280     toku_unpin_ftnode(ft->ft, node);
281 
282     //
283     // now run a checkpoint to get everything clean,
284     // and to get the rebalancing to happen
285     //
286     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
287     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
288     assert_zero(r);
289 
290     // check that lookups on the two keys is still good
291     struct check_pair pair1 = {2, "a", 0, NULL, 0};
292     r = toku_ft_lookup(ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1);
293     assert(r==0);
294     struct check_pair pair2 = {2, "z", 0, NULL, 0};
295     r = toku_ft_lookup(ft, toku_fill_dbt(&k, "z", 2), lookup_checkf, &pair2);
296     assert(r==0);
297 
298 
299     r = toku_close_ft_handle_nolsn(ft, 0);    assert(r==0);
300     toku_cachetable_close(&ct);
301 
302     toku_free(pivots[0]);
303 }
304 
305 int
test_main(int argc,const char * argv[])306 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
307     default_parse_args(argc, argv);
308     doit();
309     return 0;
310 }
311