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_child_pin = *(bool *)extra;
110     if (verbose) {
111         printf("state %d\n", state);
112     }
113     if ((state == flt_flush_before_child_pin && !after_child_pin) ||
114         (state == ft_flush_aflter_child_pin && after_child_pin)) {
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_child_pin)129 doit (bool after_child_pin) {
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_child_pin);
137 
138     toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr);
139     unlink("foo1.ft_handle");
140     r = toku_open_ft_handle("foo1.ft_handle", 1, &t, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
141     assert(r==0);
142 
143     toku_testsetup_initialize();  // must precede any other toku_testsetup calls
144 
145     r = toku_testsetup_leaf(t, &node_leaf, 1, NULL, NULL);
146     assert(r==0);
147 
148     r = toku_testsetup_nonleaf(t, 1, &node_root, 1, &node_leaf, 0, 0);
149     assert(r==0);
150 
151     r = toku_testsetup_root(t, node_root);
152     assert(r==0);
153 
154 
155     r = toku_testsetup_insert_to_nonleaf(
156         t,
157         node_root,
158         FT_INSERT,
159         "a",
160         2,
161         NULL,
162         0
163         );
164 
165     // at this point, we have inserted a message into
166     // the root, and we wish to flush it, the leaf
167     // should be empty
168 
169     struct flusher_advice fa;
170     flusher_advice_init(
171         &fa,
172         child_to_flush,
173         dont_destroy_bn,
174         recursively_flush_should_not_happen,
175         merge_should_not_happen,
176         dummy_update_status,
177         default_pick_child_after_split,
178         NULL
179         );
180 
181     FTNODE node = NULL;
182     ftnode_fetch_extra bfe;
183     bfe.create_for_min_read(t->ft);
184     toku_pin_ftnode(
185         t->ft,
186         node_root,
187         toku_cachetable_hash(t->ft->cf, node_root),
188         &bfe,
189         PL_WRITE_EXPENSIVE,
190         &node,
191         true
192         );
193     assert(node->height == 1);
194     assert(node->n_children == 1);
195     assert(toku_bnc_nbytesinbuf(BNC(node, 0)) > 0);
196 
197     // do the flush
198     toku_ft_flush_some_child(t->ft, node, &fa);
199     assert(checkpoint_callback_called);
200 
201     // now let's pin the root again and make sure it is flushed
202     toku_pin_ftnode(
203         t->ft,
204         node_root,
205         toku_cachetable_hash(t->ft->cf, node_root),
206         &bfe,
207         PL_WRITE_EXPENSIVE,
208         &node,
209         true
210         );
211     assert(node->height == 1);
212     assert(node->n_children == 1);
213     assert(toku_bnc_nbytesinbuf(BNC(node, 0)) == 0);
214     toku_unpin_ftnode(t->ft, node);
215 
216     void *ret;
217     r = toku_pthread_join(checkpoint_tid, &ret);
218     assert_zero(r);
219 
220     //
221     // now the dictionary has been checkpointed
222     // copy the file to something with a new name,
223     // open it, and verify that the state of what is
224     // checkpointed is what we expect
225     //
226 
227     r = system("cp foo1.ft_handle bar1.ft_handle ");
228     assert_zero(r);
229 
230     FT_HANDLE c_ft;
231     r = toku_open_ft_handle("bar1.ft_handle", 0, &c_ft, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
232     assert(r==0);
233 
234     //
235     // now pin the root, verify that we have a message in there, and that it is clean
236     //
237     bfe.create_for_full_read(c_ft->ft);
238     toku_pin_ftnode(
239         c_ft->ft,
240         node_root,
241         toku_cachetable_hash(c_ft->ft->cf, node_root),
242         &bfe,
243         PL_WRITE_EXPENSIVE,
244         &node,
245         true
246         );
247     assert(node->height == 1);
248     assert(!node->dirty());
249     assert(node->n_children == 1);
250     if (after_child_pin) {
251         assert(toku_bnc_nbytesinbuf(BNC(node, 0)) == 0);
252     }
253     else {
254         assert(toku_bnc_nbytesinbuf(BNC(node, 0)) > 0);
255     }
256     toku_unpin_ftnode(c_ft->ft, node);
257 
258     toku_pin_ftnode(
259         c_ft->ft,
260         node_leaf,
261         toku_cachetable_hash(c_ft->ft->cf, node_root),
262         &bfe,
263         PL_WRITE_EXPENSIVE,
264         &node,
265         true
266         );
267     assert(node->height == 0);
268     assert(!node->dirty());
269     assert(node->n_children == 1);
270     if (after_child_pin) {
271         assert(BLB_NBYTESINDATA(node,0) > 0);
272     }
273     else {
274         assert(BLB_NBYTESINDATA(node,0) == 0);
275     }
276     toku_unpin_ftnode(c_ft->ft, node);
277 
278     struct check_pair pair1 = {2, "a", 0, NULL, 0};
279     DBT k;
280     r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1);
281     assert(r==0);
282 
283 
284     r = toku_close_ft_handle_nolsn(t, 0);    assert(r==0);
285     r = toku_close_ft_handle_nolsn(c_ft, 0);    assert(r==0);
286     toku_cachetable_close(&ct);
287 }
288 
289 int
test_main(int argc,const char * argv[])290 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) {
291     default_parse_args(argc, argv);
292     doit(false);
293     doit(true);
294     return 0;
295 }
296