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 /* The goal of this test. Make sure that inserts stay behind deletes. */ 40 41 42 #include "test.h" 43 44 #include <ft-cachetable-wrappers.h> 45 #include "ft-flusher.h" 46 #include "ft-flusher-internal.h" 47 #include "cachetable/checkpoint.h" 48 49 static TOKUTXN const null_txn = 0; 50 51 enum { NODESIZE = 1024, KSIZE=NODESIZE-100, TOKU_PSIZE=20 }; 52 53 CACHETABLE ct; 54 FT_HANDLE t; 55 56 bool checkpoint_called; 57 bool checkpoint_callback_called; 58 toku_pthread_t checkpoint_tid; 59 60 61 // callback functions for toku_ft_flush_some_child 62 static bool 63 dont_destroy_bn(void* UU(extra)) 64 { 65 return false; 66 } 67 static void merge_should_not_happen(struct flusher_advice* UU(fa), 68 FT UU(h), 69 FTNODE UU(parent), 70 int UU(childnum), 71 FTNODE UU(child), 72 void* UU(extra)) 73 { 74 assert(false); 75 } 76 77 static bool recursively_flush_should_not_happen(FTNODE UU(child), void* UU(extra)) { 78 assert(false); 79 } 80 81 static int child_to_flush(FT UU(h), FTNODE parent, void* UU(extra)) { 82 assert(parent->height == 1); 83 assert(parent->n_children == 1); 84 return 0; 85 } 86 87 static void dummy_update_status(FTNODE UU(child), int UU(dirtied), void* UU(extra)) { 88 } 89 90 91 static void checkpoint_callback(void* UU(extra)) { 92 usleep(1*1024*1024); 93 checkpoint_callback_called = true; 94 } 95 96 97 static void *do_checkpoint(void *arg) { 98 // first verify that checkpointed_data is correct; 99 if (verbose) printf("starting a checkpoint\n"); 100 CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct); 101 int r = toku_checkpoint(cp, NULL, checkpoint_callback, NULL, NULL, NULL, CLIENT_CHECKPOINT); 102 assert_zero(r); 103 if (verbose) printf("completed a checkpoint\n"); 104 return arg; 105 } 106 107 108 static void flusher_callback(int state, void* extra) { 109 bool after_child_pin = *(bool *)extra; 110 if (verbose) { 111 printf("state %d\n", state); 112 } 113 if ((state == flt_flush_before_child_pin && !after_child_pin) || 114 (state == ft_flush_aflter_child_pin && after_child_pin)) { 115 checkpoint_called = true; 116 int r = toku_pthread_create(toku_uninstrumented, 117 &checkpoint_tid, 118 nullptr, 119 do_checkpoint, 120 nullptr); 121 assert_zero(r); 122 while (!checkpoint_callback_called) { 123 usleep(1 * 1024 * 1024); 124 } 125 } 126 } 127 128 static void 129 doit (bool after_child_pin) { 130 BLOCKNUM node_leaf, node_root; 131 132 int r; 133 checkpoint_called = false; 134 checkpoint_callback_called = false; 135 136 toku_flusher_thread_set_callback(flusher_callback, &after_child_pin); 137 138 toku_cachetable_create(&ct, 500*1024*1024, ZERO_LSN, nullptr); 139 unlink("foo1.ft_handle"); 140 r = toku_open_ft_handle("foo1.ft_handle", 1, &t, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun); 141 assert(r==0); 142 143 toku_testsetup_initialize(); // must precede any other toku_testsetup calls 144 145 r = toku_testsetup_leaf(t, &node_leaf, 1, NULL, NULL); 146 assert(r==0); 147 148 r = toku_testsetup_nonleaf(t, 1, &node_root, 1, &node_leaf, 0, 0); 149 assert(r==0); 150 151 r = toku_testsetup_root(t, node_root); 152 assert(r==0); 153 154 155 r = toku_testsetup_insert_to_nonleaf( 156 t, 157 node_root, 158 FT_INSERT, 159 "a", 160 2, 161 NULL, 162 0 163 ); 164 165 // at this point, we have inserted a message into 166 // the root, and we wish to flush it, the leaf 167 // should be empty 168 169 struct flusher_advice fa; 170 flusher_advice_init( 171 &fa, 172 child_to_flush, 173 dont_destroy_bn, 174 recursively_flush_should_not_happen, 175 merge_should_not_happen, 176 dummy_update_status, 177 default_pick_child_after_split, 178 NULL 179 ); 180 181 FTNODE node = NULL; 182 ftnode_fetch_extra bfe; 183 bfe.create_for_min_read(t->ft); 184 toku_pin_ftnode( 185 t->ft, 186 node_root, 187 toku_cachetable_hash(t->ft->cf, node_root), 188 &bfe, 189 PL_WRITE_EXPENSIVE, 190 &node, 191 true 192 ); 193 assert(node->height == 1); 194 assert(node->n_children == 1); 195 assert(toku_bnc_nbytesinbuf(BNC(node, 0)) > 0); 196 197 // do the flush 198 toku_ft_flush_some_child(t->ft, node, &fa); 199 assert(checkpoint_callback_called); 200 201 // now let's pin the root again and make sure it is flushed 202 toku_pin_ftnode( 203 t->ft, 204 node_root, 205 toku_cachetable_hash(t->ft->cf, node_root), 206 &bfe, 207 PL_WRITE_EXPENSIVE, 208 &node, 209 true 210 ); 211 assert(node->height == 1); 212 assert(node->n_children == 1); 213 assert(toku_bnc_nbytesinbuf(BNC(node, 0)) == 0); 214 toku_unpin_ftnode(t->ft, node); 215 216 void *ret; 217 r = toku_pthread_join(checkpoint_tid, &ret); 218 assert_zero(r); 219 220 // 221 // now the dictionary has been checkpointed 222 // copy the file to something with a new name, 223 // open it, and verify that the state of what is 224 // checkpointed is what we expect 225 // 226 227 r = system("cp foo1.ft_handle bar1.ft_handle "); 228 assert_zero(r); 229 230 FT_HANDLE c_ft; 231 r = toku_open_ft_handle("bar1.ft_handle", 0, &c_ft, NODESIZE, NODESIZE/2, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun); 232 assert(r==0); 233 234 // 235 // now pin the root, verify that we have a message in there, and that it is clean 236 // 237 bfe.create_for_full_read(c_ft->ft); 238 toku_pin_ftnode( 239 c_ft->ft, 240 node_root, 241 toku_cachetable_hash(c_ft->ft->cf, node_root), 242 &bfe, 243 PL_WRITE_EXPENSIVE, 244 &node, 245 true 246 ); 247 assert(node->height == 1); 248 assert(!node->dirty()); 249 assert(node->n_children == 1); 250 if (after_child_pin) { 251 assert(toku_bnc_nbytesinbuf(BNC(node, 0)) == 0); 252 } 253 else { 254 assert(toku_bnc_nbytesinbuf(BNC(node, 0)) > 0); 255 } 256 toku_unpin_ftnode(c_ft->ft, node); 257 258 toku_pin_ftnode( 259 c_ft->ft, 260 node_leaf, 261 toku_cachetable_hash(c_ft->ft->cf, node_root), 262 &bfe, 263 PL_WRITE_EXPENSIVE, 264 &node, 265 true 266 ); 267 assert(node->height == 0); 268 assert(!node->dirty()); 269 assert(node->n_children == 1); 270 if (after_child_pin) { 271 assert(BLB_NBYTESINDATA(node,0) > 0); 272 } 273 else { 274 assert(BLB_NBYTESINDATA(node,0) == 0); 275 } 276 toku_unpin_ftnode(c_ft->ft, node); 277 278 struct check_pair pair1 = {2, "a", 0, NULL, 0}; 279 DBT k; 280 r = toku_ft_lookup(c_ft, toku_fill_dbt(&k, "a", 2), lookup_checkf, &pair1); 281 assert(r==0); 282 283 284 r = toku_close_ft_handle_nolsn(t, 0); assert(r==0); 285 r = toku_close_ft_handle_nolsn(c_ft, 0); assert(r==0); 286 toku_cachetable_close(&ct); 287 } 288 289 int 290 test_main (int argc __attribute__((__unused__)), const char *argv[] __attribute__((__unused__))) { 291 default_parse_args(argc, argv); 292 doit(false); 293 doit(true); 294 return 0; 295 } 296