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