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 <util/dbt.h>
42 #include <ft/ft-cachetable-wrappers.h>
43 #include <ft/ft-flusher.h>
44 
45 // Promotion tracks the rightmost blocknum in the FT when a message
46 // is successfully promoted to a non-root leaf node on the right extreme.
47 //
48 // This test verifies that a split or merge of the rightmost leaf properly
49 // maintains the rightmost blocknum (which is constant - the pair's swap values,
50 // like the root blocknum).
51 
test_split_merge(void)52 static void test_split_merge(void) {
53     int r = 0;
54     char name[TOKU_PATH_MAX + 1];
55     toku_path_join(name, 2, TOKU_TEST_FILENAME, "ftdata");
56     toku_os_recursive_delete(TOKU_TEST_FILENAME);
57     r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU); CKERR(r);
58 
59     FT_HANDLE ft_handle;
60     CACHETABLE ct;
61     toku_cachetable_create(&ct, 0, ZERO_LSN, nullptr);
62     r = toku_open_ft_handle(name, 1, &ft_handle,
63                             4*1024*1024, 64*1024,
64                             TOKU_DEFAULT_COMPRESSION_METHOD, ct, NULL,
65                             toku_builtin_compare_fun); CKERR(r);
66 
67     // We have a root blocknum, but no rightmost blocknum yet.
68     FT ft = ft_handle->ft;
69     invariant(ft->h->root_blocknum.b != RESERVED_BLOCKNUM_NULL);
70     invariant(ft->rightmost_blocknum.b == RESERVED_BLOCKNUM_NULL);
71 
72     int k;
73     DBT key, val;
74     const int val_size = 1 * 1024 * 1024;
75     char *XMALLOC_N(val_size, val_buf);
76     memset(val_buf, 'x', val_size);
77     toku_fill_dbt(&val, val_buf, val_size);
78 
79     // Insert 16 rows (should induce a few splits)
80     const int rows_to_insert = 16;
81     for (int i = 0; i < rows_to_insert; i++) {
82         k = toku_htonl(i);
83         toku_fill_dbt(&key, &k, sizeof(k));
84         toku_ft_insert(ft_handle, &key, &val, NULL);
85     }
86 
87     // rightmost blocknum should be set, because the root split and promotion
88     // did a rightmost insertion directly into the rightmost leaf, lazily
89     // initializing the rightmost blocknum.
90     invariant(ft->rightmost_blocknum.b != RESERVED_BLOCKNUM_NULL);
91 
92     BLOCKNUM root_blocknum = ft->h->root_blocknum;
93     FTNODE root_node;
94     ftnode_fetch_extra bfe;
95     bfe.create_for_full_read(ft);
96     toku_pin_ftnode(ft, root_blocknum,
97                    toku_cachetable_hash(ft->cf, ft->h->root_blocknum),
98                    &bfe, PL_WRITE_EXPENSIVE, &root_node, true);
99     // root blocknum should be consistent
100     invariant(root_node->blocknum.b == ft->h->root_blocknum.b);
101     // root should have split at least once, and it should now be at height 1
102     invariant(root_node->n_children > 1);
103     invariant(root_node->height == 1);
104     // rightmost blocknum should no longer be the root, since the root split
105     invariant(ft->h->root_blocknum.b != ft->rightmost_blocknum.b);
106     // the right child should have the rightmost blocknum
107     invariant(BP_BLOCKNUM(root_node, root_node->n_children - 1).b == ft->rightmost_blocknum.b);
108 
109     BLOCKNUM rightmost_blocknum_before_merge = ft->rightmost_blocknum;
110     const int num_children_before_merge = root_node->n_children;
111 
112     // delete the last 6 rows.
113     // - 1mb each, so 6mb deleted
114     // - should be enough to delete the entire rightmost leaf + some of its neighbor
115     const int rows_to_delete = 6;
116     toku_unpin_ftnode(ft, root_node);
117     for (int i = 0; i < rows_to_delete; i++) {
118         k = toku_htonl(rows_to_insert - i);
119         toku_fill_dbt(&key, &k, sizeof(k));
120         toku_ft_delete(ft_handle, &key, NULL);
121     }
122     toku_pin_ftnode(ft, root_blocknum,
123                     toku_cachetable_hash(ft->cf, root_blocknum),
124                     &bfe, PL_WRITE_EXPENSIVE, &root_node, true);
125 
126     // - rightmost leaf should be fusible after those deletes (which were promoted directly to the leaf)
127     FTNODE rightmost_leaf;
128     toku_pin_ftnode(ft, rightmost_blocknum_before_merge,
129                    toku_cachetable_hash(ft->cf, rightmost_blocknum_before_merge),
130                    &bfe, PL_WRITE_EXPENSIVE, &rightmost_leaf, true);
131     invariant(toku_ftnode_get_reactivity(ft, rightmost_leaf) == RE_FUSIBLE);
132     toku_unpin_ftnode(ft, rightmost_leaf);
133 
134     // - merge the rightmost child now that it's fusible
135     toku_ft_merge_child(ft, root_node, root_node->n_children - 1);
136     toku_pin_ftnode(ft, root_blocknum,
137                    toku_cachetable_hash(ft->cf, root_blocknum),
138                    &bfe, PL_WRITE_EXPENSIVE, &root_node, true);
139 
140     // the merge should have worked, and the root should still be at height 1
141     invariant(root_node->n_children < num_children_before_merge);
142     invariant(root_node->height == 1);
143     // the rightmost child of the root has the rightmost blocknum
144     invariant(BP_BLOCKNUM(root_node, root_node->n_children - 1).b == ft->rightmost_blocknum.b);
145     // the value for rightmost blocknum itself should not have changed
146     // (we keep it constant, like the root blocknum)
147     invariant(rightmost_blocknum_before_merge.b == ft->rightmost_blocknum.b);
148 
149     toku_unpin_ftnode(ft, root_node);
150 
151     toku_free(val_buf);
152     toku_ft_handle_close(ft_handle);
153     toku_cachetable_close(&ct);
154     toku_os_recursive_delete(TOKU_TEST_FILENAME);
155 }
156 
test_main(int argc,const char * argv[])157 int test_main(int argc, const char *argv[]) {
158     default_parse_args(argc, argv);
159     test_split_merge();
160     return 0;
161 }
162