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