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 #include "cachetable-test.h"
41
42 bool close_called;
43 bool free_called;
44
close_usr(CACHEFILE UU (cf),int UU (i),void * UU (p),bool UU (b),LSN UU (lsn))45 static void close_usr(CACHEFILE UU(cf), int UU(i), void* UU(p), bool UU(b), LSN UU(lsn)) {
46 close_called = true;
47 }
free_usr(CACHEFILE UU (cf),void * UU (p))48 static void free_usr(CACHEFILE UU(cf), void* UU(p)) {
49 free_called = true;
50 }
51
set_cf_userdata(CACHEFILE f1)52 static void set_cf_userdata(CACHEFILE f1) {
53 toku_cachefile_set_userdata(
54 f1,
55 NULL,
56 &dummy_log_fassociate,
57 &close_usr,
58 &free_usr,
59 &dummy_chckpnt_usr,
60 &dummy_begin,
61 &dummy_end,
62 &dummy_note_pin,
63 &dummy_note_unpin
64 );
65 }
66
67 bool keep_me;
68 bool write_me;
69 bool flush_called;
UU()70 static UU() void
71 flush (CACHEFILE f __attribute__((__unused__)),
72 int UU(fd),
73 CACHEKEY k __attribute__((__unused__)),
74 void *v __attribute__((__unused__)),
75 void **dd __attribute__((__unused__)),
76 void *e __attribute__((__unused__)),
77 PAIR_ATTR s __attribute__((__unused__)),
78 PAIR_ATTR* new_size __attribute__((__unused__)),
79 bool w __attribute__((__unused__)),
80 bool keep __attribute__((__unused__)),
81 bool c __attribute__((__unused__)),
82 bool UU(is_clone)
83 )
84 {
85 flush_called = true;
86 if (!keep) keep_me = keep;
87 if (w) write_me = w;
88 }
89
90
91 static void
simple_test(bool unlink_on_close)92 simple_test(bool unlink_on_close) {
93 const int test_limit = 12;
94 int r;
95 CACHETABLE ct;
96 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
97 const char *fname1 = TOKU_TEST_FILENAME;
98 unlink(fname1);
99 CACHEFILE f1;
100 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
101 set_cf_userdata(f1);
102
103 // test that if we just open a cachefile and then close it (have no pairs active),
104 // then it does not get cached
105 close_called = false;
106 free_called = false;
107 toku_cachefile_close(&f1, false, ZERO_LSN);
108 assert(close_called);
109 assert(free_called);
110
111 // now reopen the cachefile
112 f1 = NULL;
113 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
114 set_cf_userdata(f1);
115 void* v1;
116 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
117 wc.flush_callback = flush;
118 r = toku_cachetable_get_and_pin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
119 r = toku_test_cachetable_unpin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), CACHETABLE_DIRTY, make_pair_attr(8));
120 toku_cachetable_verify(ct);
121 if (unlink_on_close) {
122 toku_cachefile_unlink_on_close(f1);
123 }
124 close_called = false;
125 free_called = false;
126 keep_me = true;
127 write_me = false;
128 flush_called = false;
129 // because we ought to have one pair in the cachetable for this cf,
130 // close should cache the cf and not free it (unless we unlink on close)
131 // also, make sure we wrote dirty pair, but we did NOT free PAIR unless
132 // unlink_on_close was set
133 toku_cachefile_close(&f1, false, ZERO_LSN);
134 CACHETABLE_STATUS_S stats;
135 toku_cachetable_get_status(ct, &stats);
136 assert(flush_called);
137 assert(close_called);
138 assert(write_me);
139 if (unlink_on_close) {
140 assert(free_called);
141 assert(!keep_me);
142 // pair should NOT still be accounted for
143 assert(stats.status[CACHETABLE_STATUS_S::CT_SIZE_CURRENT].value.num == 0);
144 }
145 else {
146 assert(keep_me);
147 assert(!free_called);
148 // pair should still be accounted for
149 assert(stats.status[CACHETABLE_STATUS_S::CT_SIZE_CURRENT].value.num == 8);
150 }
151 toku_cachetable_close(&ct);
152 if (!unlink_on_close) {
153 assert(free_called);
154 assert(!keep_me);
155 }
156 }
157
158 // test to verify that a PAIR stays in cache
159 // after the cachefile undergoes a close and reopen
test_pair_stays_in_cache(enum cachetable_dirty dirty)160 static void test_pair_stays_in_cache(enum cachetable_dirty dirty) {
161 const int test_limit = 12;
162 int r;
163 CACHETABLE ct;
164 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
165 const char *fname1 = TOKU_TEST_FILENAME;
166 unlink(fname1);
167 CACHEFILE f1;
168
169 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
170 void* v1;
171 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
172 r = toku_cachetable_get_and_pin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
173 r = toku_test_cachetable_unpin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), dirty, make_pair_attr(8));
174 toku_cachefile_close(&f1, false, ZERO_LSN);
175 // now reopen the cachefile
176 f1 = NULL;
177 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
178 // do a maybe_get_and_pin and verify that it succeeds
179 // therefore proving that the PAIR was cached
180 // and could be successfully retrieved
181 r = toku_cachetable_maybe_get_and_pin_clean(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), PL_WRITE_EXPENSIVE, &v1);
182 assert(r == 0);
183 r = toku_test_cachetable_unpin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), CACHETABLE_DIRTY, make_pair_attr(8));
184 toku_cachefile_close(&f1, false, ZERO_LSN);
185
186 toku_cachetable_close(&ct);
187 }
188
test_multiple_cachefiles(bool use_same_hash)189 static void test_multiple_cachefiles(bool use_same_hash) {
190 for (int iter = 0; iter < 3; iter++) {
191 const int test_limit = 1000;
192 int r;
193 CACHETABLE ct;
194 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
195
196 char fname1[strlen(TOKU_TEST_FILENAME) + sizeof("_1")];
197 strcpy(fname1, TOKU_TEST_FILENAME);
198 strcat(fname1, "_1");
199 char fname2[strlen(TOKU_TEST_FILENAME) + sizeof("_2")];
200 strcpy(fname2, TOKU_TEST_FILENAME);
201 strcat(fname2, "_2");
202 char fname3[strlen(TOKU_TEST_FILENAME) + sizeof("_3")];
203 strcpy(fname3, TOKU_TEST_FILENAME);
204 strcat(fname3, "_3");
205
206 unlink(fname1);
207 unlink(fname2);
208 unlink(fname3);
209 CACHEFILE f1;
210 CACHEFILE f2;
211 CACHEFILE f3;
212
213 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
214 r = toku_cachetable_openf(&f2, ct, fname2, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
215 r = toku_cachetable_openf(&f3, ct, fname3, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
216
217 void* v1;
218 void* v2;
219 void* v3;
220
221 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
222 for (int j = 0; j < 3; j++) {
223 uint32_t hash = use_same_hash ? 1 : toku_cachetable_hash(f1, make_blocknum(j));
224 r = toku_cachetable_get_and_pin(f1, make_blocknum(j), hash, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
225 r = toku_test_cachetable_unpin(f1, make_blocknum(j), hash, CACHETABLE_CLEAN, make_pair_attr(8));
226 }
227
228 for (int j = 0; j < 3; j++) {
229 uint32_t hash = use_same_hash ? 1 : toku_cachetable_hash(f2, make_blocknum(j));
230 r = toku_cachetable_get_and_pin(f2, make_blocknum(j), hash, &v2, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
231 r = toku_test_cachetable_unpin(f2, make_blocknum(j), hash, CACHETABLE_CLEAN, make_pair_attr(8));
232 }
233
234 for (int j = 0; j < 3; j++) {
235 uint32_t hash = use_same_hash ? 1 : toku_cachetable_hash(f3, make_blocknum(j));
236 r = toku_cachetable_get_and_pin(f3, make_blocknum(j), hash, &v3, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
237 r = toku_test_cachetable_unpin(f3, make_blocknum(j), hash, CACHETABLE_CLEAN, make_pair_attr(8));
238 }
239
240
241 toku_cachefile_close(&f1, false, ZERO_LSN);
242 toku_cachefile_close(&f2, false, ZERO_LSN);
243 toku_cachefile_close(&f3, false, ZERO_LSN);
244
245 char* fname_to_open = NULL;
246 if (iter == 0) {
247 fname_to_open = fname1;
248 }
249 else if (iter == 1) {
250 fname_to_open = fname2;
251 }
252 else if (iter == 2) {
253 fname_to_open = fname3;
254 }
255
256 // now reopen the cachefile
257 f1 = NULL;
258 r = toku_cachetable_openf(&f1, ct, fname_to_open, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
259 // do a maybe_get_and_pin and verify that it succeeds
260 // therefore proving that the PAIR was cached
261 // and could be successfully retrieved
262 for (int j = 0; j < 3; j++) {
263 uint32_t hash = use_same_hash ? 1 : toku_cachetable_hash(f1, make_blocknum(j));
264 r = toku_cachetable_maybe_get_and_pin_clean(f1, make_blocknum(j), hash, PL_WRITE_EXPENSIVE, &v1);
265 assert(r == 0);
266 r = toku_test_cachetable_unpin(f1, make_blocknum(j), hash, CACHETABLE_CLEAN, make_pair_attr(8));
267 }
268 toku_cachefile_close(&f1, false, ZERO_LSN);
269
270 toku_cachetable_close(&ct);
271 }
272 }
273
274 // test that the evictor works properly with closed cachefiles
test_evictor(void)275 static void test_evictor(void) {
276 const int test_limit = 12;
277 int r;
278 CACHETABLE ct;
279 toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
280
281 char fname1[strlen(TOKU_TEST_FILENAME) + sizeof("_1")];
282 strcpy(fname1, TOKU_TEST_FILENAME);
283 strcat(fname1, "_1");
284 char fname2[strlen(TOKU_TEST_FILENAME) + sizeof("_2")];
285 strcpy(fname2, TOKU_TEST_FILENAME);
286 strcat(fname2, "_2");
287
288 unlink(fname1);
289 unlink(fname2);
290 CACHEFILE f1;
291 CACHEFILE f2;
292
293 r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
294 set_cf_userdata(f1);
295 r = toku_cachetable_openf(&f2, ct, fname2, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
296 void* v1;
297 CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
298 r = toku_cachetable_get_and_pin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
299 r = toku_test_cachetable_unpin(f1, make_blocknum(1), toku_cachetable_hash(f1, make_blocknum(1)), CACHETABLE_CLEAN, make_pair_attr(8));
300 close_called = false;
301 free_called = false;
302 toku_cachefile_close(&f1, false, ZERO_LSN);
303 assert(close_called);
304 assert(!free_called);
305
306 // at this point, we should f1, along with one PAIR, stale in the cachetable
307 // now let's pin another node, and ensure that it causes an eviction and free of f1
308 r = toku_cachetable_get_and_pin(f2, make_blocknum(1), toku_cachetable_hash(f2, make_blocknum(1)), &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
309 r = toku_test_cachetable_unpin(f2, make_blocknum(1), toku_cachetable_hash(f2, make_blocknum(1)), CACHETABLE_CLEAN, make_pair_attr(8));
310 // now sleep for 2 seconds, and check to see if f1 has been closed
311 sleep(2);
312 assert(free_called);
313
314 toku_cachefile_close(&f2, false, ZERO_LSN);
315
316 toku_cachetable_close(&ct);
317 }
318
319 int
test_main(int argc,const char * argv[])320 test_main(int argc, const char *argv[]) {
321 default_parse_args(argc, argv);
322 test_evictor();
323 test_multiple_cachefiles(false);
324 test_multiple_cachefiles(true);
325 simple_test(false);
326 simple_test(true);
327 test_pair_stays_in_cache(CACHETABLE_DIRTY);
328 test_pair_stays_in_cache(CACHETABLE_CLEAN);
329 return 0;
330 }
331