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