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