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 // Verify that a message with an old msn is ignored
40 // by toku_apply_msg_to_leaf()
41 //
42 // method:
43 //  - inject valid message, verify that new value is in row
44 //  - inject message with same msn and new value, verify that original value is still in key  (verify msg.msn == node.max_msn is rejected)
45 //  - inject valid message with new value2, verify that row has new value2
46 //  - inject message with old msn, verify that row still has value2   (verify msg.msn < node.max_msn is rejected)
47 
48 
49 // TODO:
50 //  - verify that no work is done by messages that should be ignored (via workdone arg to ft_leaf_put_msg())
51 //  - maybe get counter of messages ignored for old msn (once the counter is implemented in ft-ops.c)
52 
53 #include "ft-internal.h"
54 #include <ft-cachetable-wrappers.h>
55 
56 #include "test.h"
57 
58 static FTNODE
make_node(FT_HANDLE ft,int height)59 make_node(FT_HANDLE ft, int height) {
60     FTNODE node = NULL;
61     int n_children = (height == 0) ? 1 : 0;
62     toku_create_new_ftnode(ft, &node, height, n_children);
63     if (n_children) BP_STATE(node,0) = PT_AVAIL;
64     return node;
65 }
66 
67 static void
append_leaf(FT_HANDLE ft,FTNODE leafnode,void * key,uint32_t keylen,void * val,uint32_t vallen)68 append_leaf(FT_HANDLE ft, FTNODE leafnode, void *key, uint32_t keylen, void *val, uint32_t vallen) {
69     assert(leafnode->height == 0);
70 
71     DBT thekey; toku_fill_dbt(&thekey, key, keylen);
72     DBT theval; toku_fill_dbt(&theval, val, vallen);
73     DBT badval; toku_fill_dbt(&badval, (char*)val+1, vallen);
74     DBT val2;   toku_fill_dbt(&val2, (char*)val+2, vallen);
75 
76     struct check_pair pair  = {keylen, key, vallen, val, 0};
77     struct check_pair pair2 = {keylen, key, vallen, (char*)val+2, 0};
78 
79     // apply an insert to the leaf node
80     MSN msn = next_dummymsn();
81     ft->ft->h->max_msn_in_ft = msn;
82     ft_msg msg(&thekey, &theval, FT_INSERT, msn, toku_xids_get_root_xids());
83     txn_gc_info gc_info(nullptr, TXNID_NONE, TXNID_NONE, false);
84 
85     toku_ft_leaf_apply_msg(
86         ft->ft->cmp,
87         ft->ft->update_fun,
88         leafnode,
89         -1,
90         msg,
91         &gc_info,
92         nullptr,
93         nullptr,
94         nullptr);
95     {
96         int r = toku_ft_lookup(ft, &thekey, lookup_checkf, &pair);
97         assert(r==0);
98         assert(pair.call_count==1);
99     }
100 
101     ft_msg badmsg(&thekey, &badval, FT_INSERT, msn, toku_xids_get_root_xids());
102     toku_ft_leaf_apply_msg(
103         ft->ft->cmp,
104         ft->ft->update_fun,
105         leafnode,
106         -1,
107         badmsg,
108         &gc_info,
109         nullptr,
110         nullptr,
111         nullptr);
112 
113     // message should be rejected for duplicate msn, row should still have original val
114     {
115 	      int r = toku_ft_lookup(ft, &thekey, lookup_checkf, &pair);
116 	      assert(r==0);
117 	      assert(pair.call_count==2);
118     }
119 
120     // now verify that message with proper msn gets through
121     msn = next_dummymsn();
122     ft->ft->h->max_msn_in_ft = msn;
123     ft_msg msg2(&thekey, &val2,  FT_INSERT, msn, toku_xids_get_root_xids());
124     toku_ft_leaf_apply_msg(
125         ft->ft->cmp,
126         ft->ft->update_fun,
127         leafnode,
128         -1,
129         msg2,
130         &gc_info,
131         nullptr,
132         nullptr,
133         nullptr);
134 
135     // message should be accepted, val should have new value
136     {
137 	      int r = toku_ft_lookup(ft, &thekey, lookup_checkf, &pair2);
138 	      assert(r==0);
139 	      assert(pair2.call_count==1);
140     }
141 
142     // now verify that message with lesser (older) msn is rejected
143     msn.msn = msn.msn - 10;
144     ft_msg msg3(&thekey, &badval, FT_INSERT, msn, toku_xids_get_root_xids());
145     toku_ft_leaf_apply_msg(
146         ft->ft->cmp,
147         ft->ft->update_fun,
148         leafnode,
149         -1,
150         msg3,
151         &gc_info,
152         nullptr,
153         nullptr,
154         nullptr);
155 
156     // message should be rejected, val should still have value in pair2
157     {
158 	      int r = toku_ft_lookup(ft, &thekey, lookup_checkf, &pair2);
159 	      assert(r==0);
160 	      assert(pair2.call_count==2);
161     }
162 
163     // don't forget to dirty the node
164     leafnode->set_dirty();
165 }
166 
167 static void
populate_leaf(FT_HANDLE ft,FTNODE leafnode,int k,int v)168 populate_leaf(FT_HANDLE ft, FTNODE leafnode, int k, int v) {
169     char vbuf[32]; // store v in a buffer large enough to dereference unaligned int's
170     memset(vbuf, 0, sizeof vbuf);
171     memcpy(vbuf, &v, sizeof v);
172     append_leaf(ft, leafnode, &k, sizeof k, vbuf, sizeof v);
173 }
174 
175 static void
test_msnfilter(int do_verify)176 test_msnfilter(int do_verify) {
177     int r;
178 
179     // cleanup
180     const char *fname = TOKU_TEST_FILENAME;
181     r = unlink(fname);
182     if (r != 0) {
183         assert(r == -1);
184         assert(get_error_errno() == ENOENT);
185     }
186 
187     // create a cachetable
188     CACHETABLE ct = NULL;
189     toku_cachetable_create(&ct, 0, ZERO_LSN, nullptr);
190 
191     // create the ft
192     TOKUTXN null_txn = NULL;
193     FT_HANDLE ft = NULL;
194     r = toku_open_ft_handle(fname, 1, &ft, 1024, 256, TOKU_DEFAULT_COMPRESSION_METHOD, ct, null_txn, toku_builtin_compare_fun);
195     assert(r == 0);
196 
197     FTNODE newroot = make_node(ft, 0);
198 
199     // set the new root to point to the new tree
200     toku_ft_set_new_root_blocknum(ft->ft, newroot->blocknum);
201 
202     // KLUDGE: Unpin the new root so toku_ft_lookup() can pin it.  (Pin lock is no longer a recursive
203     //         mutex.)  Just leaving it unpinned for this test program works  because it is the only
204     //         node in the cachetable and won't be evicted.  The right solution would be to lock the
205     //         node and unlock it again before and after each message injection, but that requires more
206     //         work than it's worth (setting up dummy callbacks, etc.)
207     //
208     toku_unpin_ftnode(ft->ft, newroot);
209 
210     populate_leaf(ft, newroot, htonl(2), 1);
211 
212     if (do_verify) {
213         r = toku_verify_ft(ft);
214         assert(r == 0);
215     }
216 
217     // flush to the file system
218     r = toku_close_ft_handle_nolsn(ft, 0);
219     assert(r == 0);
220 
221     // shutdown the cachetable
222     toku_cachetable_close(&ct);
223 }
224 
225 static int
usage(void)226 usage(void) {
227     return 1;
228 }
229 
230 int
test_main(int argc,const char * argv[])231 test_main (int argc , const char *argv[]) {
232     int do_verify = 1;
233     initialize_dummymsn();
234     for (int i = 1; i < argc; i++) {
235         const char *arg = argv[i];
236         if (strcmp(arg, "-v") == 0) {
237             verbose++;
238             continue;
239         }
240         if (strcmp(arg, "-q") == 0) {
241             verbose = 0;
242             continue;
243         }
244         if (strcmp(arg, "--verify") == 0 && i+1 < argc) {
245             do_verify = atoi(argv[++i]);
246             continue;
247         }
248         return usage();
249     }
250     test_msnfilter(do_verify);
251     return 0;
252 }
253