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