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/cachetable-internal.h"
41
42 class evictor_unit_test {
43 public:
44 evictor m_ev;
45 pair_list m_pl;
46 cachefile_list m_cf_list;
47 KIBBUTZ m_kb;
48 void init();
49 void destroy();
50 void run_test();
51 void verify_ev_init(long limit);
52 void verify_ev_destroy();
53 void verify_ev_counts();
54 void verify_ev_m_size_reserved();
55 void verify_ev_handling_cache_pressure();
56
57 // function to disable the eviction thread from waking up every second
58 void disable_ev_thread();
59 };
60
61 // initialize this class to run tests
init()62 void evictor_unit_test::init() {
63 ZERO_STRUCT(m_pl);
64 ZERO_STRUCT(m_cf_list);
65 m_pl.init();
66 m_cf_list.init();
67 m_kb = NULL;
68 int r = toku_kibbutz_create(1, &m_kb);
69 assert(r == 0);
70 }
71
72 // destroy class after tests have run
destroy()73 void evictor_unit_test::destroy() {
74 m_pl.destroy();
75 m_cf_list.destroy();
76 toku_kibbutz_destroy(m_kb);
77 }
78
79 // test that verifies evictor.init properly worked
verify_ev_init(long limit)80 void evictor_unit_test::verify_ev_init(long limit) {
81 assert(m_ev.m_kibbutz == m_kb);
82 assert(m_ev.m_pl == &m_pl);
83 assert(m_ev.m_cf_list == &m_cf_list);
84 assert(m_ev.m_low_size_watermark == limit);
85 assert(m_ev.m_num_sleepers == 0);
86 assert(m_ev.m_run_thread == true);
87 assert(m_ev.m_size_current == 0);
88 assert(read_partitioned_counter(m_ev.m_size_leaf) == 0);
89 assert(read_partitioned_counter(m_ev.m_size_nonleaf) == 0);
90 assert(read_partitioned_counter(m_ev.m_size_rollback) == 0);
91 assert(read_partitioned_counter(m_ev.m_size_cachepressure) == 0);
92 assert(m_ev.m_size_evicting == 0);
93 // this comes from definition of unreservable_memory in cachetable.cc
94 assert(m_ev.m_size_reserved == (limit/4));
95 }
96
97 // test that verifies evictor.destroy properly worked
verify_ev_destroy()98 void evictor_unit_test::verify_ev_destroy() {
99 assert(m_ev.m_num_sleepers == 0);
100 assert(m_ev.m_run_thread == false);
101 }
102
disable_ev_thread()103 void evictor_unit_test::disable_ev_thread() {
104 toku_mutex_lock(&m_ev.m_ev_thread_lock);
105 m_ev.m_period_in_seconds = 0;
106 // signal eviction thread so that it wakes up
107 // and then sleeps indefinitely
108 m_ev.signal_eviction_thread();
109 toku_mutex_unlock(&m_ev.m_ev_thread_lock);
110 // sleep for one second to ensure eviction thread picks up new period
111 usleep(1*1024*1024);
112 }
113
114 // test that verifies that counts, such as m_size_current
115 // are accurately maintained
verify_ev_counts()116 void evictor_unit_test::verify_ev_counts() {
117 long limit = 10;
118 long expected_m_size_reserved = limit/4;
119 ZERO_STRUCT(m_ev);
120 m_ev.init(limit, &m_pl, &m_cf_list, m_kb, 0);
121 this->verify_ev_init(limit);
122
123 m_ev.add_to_size_current(1);
124 assert(m_ev.m_size_current == 1);
125 assert(m_ev.m_size_reserved == expected_m_size_reserved);
126 assert(read_partitioned_counter(m_ev.m_size_leaf) == 0);
127 assert(read_partitioned_counter(m_ev.m_size_nonleaf) == 0);
128 assert(read_partitioned_counter(m_ev.m_size_rollback) == 0);
129 assert(read_partitioned_counter(m_ev.m_size_cachepressure) == 0);
130 assert(m_ev.m_size_evicting == 0);
131
132 m_ev.add_to_size_current(3);
133 assert(m_ev.m_size_current == 4);
134
135 m_ev.remove_from_size_current(4);
136 assert(m_ev.m_size_current == 0);
137 assert(m_ev.m_size_reserved == expected_m_size_reserved);
138
139 PAIR_ATTR attr = {
140 .size = 1,
141 .nonleaf_size = 2,
142 .leaf_size = 3,
143 .rollback_size = 4,
144 .cache_pressure_size = 5,
145 .is_valid = true
146 };
147
148 m_ev.add_pair_attr(attr);
149 assert(m_ev.m_size_current == 1);
150 assert(read_partitioned_counter(m_ev.m_size_nonleaf) == 2);
151 assert(read_partitioned_counter(m_ev.m_size_leaf) == 3);
152 assert(read_partitioned_counter(m_ev.m_size_rollback) == 4);
153 assert(read_partitioned_counter(m_ev.m_size_cachepressure) == 5);
154 m_ev.remove_pair_attr(attr);
155 assert(m_ev.m_size_current == 0);
156 assert(read_partitioned_counter(m_ev.m_size_leaf) == 0);
157 assert(read_partitioned_counter(m_ev.m_size_nonleaf) == 0);
158 assert(read_partitioned_counter(m_ev.m_size_rollback) == 0);
159 assert(read_partitioned_counter(m_ev.m_size_cachepressure) == 0);
160
161 PAIR_ATTR other_attr = {
162 .size = 2,
163 .nonleaf_size = 3,
164 .leaf_size = 4,
165 .rollback_size = 5,
166 .cache_pressure_size = 6,
167 .is_valid = true
168 };
169 m_ev.change_pair_attr(attr, other_attr);
170 assert(m_ev.m_size_current == 1);
171 assert(read_partitioned_counter(m_ev.m_size_leaf) == 1);
172 assert(read_partitioned_counter(m_ev.m_size_nonleaf) == 1);
173 assert(read_partitioned_counter(m_ev.m_size_rollback) == 1);
174 assert(read_partitioned_counter(m_ev.m_size_cachepressure) == 1);
175
176 m_ev.m_size_current = 0;
177 m_ev.destroy();
178 this->verify_ev_destroy();
179 }
180
181 // test to verify the functionality surrounding m_size_reserved
verify_ev_m_size_reserved()182 void evictor_unit_test::verify_ev_m_size_reserved() {
183 long limit = 400;
184 long expected_m_size_reserved = 100; //limit/4
185 ZERO_STRUCT(m_ev);
186 m_ev.init(limit, &m_pl, &m_cf_list, m_kb, 0);
187 this->verify_ev_init(limit);
188 assert(m_ev.m_size_reserved == expected_m_size_reserved);
189 m_ev.m_num_eviction_thread_runs = 0;
190 m_ev.reserve_memory(0.5, 0);
191 assert(m_ev.m_size_reserved == 100+150); //100 original, 150 from last call
192 assert(m_ev.m_size_current == 150);
193 assert(m_ev.m_size_evicting == 0);
194 usleep(1*1024*1024); // sleep to give eviction thread a chance to wake up
195 assert(m_ev.m_num_eviction_thread_runs > 0);
196
197 m_ev.m_size_current = 0;
198 m_ev.destroy();
199 this->verify_ev_destroy();
200 }
201
202 // test to verify functionality of handling cache pressure,
203 // ensures that wait_for_cache_pressure_to_subside works correctly,
204 // that decrease_m_size_evicting works correctly, and the logic for when to wake
205 // threads up works correctly
verify_ev_handling_cache_pressure()206 void evictor_unit_test::verify_ev_handling_cache_pressure() {
207 long limit = 400;
208 ZERO_STRUCT(m_ev);
209 m_ev.init(limit, &m_pl, &m_cf_list, m_kb, 0);
210 this->verify_ev_init(limit);
211 m_ev.m_low_size_watermark = 400;
212 m_ev.m_low_size_hysteresis = 400;
213 m_ev.m_high_size_hysteresis = 500;
214 m_ev.m_high_size_watermark = 500;
215 m_ev.m_size_current = 500;
216
217 m_ev.m_num_eviction_thread_runs = 0;
218
219 // test that waiting for cache pressure wakes eviction thread
220 assert(m_ev.m_num_sleepers == 0);
221 m_ev.wait_for_cache_pressure_to_subside();
222 assert(m_ev.m_num_eviction_thread_runs == 1);
223 assert(m_ev.m_num_sleepers == 0);
224
225 m_ev.m_num_eviction_thread_runs = 0;
226 m_ev.m_size_evicting = 101;
227 m_ev.decrease_size_evicting(101);
228 usleep(1*1024*1024);
229 // should not have been signaled because we have no sleepers
230 assert(m_ev.m_num_eviction_thread_runs == 0);
231
232 m_ev.m_num_eviction_thread_runs = 0;
233 m_ev.m_size_evicting = 101;
234 m_ev.m_num_sleepers = 1;
235 m_ev.decrease_size_evicting(2);
236 usleep(1*1024*1024);
237 // should have been signaled because we have sleepers
238 assert(m_ev.m_num_eviction_thread_runs == 1);
239 assert(m_ev.m_num_sleepers == 1); // make sure fake sleeper did not go away
240
241 m_ev.m_num_eviction_thread_runs = 0;
242 m_ev.m_size_evicting = 102;
243 m_ev.m_num_sleepers = 1;
244 m_ev.decrease_size_evicting(1);
245 usleep(1*1024*1024);
246 // should not have been signaled because we did not go to less than 100
247 assert(m_ev.m_num_eviction_thread_runs == 0);
248 assert(m_ev.m_num_sleepers == 1); // make sure fake sleeper did not go away
249
250 m_ev.m_size_evicting = 0;
251 m_ev.m_num_sleepers = 0;
252 m_ev.m_size_current = 0;
253 m_ev.destroy();
254 this->verify_ev_destroy();
255 }
256
run_test()257 void evictor_unit_test::run_test() {
258 this->verify_ev_counts();
259 this->verify_ev_m_size_reserved();
260 this->verify_ev_handling_cache_pressure();
261 return;
262 }
263
264 int
test_main(int argc,const char * argv[])265 test_main(int argc, const char *argv[]) {
266 default_parse_args(argc, argv);
267 evictor_unit_test ev_test;
268 ev_test.init();
269 ev_test.run_test();
270 ev_test.destroy();
271 return 0;
272 }
273