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