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