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 
41 #include "ft/cursor.h"
42 
43 enum ftnode_verify_type { read_all = 1, read_compressed, read_none };
44 
45 #ifndef MIN
46 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
47 #endif
48 
string_key_cmp(DB * UU (e),const DBT * a,const DBT * b)49 static int string_key_cmp(DB *UU(e), const DBT *a, const DBT *b) {
50     char *CAST_FROM_VOIDP(s, a->data);
51     char *CAST_FROM_VOIDP(t, b->data);
52     return strcmp(s, t);
53 }
54 
le_add_to_bn(bn_data * bn,uint32_t idx,const char * key,int keylen,const char * val,int vallen)55 static void le_add_to_bn(bn_data *bn,
56                          uint32_t idx,
57                          const char *key,
58                          int keylen,
59                          const char *val,
60                          int vallen) {
61     LEAFENTRY r = NULL;
62     uint32_t size_needed = LE_CLEAN_MEMSIZE(vallen);
63     void *maybe_free = nullptr;
64     bn->get_space_for_insert(idx, key, keylen, size_needed, &r, &maybe_free);
65     if (maybe_free) {
66         toku_free(maybe_free);
67     }
68     resource_assert(r);
69     r->type = LE_CLEAN;
70     r->u.clean.vallen = vallen;
71     memcpy(r->u.clean.val, val, vallen);
72 }
73 
le_malloc(bn_data * bn,uint32_t idx,const char * key,const char * val)74 static void le_malloc(bn_data *bn,
75                       uint32_t idx,
76                       const char *key,
77                       const char *val) {
78     int keylen = strlen(key) + 1;
79     int vallen = strlen(val) + 1;
80     le_add_to_bn(bn, idx, key, keylen, val, vallen);
81 }
82 
test1(int fd,FT ft_h,FTNODE * dn)83 static void test1(int fd, FT ft_h, FTNODE *dn) {
84     int r;
85     ftnode_fetch_extra bfe_all;
86     bfe_all.create_for_full_read(ft_h);
87     FTNODE_DISK_DATA ndd = NULL;
88     r = toku_deserialize_ftnode_from(
89         fd, make_blocknum(20), 0 /*pass zero for hash*/, dn, &ndd, &bfe_all);
90     bool is_leaf = ((*dn)->height == 0);
91     invariant(r == 0);
92     for (int i = 0; i < (*dn)->n_children; i++) {
93         invariant(BP_STATE(*dn, i) == PT_AVAIL);
94     }
95     // should sweep and NOT get rid of anything
96     PAIR_ATTR attr;
97     memset(&attr, 0, sizeof(attr));
98     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
99     for (int i = 0; i < (*dn)->n_children; i++) {
100         invariant(BP_STATE(*dn, i) == PT_AVAIL);
101     }
102     // should sweep and get compress all
103     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
104     for (int i = 0; i < (*dn)->n_children; i++) {
105         if (!is_leaf) {
106             invariant(BP_STATE(*dn, i) == PT_COMPRESSED);
107         } else {
108             invariant(BP_STATE(*dn, i) == PT_ON_DISK);
109         }
110     }
111     PAIR_ATTR size;
112     bool req = toku_ftnode_pf_req_callback(*dn, &bfe_all);
113     invariant(req);
114     toku_ftnode_pf_callback(*dn, ndd, &bfe_all, fd, &size);
115     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
116     for (int i = 0; i < (*dn)->n_children; i++) {
117         invariant(BP_STATE(*dn, i) == PT_AVAIL);
118     }
119     // should sweep and get compress all
120     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
121     for (int i = 0; i < (*dn)->n_children; i++) {
122         if (!is_leaf) {
123             invariant(BP_STATE(*dn, i) == PT_COMPRESSED);
124         } else {
125             invariant(BP_STATE(*dn, i) == PT_ON_DISK);
126         }
127     }
128 
129     req = toku_ftnode_pf_req_callback(*dn, &bfe_all);
130     invariant(req);
131     toku_ftnode_pf_callback(*dn, ndd, &bfe_all, fd, &size);
132     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
133     for (int i = 0; i < (*dn)->n_children; i++) {
134         invariant(BP_STATE(*dn, i) == PT_AVAIL);
135     }
136     (*dn)->set_dirty();
137     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
138     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
139     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
140     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
141     for (int i = 0; i < (*dn)->n_children; i++) {
142         invariant(BP_STATE(*dn, i) == PT_AVAIL);
143     }
144     toku_free(ndd);
145     toku_ftnode_free(dn);
146 }
147 
search_cmp(const struct ft_search & UU (so),const DBT * UU (key))148 static int search_cmp(const struct ft_search &UU(so), const DBT *UU(key)) {
149     return 0;
150 }
151 
test2(int fd,FT ft_h,FTNODE * dn)152 static void test2(int fd, FT ft_h, FTNODE *dn) {
153     DBT left, right;
154     DB dummy_db;
155     memset(&dummy_db, 0, sizeof(dummy_db));
156     memset(&left, 0, sizeof(left));
157     memset(&right, 0, sizeof(right));
158     ft_search search;
159 
160     ftnode_fetch_extra bfe_subset;
161     bfe_subset.create_for_subset_read(
162         ft_h,
163         ft_search_init(
164             &search, search_cmp, FT_SEARCH_LEFT, nullptr, nullptr, nullptr),
165         &left,
166         &right,
167         true,
168         true,
169         false,
170         false);
171 
172     FTNODE_DISK_DATA ndd = NULL;
173     int r = toku_deserialize_ftnode_from(
174         fd, make_blocknum(20), 0 /*pass zero for hash*/, dn, &ndd, &bfe_subset);
175     invariant(r == 0);
176     bool is_leaf = ((*dn)->height == 0);
177     // at this point, although both partitions are available, only the
178     // second basement node should have had its clock
179     // touched
180     invariant(BP_STATE(*dn, 0) == PT_AVAIL);
181     invariant(BP_STATE(*dn, 1) == PT_AVAIL);
182     invariant(BP_SHOULD_EVICT(*dn, 0));
183     invariant(!BP_SHOULD_EVICT(*dn, 1));
184     PAIR_ATTR attr;
185     memset(&attr, 0, sizeof(attr));
186     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
187     invariant(BP_STATE(*dn, 0) == ((is_leaf) ? PT_ON_DISK : PT_COMPRESSED));
188     invariant(BP_STATE(*dn, 1) == PT_AVAIL);
189     invariant(BP_SHOULD_EVICT(*dn, 1));
190     toku_ftnode_pe_callback(*dn, attr, ft_h, def_pe_finalize_impl, nullptr);
191     invariant(BP_STATE(*dn, 1) == ((is_leaf) ? PT_ON_DISK : PT_COMPRESSED));
192 
193     bool req = toku_ftnode_pf_req_callback(*dn, &bfe_subset);
194     invariant(req);
195     toku_ftnode_pf_callback(*dn, ndd, &bfe_subset, fd, &attr);
196     invariant(BP_STATE(*dn, 0) == PT_AVAIL);
197     invariant(BP_STATE(*dn, 1) == PT_AVAIL);
198     invariant(BP_SHOULD_EVICT(*dn, 0));
199     invariant(!BP_SHOULD_EVICT(*dn, 1));
200 
201     toku_free(ndd);
202     toku_ftnode_free(dn);
203 }
204 
test3_leaf(int fd,FT ft_h,FTNODE * dn)205 static void test3_leaf(int fd, FT ft_h, FTNODE *dn) {
206     DBT left, right;
207     DB dummy_db;
208     memset(&dummy_db, 0, sizeof(dummy_db));
209     memset(&left, 0, sizeof(left));
210     memset(&right, 0, sizeof(right));
211 
212     ftnode_fetch_extra bfe_min;
213     bfe_min.create_for_min_read(ft_h);
214 
215     FTNODE_DISK_DATA ndd = NULL;
216     int r = toku_deserialize_ftnode_from(
217         fd, make_blocknum(20), 0 /*pass zero for hash*/, dn, &ndd, &bfe_min);
218     invariant(r == 0);
219     //
220     // make sure we have a leaf
221     //
222     invariant((*dn)->height == 0);
223     for (int i = 0; i < (*dn)->n_children; i++) {
224         invariant(BP_STATE(*dn, i) == PT_ON_DISK);
225     }
226     toku_ftnode_free(dn);
227     toku_free(ndd);
228 }
229 
test_serialize_nonleaf(void)230 static void test_serialize_nonleaf(void) {
231     //    struct ft_handle source_ft;
232     struct ftnode sn, *dn;
233 
234     int fd = open(TOKU_TEST_FILENAME,
235                   O_RDWR | O_CREAT | O_BINARY,
236                   S_IRWXU | S_IRWXG | S_IRWXO);
237     invariant(fd >= 0);
238 
239     int r;
240 
241     //    source_ft.fd=fd;
242     sn.max_msn_applied_to_node_on_disk.msn = 0;
243     sn.flags = 0x11223344;
244     sn.blocknum.b = 20;
245     sn.layout_version = FT_LAYOUT_VERSION;
246     sn.layout_version_original = FT_LAYOUT_VERSION;
247     sn.height = 1;
248     sn.n_children = 2;
249     sn.set_dirty();
250     sn.oldest_referenced_xid_known = TXNID_NONE;
251     MALLOC_N(2, sn.bp);
252     DBT pivotkey;
253     sn.pivotkeys.create_from_dbts(toku_fill_dbt(&pivotkey, "hello", 6), 1);
254     BP_BLOCKNUM(&sn, 0).b = 30;
255     BP_BLOCKNUM(&sn, 1).b = 35;
256     BP_STATE(&sn, 0) = PT_AVAIL;
257     BP_STATE(&sn, 1) = PT_AVAIL;
258     set_BNC(&sn, 0, toku_create_empty_nl());
259     set_BNC(&sn, 1, toku_create_empty_nl());
260     // Create XIDS
261     XIDS xids_0 = toku_xids_get_root_xids();
262     XIDS xids_123;
263     XIDS xids_234;
264     r = toku_xids_create_child(xids_0, &xids_123, (TXNID)123);
265     CKERR(r);
266     r = toku_xids_create_child(xids_123, &xids_234, (TXNID)234);
267     CKERR(r);
268 
269     toku::comparator cmp;
270     cmp.create(string_key_cmp, nullptr);
271 
272     toku_bnc_insert_msg(BNC(&sn, 0),
273                         "a",
274                         2,
275                         "aval",
276                         5,
277                         FT_NONE,
278                         next_dummymsn(),
279                         xids_0,
280                         true,
281                         cmp);
282     toku_bnc_insert_msg(BNC(&sn, 0),
283                         "b",
284                         2,
285                         "bval",
286                         5,
287                         FT_NONE,
288                         next_dummymsn(),
289                         xids_123,
290                         false,
291                         cmp);
292     toku_bnc_insert_msg(BNC(&sn, 1),
293                         "x",
294                         2,
295                         "xval",
296                         5,
297                         FT_NONE,
298                         next_dummymsn(),
299                         xids_234,
300                         true,
301                         cmp);
302 
303     // Cleanup:
304     toku_xids_destroy(&xids_0);
305     toku_xids_destroy(&xids_123);
306     toku_xids_destroy(&xids_234);
307     cmp.destroy();
308 
309     FT_HANDLE XMALLOC(ft);
310     FT XCALLOC(ft_h);
311     toku_ft_init(ft_h,
312                  make_blocknum(0),
313                  ZERO_LSN,
314                  TXNID_NONE,
315                  4 * 1024 * 1024,
316                  128 * 1024,
317                  TOKU_DEFAULT_COMPRESSION_METHOD,
318                  16);
319     ft_h->cmp.create(string_key_cmp, nullptr);
320     ft->ft = ft_h;
321 
322     ft_h->blocktable.create();
323     {
324         int r_truncate = ftruncate(fd, 0);
325         CKERR(r_truncate);
326     }
327     // Want to use block #20
328     BLOCKNUM b = make_blocknum(0);
329     while (b.b < 20) {
330         ft_h->blocktable.allocate_blocknum(&b, ft_h);
331     }
332     invariant(b.b == 20);
333 
334     {
335         DISKOFF offset;
336         DISKOFF size;
337         ft_h->blocktable.realloc_on_disk(b, 100, &offset, ft_h, fd, false);
338         invariant(offset ==
339                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
340 
341         ft_h->blocktable.translate_blocknum_to_offset_size(b, &offset, &size);
342         invariant(offset ==
343                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
344         invariant(size == 100);
345     }
346     FTNODE_DISK_DATA ndd = NULL;
347     r = toku_serialize_ftnode_to(
348         fd, make_blocknum(20), &sn, &ndd, true, ft->ft, false);
349     invariant(r == 0);
350 
351     test1(fd, ft_h, &dn);
352     test2(fd, ft_h, &dn);
353 
354     toku_destroy_ftnode_internals(&sn);
355     toku_free(ndd);
356 
357     ft_h->blocktable.block_free(
358         BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE, 100);
359     ft_h->blocktable.destroy();
360     toku_free(ft_h->h);
361     ft_h->cmp.destroy();
362     toku_free(ft_h);
363     toku_free(ft);
364 
365     r = close(fd);
366     invariant(r != -1);
367 }
368 
test_serialize_leaf(void)369 static void test_serialize_leaf(void) {
370     //    struct ft_handle source_ft;
371     struct ftnode sn, *dn;
372 
373     int fd = open(TOKU_TEST_FILENAME,
374                   O_RDWR | O_CREAT | O_BINARY,
375                   S_IRWXU | S_IRWXG | S_IRWXO);
376     invariant(fd >= 0);
377 
378     int r;
379 
380     sn.max_msn_applied_to_node_on_disk.msn = 0;
381     sn.flags = 0x11223344;
382     sn.blocknum.b = 20;
383     sn.layout_version = FT_LAYOUT_VERSION;
384     sn.layout_version_original = FT_LAYOUT_VERSION;
385     sn.height = 0;
386     sn.n_children = 2;
387     sn.set_dirty();
388     sn.oldest_referenced_xid_known = TXNID_NONE;
389     MALLOC_N(sn.n_children, sn.bp);
390     DBT pivotkey;
391     sn.pivotkeys.create_from_dbts(toku_fill_dbt(&pivotkey, "b", 2), 1);
392     BP_STATE(&sn, 0) = PT_AVAIL;
393     BP_STATE(&sn, 1) = PT_AVAIL;
394     set_BLB(&sn, 0, toku_create_empty_bn());
395     set_BLB(&sn, 1, toku_create_empty_bn());
396     le_malloc(BLB_DATA(&sn, 0), 0, "a", "aval");
397     le_malloc(BLB_DATA(&sn, 0), 1, "b", "bval");
398     le_malloc(BLB_DATA(&sn, 1), 0, "x", "xval");
399 
400     FT_HANDLE XMALLOC(ft);
401     FT XCALLOC(ft_h);
402     toku_ft_init(ft_h,
403                  make_blocknum(0),
404                  ZERO_LSN,
405                  TXNID_NONE,
406                  4 * 1024 * 1024,
407                  128 * 1024,
408                  TOKU_DEFAULT_COMPRESSION_METHOD,
409                  16);
410     ft->ft = ft_h;
411 
412     ft_h->blocktable.create();
413     {
414         int r_truncate = ftruncate(fd, 0);
415         CKERR(r_truncate);
416     }
417     // Want to use block #20
418     BLOCKNUM b = make_blocknum(0);
419     while (b.b < 20) {
420         ft_h->blocktable.allocate_blocknum(&b, ft_h);
421     }
422     invariant(b.b == 20);
423 
424     {
425         DISKOFF offset;
426         DISKOFF size;
427         ft_h->blocktable.realloc_on_disk(b, 100, &offset, ft_h, fd, false);
428         invariant(offset ==
429                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
430 
431         ft_h->blocktable.translate_blocknum_to_offset_size(b, &offset, &size);
432         invariant(offset ==
433                (DISKOFF)BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE);
434         invariant(size == 100);
435     }
436     FTNODE_DISK_DATA ndd = NULL;
437     r = toku_serialize_ftnode_to(
438         fd, make_blocknum(20), &sn, &ndd, true, ft->ft, false);
439     invariant(r == 0);
440 
441     test1(fd, ft_h, &dn);
442     test3_leaf(fd, ft_h, &dn);
443 
444     toku_destroy_ftnode_internals(&sn);
445 
446     ft_h->blocktable.block_free(
447         BlockAllocator::BLOCK_ALLOCATOR_TOTAL_HEADER_RESERVE, 100);
448     ft_h->blocktable.destroy();
449     toku_free(ft_h->h);
450     toku_free(ft_h);
451     toku_free(ft);
452     toku_free(ndd);
453     r = close(fd);
454     invariant(r != -1);
455 }
456 
test_main(int argc,const char * argv[])457 int test_main(int argc __attribute__((__unused__)),
458               const char *argv[] __attribute__((__unused__))) {
459     initialize_dummymsn();
460     test_serialize_nonleaf();
461     test_serialize_leaf();
462 
463     return 0;
464 }
465