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