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 #include "test.h"
40
int64_key_cmp(DB * db UU (),const DBT * a,const DBT * b)41 static int int64_key_cmp(DB *db UU(), const DBT *a, const DBT *b) {
42 int64_t x = *(int64_t *)a->data;
43 int64_t y = *(int64_t *)b->data;
44
45 if (x < y)
46 return -1;
47 if (x > y)
48 return 1;
49 return 0;
50 }
51
test_prefetch_read(int fd,FT_HANDLE UU (ft),FT ft_h)52 static void test_prefetch_read(int fd, FT_HANDLE UU(ft), FT ft_h) {
53 int r;
54 FT_CURSOR XMALLOC(cursor);
55 FTNODE dn = NULL;
56 PAIR_ATTR attr;
57
58 // first test that prefetching everything should work
59 memset(&cursor->range_lock_left_key, 0, sizeof(DBT));
60 memset(&cursor->range_lock_right_key, 0, sizeof(DBT));
61 cursor->left_is_neg_infty = true;
62 cursor->right_is_pos_infty = true;
63 cursor->disable_prefetching = false;
64
65 ftnode_fetch_extra bfe;
66
67 // quick test to see that we have the right behavior when we set
68 // disable_prefetching to true
69 cursor->disable_prefetching = true;
70 bfe.create_for_prefetch(ft_h, cursor);
71 FTNODE_DISK_DATA ndd = NULL;
72 r = toku_deserialize_ftnode_from(
73 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
74 invariant(r == 0);
75 invariant(dn->n_children == 3);
76 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
77 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
78 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
79 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
80 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
81 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
82 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
83 bfe.destroy();
84 toku_ftnode_free(&dn);
85 toku_free(ndd);
86
87 // now enable prefetching again
88 cursor->disable_prefetching = false;
89
90 bfe.create_for_prefetch(ft_h, cursor);
91 r = toku_deserialize_ftnode_from(
92 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
93 invariant(r == 0);
94 invariant(dn->n_children == 3);
95 invariant(BP_STATE(dn, 0) == PT_AVAIL);
96 invariant(BP_STATE(dn, 1) == PT_AVAIL);
97 invariant(BP_STATE(dn, 2) == PT_AVAIL);
98 toku_ftnode_pe_callback(
99 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
100 invariant(BP_STATE(dn, 0) == PT_COMPRESSED);
101 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
102 invariant(BP_STATE(dn, 2) == PT_COMPRESSED);
103 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
104 invariant(BP_STATE(dn, 0) == PT_AVAIL);
105 invariant(BP_STATE(dn, 1) == PT_AVAIL);
106 invariant(BP_STATE(dn, 2) == PT_AVAIL);
107 bfe.destroy();
108 toku_ftnode_free(&dn);
109 toku_free(ndd);
110
111 uint64_t left_key = 150;
112 toku_fill_dbt(&cursor->range_lock_left_key, &left_key, sizeof(uint64_t));
113 cursor->left_is_neg_infty = false;
114 bfe.create_for_prefetch(ft_h, cursor);
115 r = toku_deserialize_ftnode_from(
116 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
117 invariant(r == 0);
118 invariant(dn->n_children == 3);
119 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
120 invariant(BP_STATE(dn, 1) == PT_AVAIL);
121 invariant(BP_STATE(dn, 2) == PT_AVAIL);
122 toku_ftnode_pe_callback(
123 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
124 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
125 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
126 invariant(BP_STATE(dn, 2) == PT_COMPRESSED);
127 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
128 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
129 invariant(BP_STATE(dn, 1) == PT_AVAIL);
130 invariant(BP_STATE(dn, 2) == PT_AVAIL);
131 bfe.destroy();
132 toku_ftnode_free(&dn);
133 toku_free(ndd);
134
135 uint64_t right_key = 151;
136 toku_fill_dbt(&cursor->range_lock_right_key, &right_key, sizeof(uint64_t));
137 cursor->right_is_pos_infty = false;
138 bfe.create_for_prefetch(ft_h, cursor);
139 r = toku_deserialize_ftnode_from(
140 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
141 invariant(r == 0);
142 invariant(dn->n_children == 3);
143 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
144 invariant(BP_STATE(dn, 1) == PT_AVAIL);
145 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
146 toku_ftnode_pe_callback(
147 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
148 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
149 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
150 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
151 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
152 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
153 invariant(BP_STATE(dn, 1) == PT_AVAIL);
154 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
155 bfe.destroy();
156 toku_ftnode_free(&dn);
157 toku_free(ndd);
158
159 left_key = 100000;
160 right_key = 100000;
161 bfe.create_for_prefetch(ft_h, cursor);
162 r = toku_deserialize_ftnode_from(
163 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
164 invariant(r == 0);
165 invariant(dn->n_children == 3);
166 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
167 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
168 invariant(BP_STATE(dn, 2) == PT_AVAIL);
169 toku_ftnode_pe_callback(
170 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
171 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
172 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
173 invariant(BP_STATE(dn, 2) == PT_COMPRESSED);
174 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
175 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
176 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
177 invariant(BP_STATE(dn, 2) == PT_AVAIL);
178 bfe.destroy();
179 toku_free(ndd);
180 toku_ftnode_free(&dn);
181
182 left_key = 100;
183 right_key = 100;
184 bfe.create_for_prefetch(ft_h, cursor);
185 r = toku_deserialize_ftnode_from(
186 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
187 invariant(r == 0);
188 invariant(dn->n_children == 3);
189 invariant(BP_STATE(dn, 0) == PT_AVAIL);
190 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
191 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
192 toku_ftnode_pe_callback(
193 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
194 invariant(BP_STATE(dn, 0) == PT_COMPRESSED);
195 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
196 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
197 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
198 invariant(BP_STATE(dn, 0) == PT_AVAIL);
199 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
200 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
201 bfe.destroy();
202 toku_ftnode_free(&dn);
203 toku_free(ndd);
204
205 toku_free(cursor);
206 }
207
test_subset_read(int fd,FT_HANDLE UU (ft),FT ft_h)208 static void test_subset_read(int fd, FT_HANDLE UU(ft), FT ft_h) {
209 int r;
210 FT_CURSOR XMALLOC(cursor);
211 FTNODE dn = NULL;
212 FTNODE_DISK_DATA ndd = NULL;
213 PAIR_ATTR attr;
214
215 // first test that prefetching everything should work
216 memset(&cursor->range_lock_left_key, 0, sizeof(DBT));
217 memset(&cursor->range_lock_right_key, 0, sizeof(DBT));
218 cursor->left_is_neg_infty = true;
219 cursor->right_is_pos_infty = true;
220
221 uint64_t left_key = 150;
222 uint64_t right_key = 151;
223 DBT left, right;
224 toku_fill_dbt(&left, &left_key, sizeof(left_key));
225 toku_fill_dbt(&right, &right_key, sizeof(right_key));
226
227 ftnode_fetch_extra bfe;
228 bfe.create_for_subset_read(
229 ft_h, NULL, &left, &right, false, false, false, false);
230
231 // fake the childnum to read
232 // set disable_prefetching ON
233 bfe.child_to_read = 2;
234 bfe.disable_prefetching = true;
235 r = toku_deserialize_ftnode_from(
236 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
237 invariant(r == 0);
238 invariant(dn->n_children == 3);
239 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
240 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
241 invariant(BP_STATE(dn, 2) == PT_AVAIL);
242 // need to call this twice because we had a subset read before, that touched
243 // the clock
244 toku_ftnode_pe_callback(
245 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
246 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
247 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
248 invariant(BP_STATE(dn, 2) == PT_AVAIL);
249 toku_ftnode_pe_callback(
250 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
251 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
252 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
253 invariant(BP_STATE(dn, 2) == PT_COMPRESSED);
254 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
255 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
256 invariant(BP_STATE(dn, 1) == PT_ON_DISK);
257 invariant(BP_STATE(dn, 2) == PT_AVAIL);
258 toku_ftnode_free(&dn);
259 toku_free(ndd);
260
261 // fake the childnum to read
262 bfe.child_to_read = 2;
263 bfe.disable_prefetching = false;
264 r = toku_deserialize_ftnode_from(
265 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
266 invariant(r == 0);
267 invariant(dn->n_children == 3);
268 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
269 invariant(BP_STATE(dn, 1) == PT_AVAIL);
270 invariant(BP_STATE(dn, 2) == PT_AVAIL);
271 // need to call this twice because we had a subset read before, that touched
272 // the clock
273 toku_ftnode_pe_callback(
274 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
275 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
276 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
277 invariant(BP_STATE(dn, 2) == PT_AVAIL);
278 toku_ftnode_pe_callback(
279 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
280 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
281 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
282 invariant(BP_STATE(dn, 2) == PT_COMPRESSED);
283 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
284 invariant(BP_STATE(dn, 0) == PT_ON_DISK);
285 invariant(BP_STATE(dn, 1) == PT_AVAIL);
286 invariant(BP_STATE(dn, 2) == PT_AVAIL);
287 toku_ftnode_free(&dn);
288 toku_free(ndd);
289
290 // fake the childnum to read
291 bfe.child_to_read = 0;
292 r = toku_deserialize_ftnode_from(
293 fd, make_blocknum(20), 0 /*pass zero for hash*/, &dn, &ndd, &bfe);
294 invariant(r == 0);
295 invariant(dn->n_children == 3);
296 invariant(BP_STATE(dn, 0) == PT_AVAIL);
297 invariant(BP_STATE(dn, 1) == PT_AVAIL);
298 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
299 // need to call this twice because we had a subset read before, that touched
300 // the clock
301 toku_ftnode_pe_callback(
302 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
303 invariant(BP_STATE(dn, 0) == PT_AVAIL);
304 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
305 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
306 toku_ftnode_pe_callback(
307 dn, make_pair_attr(0xffffffff), ft_h, def_pe_finalize_impl, nullptr);
308 invariant(BP_STATE(dn, 0) == PT_COMPRESSED);
309 invariant(BP_STATE(dn, 1) == PT_COMPRESSED);
310 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
311 r = toku_ftnode_pf_callback(dn, ndd, &bfe, fd, &attr);
312 invariant(BP_STATE(dn, 0) == PT_AVAIL);
313 invariant(BP_STATE(dn, 1) == PT_AVAIL);
314 invariant(BP_STATE(dn, 2) == PT_ON_DISK);
315 toku_ftnode_free(&dn);
316 toku_free(ndd);
317
318 toku_free(cursor);
319 }
320
test_prefetching(void)321 static void test_prefetching(void) {
322 // struct ft_handle source_ft;
323 struct ftnode sn;
324
325 int fd = open(TOKU_TEST_FILENAME,
326 O_RDWR | O_CREAT | O_BINARY,
327 S_IRWXU | S_IRWXG | S_IRWXO);
328 invariant(fd >= 0);
329
330 int r;
331
332 // source_ft.fd=fd;
333 sn.max_msn_applied_to_node_on_disk.msn = 0;
334 sn.flags = 0x11223344;
335 sn.blocknum.b = 20;
336 sn.layout_version = FT_LAYOUT_VERSION;
337 sn.layout_version_original = FT_LAYOUT_VERSION;
338 sn.height = 1;
339 sn.n_children = 3;
340 sn.set_dirty();
341 sn.oldest_referenced_xid_known = TXNID_NONE;
342
343 uint64_t key1 = 100;
344 uint64_t key2 = 200;
345
346 MALLOC_N(sn.n_children, sn.bp);
347 DBT pivotkeys[2];
348 toku_fill_dbt(&pivotkeys[0], &key1, sizeof(key1));
349 toku_fill_dbt(&pivotkeys[1], &key2, sizeof(key2));
350 sn.pivotkeys.create_from_dbts(pivotkeys, 2);
351 BP_BLOCKNUM(&sn, 0).b = 30;
352 BP_BLOCKNUM(&sn, 1).b = 35;
353 BP_BLOCKNUM(&sn, 2).b = 40;
354 BP_STATE(&sn, 0) = PT_AVAIL;
355 BP_STATE(&sn, 1) = PT_AVAIL;
356 BP_STATE(&sn, 2) = PT_AVAIL;
357 set_BNC(&sn, 0, toku_create_empty_nl());
358 set_BNC(&sn, 1, toku_create_empty_nl());
359 set_BNC(&sn, 2, toku_create_empty_nl());
360 // Create XIDS
361 XIDS xids_0 = toku_xids_get_root_xids();
362 XIDS xids_123;
363 XIDS xids_234;
364 r = toku_xids_create_child(xids_0, &xids_123, (TXNID)123);
365 CKERR(r);
366 r = toku_xids_create_child(xids_123, &xids_234, (TXNID)234);
367 CKERR(r);
368
369 // data in the buffers does not matter in this test
370 // Cleanup:
371 toku_xids_destroy(&xids_0);
372 toku_xids_destroy(&xids_123);
373 toku_xids_destroy(&xids_234);
374
375 FT_HANDLE XMALLOC(ft);
376 FT XCALLOC(ft_h);
377 toku_ft_init(ft_h,
378 make_blocknum(0),
379 ZERO_LSN,
380 TXNID_NONE,
381 4 * 1024 * 1024,
382 128 * 1024,
383 TOKU_DEFAULT_COMPRESSION_METHOD,
384 16);
385 ft_h->cmp.create(int64_key_cmp, nullptr);
386 ft->ft = ft_h;
387 ft_h->blocktable.create();
388 {
389 int r_truncate = ftruncate(fd, 0);
390 CKERR(r_truncate);
391 }
392 // Want to use block #20
393 BLOCKNUM b = make_blocknum(0);
394 while (b.b < 20) {
395 ft_h->blocktable.allocate_blocknum(&b, ft_h);
396 }
397 invariant(b.b == 20);
398
399 {
400 DISKOFF offset;
401 DISKOFF size;
402 ft_h->blocktable.realloc_on_disk(b, 100, &offset, ft_h, fd, false);
403 invariant(offset ==
404 (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
405
406 ft_h->blocktable.translate_blocknum_to_offset_size(b, &offset, &size);
407 invariant(offset ==
408 (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
409 invariant(size == 100);
410 }
411 FTNODE_DISK_DATA ndd = NULL;
412 r = toku_serialize_ftnode_to(
413 fd, make_blocknum(20), &sn, &ndd, true, ft->ft, false);
414 invariant(r == 0);
415
416 test_prefetch_read(fd, ft, ft_h);
417 test_subset_read(fd, ft, ft_h);
418
419 toku_destroy_ftnode_internals(&sn);
420
421 ft_h->blocktable.block_free(
422 BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE, 100);
423 ft_h->blocktable.destroy();
424 ft_h->cmp.destroy();
425 toku_free(ft_h->h);
426 toku_free(ft_h);
427 toku_free(ft);
428 toku_free(ndd);
429
430 r = close(fd);
431 invariant(r != -1);
432 }
433
test_main(int argc,const char * argv[])434 int test_main(int argc __attribute__((__unused__)),
435 const char *argv[] __attribute__((__unused__))) {
436 test_prefetching();
437
438 return 0;
439 }
440