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