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 
41 bool flush_may_occur;
42 long expected_bytes_to_free;
43 
44 static void
flush(CACHEFILE f,int UU (fd),CACHEKEY k,void * UU (v),void ** UU (dd),void * e,PAIR_ATTR s,PAIR_ATTR * new_size,bool w,bool keep,bool c,bool UU (is_clone))45 flush (CACHEFILE f __attribute__((__unused__)),
46        int UU(fd),
47        CACHEKEY k  __attribute__((__unused__)),
48        void* UU(v),
49        void** UU(dd),
50        void *e     __attribute__((__unused__)),
51        PAIR_ATTR s      __attribute__((__unused__)),
52        PAIR_ATTR* new_size      __attribute__((__unused__)),
53        bool w      __attribute__((__unused__)),
54        bool keep,
55        bool c      __attribute__((__unused__)),
56         bool UU(is_clone)
57        ) {
58     assert(flush_may_occur);
59     if (!keep) {
60         //int* foo = v;
61         //assert(*foo == 3);
62         toku_free(v);
63     }
64 }
65 
66 static int
fetch(CACHEFILE f,PAIR UU (p),int UU (fd),CACHEKEY k,uint32_t fullhash,void ** value,void ** UU (dd),PAIR_ATTR * sizep,int * dirtyp,void * extraargs)67 fetch (CACHEFILE f        __attribute__((__unused__)),
68        PAIR UU(p),
69        int UU(fd),
70        CACHEKEY k         __attribute__((__unused__)),
71        uint32_t fullhash __attribute__((__unused__)),
72        void **value       __attribute__((__unused__)),
73        void** UU(dd),
74        PAIR_ATTR *sizep        __attribute__((__unused__)),
75        int  *dirtyp,
76        void *extraargs    __attribute__((__unused__))
77        ) {
78     *dirtyp = 0;
79     int* XMALLOC(foo);
80     *value = foo;
81     *sizep = make_pair_attr(4);
82     *foo = 4;
83     return 0;
84 }
85 
86 static void
other_flush(CACHEFILE f,int UU (fd),CACHEKEY k,void * v,void ** UU (dd),void * e,PAIR_ATTR s,PAIR_ATTR * new_size,bool w,bool keep,bool c,bool UU (is_clone))87 other_flush (CACHEFILE f __attribute__((__unused__)),
88        int UU(fd),
89        CACHEKEY k  __attribute__((__unused__)),
90        void *v     __attribute__((__unused__)),
91 	     void** UU(dd),
92        void *e     __attribute__((__unused__)),
93        PAIR_ATTR s      __attribute__((__unused__)),
94        PAIR_ATTR* new_size      __attribute__((__unused__)),
95        bool w      __attribute__((__unused__)),
96        bool keep   __attribute__((__unused__)),
97        bool c      __attribute__((__unused__)),
98         bool UU(is_clone)
99        ) {
100 }
101 
102 static void
pe_est_callback(void * UU (ftnode_pv),void * UU (dd),long * bytes_freed_estimate,enum partial_eviction_cost * cost,void * UU (write_extraargs))103 pe_est_callback(
104     void* UU(ftnode_pv),
105     void* UU(dd),
106     long* bytes_freed_estimate,
107     enum partial_eviction_cost *cost,
108     void* UU(write_extraargs)
109     )
110 {
111     *bytes_freed_estimate = 1000;
112     *cost = PE_EXPENSIVE;
113 }
114 
115 static int
pe_callback(void * ftnode_pv,PAIR_ATTR UU (bytes_to_free),void * extraargs,void (* finalize)(PAIR_ATTR bytes_freed,void * extra),void * finalize_extra)116 pe_callback (
117     void *ftnode_pv,
118     PAIR_ATTR UU(bytes_to_free),
119     void* extraargs __attribute__((__unused__)),
120     void (*finalize)(PAIR_ATTR bytes_freed, void *extra),
121     void *finalize_extra
122     )
123 {
124     usleep(1*1024*1024);
125     if (verbose) printf("calling pe_callback\n");
126     expected_bytes_to_free--;
127     int* CAST_FROM_VOIDP(foo, ftnode_pv);
128     int blah = *foo;
129     *foo = blah-1;
130     finalize(make_pair_attr(bytes_to_free.size-1), finalize_extra);
131     return 0;
132 }
133 
134 static int
other_pe_callback(void * ftnode_pv,PAIR_ATTR bytes_to_free,void * extraargs,void (* finalize)(PAIR_ATTR bytes_freed,void * extra),void * finalize_extra)135 other_pe_callback (
136     void *ftnode_pv __attribute__((__unused__)),
137     PAIR_ATTR bytes_to_free __attribute__((__unused__)),
138     void* extraargs __attribute__((__unused__)),
139     void (*finalize)(PAIR_ATTR bytes_freed, void *extra),
140     void *finalize_extra
141     )
142 {
143     finalize(bytes_to_free, finalize_extra);
144     return 0;
145 }
146 
147 static void
cachetable_test(void)148 cachetable_test (void) {
149     const int test_limit = 20;
150     int r;
151     CACHETABLE ct;
152     toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
153     evictor_test_helpers::set_hysteresis_limits(&ct->ev, test_limit, 100*test_limit);
154     evictor_test_helpers::disable_ev_thread(&ct->ev);
155     const char *fname1 = TOKU_TEST_FILENAME;
156     unlink(fname1);
157     CACHEFILE f1;
158     r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
159 
160     void* v1;
161     void* v2;
162     flush_may_occur = false;
163     for (int i = 0; i < 100000; i++) {
164       CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
165       wc.flush_callback = flush;
166       wc.pe_est_callback = pe_est_callback;
167       wc.pe_callback = pe_callback;
168       r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, fetch, def_pf_req_callback, def_pf_callback, true, NULL);
169       r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(4));
170     }
171     for (int i = 0; i < 8; i++) {
172       CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
173       wc.flush_callback = flush;
174       wc.pe_est_callback = pe_est_callback;
175       wc.pe_callback = pe_callback;
176       r = toku_cachetable_get_and_pin(f1, make_blocknum(2), 2, &v2, wc, fetch, def_pf_req_callback, def_pf_callback, true, NULL);
177       r = toku_test_cachetable_unpin(f1, make_blocknum(2), 2, CACHETABLE_CLEAN, make_pair_attr(4));
178     }
179     for (int i = 0; i < 4; i++) {
180       CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
181       wc.flush_callback = flush;
182       wc.pe_est_callback = pe_est_callback;
183       wc.pe_callback = pe_callback;
184       r = toku_cachetable_get_and_pin(f1, make_blocknum(3), 3, &v2, wc, fetch, def_pf_req_callback, def_pf_callback, true, NULL);
185       r = toku_test_cachetable_unpin(f1, make_blocknum(3), 3, CACHETABLE_CLEAN, make_pair_attr(4));
186     }
187     for (int i = 0; i < 2; i++) {
188       CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
189       wc.flush_callback = flush;
190       wc.pe_est_callback = pe_est_callback;
191       wc.pe_callback = pe_callback;
192       r = toku_cachetable_get_and_pin(f1, make_blocknum(4), 4, &v2, wc, fetch, def_pf_req_callback, def_pf_callback, true, NULL);
193       r = toku_test_cachetable_unpin(f1, make_blocknum(4), 4, CACHETABLE_CLEAN, make_pair_attr(4));
194     }
195     flush_may_occur = false;
196     expected_bytes_to_free = 4;
197     CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
198     wc.flush_callback = other_flush;
199     wc.pe_est_callback = pe_est_callback;
200     wc.pe_callback = other_pe_callback;
201     toku_cachetable_put(f1, make_blocknum(5), 5, NULL, make_pair_attr(4), wc, put_callback_nop);
202     flush_may_occur = true;
203     r = toku_test_cachetable_unpin(f1, make_blocknum(5), 5, CACHETABLE_CLEAN, make_pair_attr(8));
204     ct->ev.signal_eviction_thread();
205 
206     // we are testing that having a wildly different estimate than
207     // what actually gets freed is ok
208     // in the callbacks, we estimate that 1000 bytes gets freed
209     // whereas in reality, only 1 byte will be freed
210     // we measure that only 1 byte gets freed (which leaves cachetable
211     // oversubscrubed)
212     usleep(3*1024*1024);
213     assert(expected_bytes_to_free == 3);
214 
215 
216     toku_cachefile_close(&f1, false, ZERO_LSN);
217     toku_cachetable_close(&ct);
218 }
219 
220 int
test_main(int argc,const char * argv[])221 test_main(int argc, const char *argv[]) {
222     default_parse_args(argc, argv);
223     cachetable_test();
224     return 0;
225 }
226