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