2
* Copyright (C) 2015 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Michi Henning <michi.henning@canonical.com>
19
#include <core/persistent_string_cache.h>
21
#include <boost/filesystem.hpp>
22
#include <gtest/gtest.h>
29
// Removes the contents of db_dir, but not db_dir itself.
31
void unlink_db(string const& db_dir)
33
namespace fs = boost::filesystem;
36
for (fs::directory_iterator end, it(db_dir); it != end; ++it)
38
remove_all(it->path());
46
string const test_db = TEST_DIR "/db";
48
TEST(PersistentStringCache, basic)
53
// Constructor and move constructor.
54
auto c = PersistentStringCache::open(test_db, 1024, CacheDiscardPolicy::lru_only);
55
PersistentStringCache c2(move(*c));
56
EXPECT_EQ(1024, c2.max_size_in_bytes());
60
// Constructor and move assignment.
61
auto c = PersistentStringCache::open(test_db);
62
auto c2 = PersistentStringCache::open(test_db + "2", 2048, CacheDiscardPolicy::lru_ttl);
64
EXPECT_EQ(2048, c->max_size_in_bytes());
67
// Tests below are cursory, simply calling each method once.
68
// Note: get_or_put() is tested by PersistentStringCacheImpl_test.cpp.
70
auto c = PersistentStringCache::open(test_db);
73
Optional<string> metadata;
74
Optional<PersistentStringCache::Data> data;
78
data = c->get_data("x");
80
metadata = c->get_metadata("x");
81
EXPECT_FALSE(metadata);
82
EXPECT_FALSE(c->contains_key("x"));
83
EXPECT_EQ(0, c->size());
84
EXPECT_EQ(0, c->size_in_bytes());
85
EXPECT_EQ(1024, c->max_size_in_bytes());
86
EXPECT_GE(c->disk_size_in_bytes(), 0); // For coverage
87
EXPECT_EQ(CacheDiscardPolicy::lru_only, c->discard_policy());
88
EXPECT_TRUE(c->put("x", ""));
89
EXPECT_TRUE(c->put("x", "x", 1));
90
EXPECT_TRUE(c->put("x", "y", ""));
91
EXPECT_TRUE(c->put("x", "y", 1, "z", 1));
92
EXPECT_TRUE(c->put_metadata("x", "z"));
93
EXPECT_TRUE(c->put_metadata("x", "z", 1));
94
data = c->take_data("x");
95
EXPECT_TRUE(bool(data));
96
EXPECT_EQ("y", data->value);
97
EXPECT_EQ("z", data->metadata);
98
PersistentStringCache::Data data2;
99
data2 = *data; // Assignment operator
100
EXPECT_EQ("y", data2.value);
101
EXPECT_EQ("z", data2.metadata);
104
EXPECT_FALSE(c->invalidate("x"));
105
EXPECT_FALSE(c->touch("x"));
109
c->invalidate({"x"});
110
EXPECT_FALSE(c->contains_key("x"));
115
auto handler = [&](string const&, CacheEvent, PersistentCacheStats const&)
119
c->set_handler(AllCacheEvents, handler);
120
c->set_handler(CacheEvent::get, handler);
124
TEST(PersistentStringCache, stats)
127
PersistentCacheStats s;
128
EXPECT_EQ("", s.cache_path());
129
EXPECT_EQ(CacheDiscardPolicy::lru_only, s.policy());
130
EXPECT_EQ(0, s.size());
131
EXPECT_EQ(0, s.size_in_bytes());
132
EXPECT_EQ(0, s.max_size_in_bytes());
133
EXPECT_EQ(0, s.hits());
134
EXPECT_EQ(0, s.misses());
135
EXPECT_EQ(0, s.hits_since_last_miss());
136
EXPECT_EQ(0, s.misses_since_last_hit());
137
EXPECT_EQ(0, s.longest_hit_run());
138
EXPECT_EQ(0, s.longest_miss_run());
139
EXPECT_EQ(0, s.ttl_evictions());
140
EXPECT_EQ(0, s.lru_evictions());
141
EXPECT_EQ(chrono::system_clock::time_point(), s.most_recent_hit_time());
142
EXPECT_EQ(chrono::system_clock::time_point(), s.most_recent_miss_time());
143
EXPECT_EQ(chrono::system_clock::time_point(), s.longest_hit_run_time());
144
EXPECT_EQ(chrono::system_clock::time_point(), s.longest_miss_run_time());
145
auto hist = s.histogram();
146
for (unsigned i = 0; i < hist.size(); ++i)
148
EXPECT_EQ(0, hist[i]); // Other bins must still be empty.
155
auto c = PersistentStringCache::open(test_db, 1024, CacheDiscardPolicy::lru_ttl);
157
// Check that we start out with everything initialized.
159
EXPECT_EQ(test_db, s.cache_path());
160
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s.policy());
161
EXPECT_EQ(0, s.size());
162
EXPECT_EQ(0, s.size_in_bytes());
163
EXPECT_EQ(1024, s.max_size_in_bytes());
164
EXPECT_EQ(0, s.hits());
165
EXPECT_EQ(0, s.misses());
166
EXPECT_EQ(0, s.hits_since_last_miss());
167
EXPECT_EQ(0, s.misses_since_last_hit());
168
EXPECT_EQ(0, s.longest_hit_run());
169
EXPECT_EQ(0, s.longest_miss_run());
170
EXPECT_EQ(0, s.ttl_evictions());
171
EXPECT_EQ(0, s.lru_evictions());
172
EXPECT_EQ(chrono::system_clock::time_point(), s.most_recent_hit_time());
173
EXPECT_EQ(chrono::system_clock::time_point(), s.most_recent_miss_time());
174
EXPECT_EQ(chrono::system_clock::time_point(), s.longest_hit_run_time());
175
EXPECT_EQ(chrono::system_clock::time_point(), s.longest_miss_run_time());
177
// Incur a miss followed by a hit.
178
auto now = chrono::system_clock::now();
180
EXPECT_FALSE(c->get("y"));
181
EXPECT_TRUE(bool(c->get("x")));
184
EXPECT_EQ(test_db, s.cache_path()); // Must not have changed.
185
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s.policy()); // Must not have changed.
186
EXPECT_EQ(1, s.size());
187
EXPECT_EQ(2, s.size_in_bytes());
188
EXPECT_EQ(1024, s.max_size_in_bytes()); // Must not have changed.
189
EXPECT_EQ(1, s.hits());
190
EXPECT_EQ(1, s.misses());
191
EXPECT_EQ(1, s.hits_since_last_miss());
192
EXPECT_EQ(0, s.misses_since_last_hit());
193
EXPECT_EQ(1, s.longest_hit_run());
194
EXPECT_EQ(1, s.longest_miss_run());
195
EXPECT_EQ(0, s.ttl_evictions());
196
EXPECT_EQ(0, s.lru_evictions());
197
EXPECT_NE(chrono::system_clock::time_point(), s.most_recent_hit_time());
198
EXPECT_GE(s.most_recent_hit_time(), now);
199
EXPECT_NE(chrono::system_clock::time_point(), s.most_recent_miss_time());
200
EXPECT_GE(s.most_recent_miss_time(), now);
201
EXPECT_NE(chrono::system_clock::time_point(), s.longest_hit_run_time());
202
EXPECT_GE(s.longest_hit_run_time(), now);
203
EXPECT_NE(chrono::system_clock::time_point(), s.longest_miss_run_time());
204
EXPECT_GE(s.longest_miss_run_time(), now);
206
auto last_hit_time = s.most_recent_hit_time();
207
auto last_miss_time = s.most_recent_miss_time();
208
auto hit_run_time = s.longest_hit_run_time();
209
auto miss_run_time = s.longest_miss_run_time();
212
now = chrono::system_clock::now();
213
EXPECT_TRUE(bool(c->get("x")));
214
EXPECT_TRUE(bool(c->get("x")));
217
EXPECT_EQ(3, s.hits());
218
EXPECT_EQ(1, s.misses());
219
EXPECT_EQ(3, s.hits_since_last_miss());
220
EXPECT_EQ(0, s.misses_since_last_hit());
221
EXPECT_EQ(3, s.longest_hit_run());
222
EXPECT_EQ(1, s.longest_miss_run());
223
EXPECT_EQ(0, s.ttl_evictions());
224
EXPECT_EQ(0, s.lru_evictions());
225
EXPECT_LE(last_hit_time, s.most_recent_hit_time());
226
EXPECT_EQ(last_miss_time, s.most_recent_miss_time());
227
EXPECT_LE(hit_run_time, s.longest_hit_run_time());
228
EXPECT_EQ(miss_run_time, s.longest_miss_run_time());
230
last_hit_time = s.most_recent_hit_time();
231
last_miss_time = s.most_recent_miss_time();
232
hit_run_time = s.longest_hit_run_time();
233
miss_run_time = s.longest_miss_run_time();
236
now = chrono::system_clock::now();
237
EXPECT_FALSE(c->get("y"));
238
EXPECT_FALSE(c->get("y"));
239
EXPECT_FALSE(c->get("y"));
240
EXPECT_FALSE(c->get("y"));
243
EXPECT_EQ(3, s.hits());
244
EXPECT_EQ(5, s.misses());
245
EXPECT_EQ(0, s.hits_since_last_miss());
246
EXPECT_EQ(4, s.misses_since_last_hit());
247
EXPECT_EQ(3, s.longest_hit_run());
248
EXPECT_EQ(4, s.longest_miss_run());
249
EXPECT_EQ(0, s.ttl_evictions());
250
EXPECT_EQ(0, s.lru_evictions());
251
EXPECT_EQ(last_hit_time, s.most_recent_hit_time());
252
EXPECT_LE(last_miss_time, s.most_recent_miss_time());
253
EXPECT_EQ(hit_run_time, s.longest_hit_run_time());
254
EXPECT_LE(miss_run_time, s.longest_miss_run_time());
256
last_hit_time = s.most_recent_hit_time();
257
last_miss_time = s.most_recent_miss_time();
258
hit_run_time = s.longest_hit_run_time();
259
miss_run_time = s.longest_miss_run_time();
262
now = chrono::system_clock::now();
263
EXPECT_TRUE(bool(c->get("x")));
266
EXPECT_EQ(4, s.hits());
267
EXPECT_EQ(5, s.misses());
268
EXPECT_EQ(1, s.hits_since_last_miss());
269
EXPECT_EQ(0, s.misses_since_last_hit());
270
EXPECT_EQ(3, s.longest_hit_run());
271
EXPECT_EQ(4, s.longest_miss_run());
272
EXPECT_EQ(0, s.ttl_evictions());
273
EXPECT_EQ(0, s.lru_evictions());
274
EXPECT_LE(last_hit_time, s.most_recent_hit_time());
275
EXPECT_EQ(last_miss_time, s.most_recent_miss_time());
276
EXPECT_LE(hit_run_time, s.longest_hit_run_time());
277
EXPECT_EQ(miss_run_time, s.longest_miss_run_time());
279
last_hit_time = s.most_recent_hit_time();
280
last_miss_time = s.most_recent_miss_time();
281
hit_run_time = s.longest_hit_run_time();
282
miss_run_time = s.longest_miss_run_time();
287
EXPECT_EQ(test_db, s2.cache_path());
288
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s2.policy());
289
EXPECT_EQ(1, s2.size());
290
EXPECT_EQ(2, s2.size_in_bytes());
291
EXPECT_EQ(1024, s2.max_size_in_bytes());
292
EXPECT_EQ(4, s2.hits());
293
EXPECT_EQ(5, s2.misses());
294
EXPECT_EQ(1, s2.hits_since_last_miss());
295
EXPECT_EQ(0, s2.misses_since_last_hit());
296
EXPECT_EQ(3, s.longest_hit_run());
297
EXPECT_EQ(4, s.longest_miss_run());
298
EXPECT_EQ(last_hit_time, s2.most_recent_hit_time());
299
EXPECT_EQ(last_miss_time, s2.most_recent_miss_time());
300
EXPECT_EQ(hit_run_time, s2.longest_hit_run_time());
301
EXPECT_EQ(miss_run_time, s2.longest_miss_run_time());
305
// Assignment operator.
306
PersistentCacheStats s2;
308
EXPECT_EQ(test_db, s2.cache_path());
309
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s2.policy());
310
EXPECT_EQ(1, s2.size());
311
EXPECT_EQ(2, s2.size_in_bytes());
312
EXPECT_EQ(1024, s2.max_size_in_bytes());
313
EXPECT_EQ(4, s2.hits());
314
EXPECT_EQ(5, s2.misses());
315
EXPECT_EQ(1, s2.hits_since_last_miss());
316
EXPECT_EQ(0, s2.misses_since_last_hit());
317
EXPECT_EQ(3, s.longest_hit_run());
318
EXPECT_EQ(4, s.longest_miss_run());
319
EXPECT_EQ(last_hit_time, s2.most_recent_hit_time());
320
EXPECT_EQ(last_miss_time, s2.most_recent_miss_time());
321
EXPECT_EQ(hit_run_time, s2.longest_hit_run_time());
322
EXPECT_EQ(miss_run_time, s2.longest_miss_run_time());
327
PersistentCacheStats s2(move(s));
328
EXPECT_EQ(test_db, s2.cache_path());
329
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s2.policy());
330
EXPECT_EQ(1, s2.size());
331
EXPECT_EQ(2, s2.size_in_bytes());
332
EXPECT_EQ(1024, s2.max_size_in_bytes());
333
EXPECT_EQ(4, s2.hits());
334
EXPECT_EQ(5, s2.misses());
335
EXPECT_EQ(1, s2.hits_since_last_miss());
336
EXPECT_EQ(0, s2.misses_since_last_hit());
337
EXPECT_EQ(3, s.longest_hit_run());
338
EXPECT_EQ(4, s.longest_miss_run());
339
EXPECT_EQ(last_hit_time, s2.most_recent_hit_time());
340
EXPECT_EQ(last_miss_time, s2.most_recent_miss_time());
341
EXPECT_EQ(hit_run_time, s2.longest_hit_run_time());
342
EXPECT_EQ(miss_run_time, s2.longest_miss_run_time());
344
s.cache_path(); // Moved-from instance must be usable.
347
PersistentCacheStats s3;
349
EXPECT_EQ(test_db, s3.cache_path());
350
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s3.policy());
351
EXPECT_EQ(1, s3.size());
352
EXPECT_EQ(2, s3.size_in_bytes());
353
EXPECT_EQ(1024, s3.max_size_in_bytes());
354
EXPECT_EQ(4, s3.hits());
355
EXPECT_EQ(5, s3.misses());
356
EXPECT_EQ(1, s3.hits_since_last_miss());
357
EXPECT_EQ(0, s3.misses_since_last_hit());
358
EXPECT_EQ(3, s.longest_hit_run());
359
EXPECT_EQ(4, s.longest_miss_run());
360
EXPECT_EQ(last_hit_time, s3.most_recent_hit_time());
361
EXPECT_EQ(last_miss_time, s3.most_recent_miss_time());
362
EXPECT_EQ(hit_run_time, s3.longest_hit_run_time());
363
EXPECT_EQ(miss_run_time, s3.longest_miss_run_time());
365
s2.cache_path(); // Moved-from instance must be usable.
368
// To get coverage for copying and moving from the internal instance,
369
// we need to use an event handler because the event handler is passed the
370
// shared_ptr to the internal instance, whereas c->stats() returns a copy.
372
auto copy_construct_handler = [&](string const&, CacheEvent, PersistentCacheStats const& s)
374
PersistentCacheStats s2(s);
375
EXPECT_EQ(test_db, s2.cache_path());
376
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s2.policy());
377
EXPECT_EQ(1, s2.size());
378
EXPECT_EQ(2, s2.size_in_bytes());
379
EXPECT_EQ(1024, s2.max_size_in_bytes());
380
EXPECT_EQ(4, s2.hits());
381
EXPECT_EQ(5, s2.misses());
382
EXPECT_EQ(1, s2.hits_since_last_miss());
383
EXPECT_EQ(0, s2.misses_since_last_hit());
384
EXPECT_EQ(3, s.longest_hit_run());
385
EXPECT_EQ(4, s.longest_miss_run());
386
EXPECT_EQ(last_hit_time, s2.most_recent_hit_time());
387
EXPECT_EQ(last_miss_time, s2.most_recent_miss_time());
388
EXPECT_EQ(hit_run_time, s2.longest_hit_run_time());
389
EXPECT_EQ(miss_run_time, s2.longest_miss_run_time());
391
EXPECT_EQ(test_db, s.cache_path()); // Source must remain intact.
393
c->set_handler(CacheEvent::touch, copy_construct_handler);
396
auto move_assign_handler = [&](string const&, CacheEvent, PersistentCacheStats const& s)
398
PersistentCacheStats s2;
400
EXPECT_EQ(test_db, s2.cache_path());
401
EXPECT_EQ(CacheDiscardPolicy::lru_ttl, s2.policy());
402
EXPECT_EQ(1, s2.size());
403
EXPECT_EQ(2, s2.size_in_bytes());
404
EXPECT_EQ(1024, s2.max_size_in_bytes());
405
EXPECT_EQ(4, s2.hits());
406
EXPECT_EQ(5, s2.misses());
407
EXPECT_EQ(1, s2.hits_since_last_miss());
408
EXPECT_EQ(0, s2.misses_since_last_hit());
409
EXPECT_EQ(3, s.longest_hit_run());
410
EXPECT_EQ(4, s.longest_miss_run());
411
EXPECT_EQ(last_hit_time, s2.most_recent_hit_time());
412
EXPECT_EQ(last_miss_time, s2.most_recent_miss_time());
413
EXPECT_EQ(hit_run_time, s2.longest_hit_run_time());
414
EXPECT_EQ(miss_run_time, s2.longest_miss_run_time());
416
EXPECT_EQ(test_db, s.cache_path()); // Moved-from instance wasn't really moved from.
418
c->set_handler(CacheEvent::touch, move_assign_handler);
421
// Move construction is impossible because handlers are passed a const ref
422
// to the internal instance.
428
auto c = PersistentStringCache::open(test_db, 1024, CacheDiscardPolicy::lru_ttl);
430
// Check that eviction counts are correct.
431
c->put("1", string(200, 'a'));
432
c->put("2", string(200, 'a'));
433
c->put("3", string(200, 'a'));
434
c->put("4", string(200, 'a'));
435
c->put("5", string(200, 'a'));
437
// Cache almost full now (1005 bytes). Adding a 401-byte record must evict two entries.
438
c->put("6", string(400, 'a'));
439
EXPECT_EQ(1004, c->size_in_bytes());
441
EXPECT_EQ(4, s.size());
442
EXPECT_EQ(0, s.ttl_evictions());
443
EXPECT_EQ(2, s.lru_evictions());
445
// Add two records that expire in 500ms. These must evict two more entries.
446
auto later = chrono::system_clock::now() + chrono::milliseconds(500);
447
c->put("7", string(200, 'a'), later);
448
c->put("8", string(200, 'a'), later);
449
EXPECT_EQ(1004, c->size_in_bytes());
451
EXPECT_EQ(4, s.size());
452
EXPECT_EQ(0, s.ttl_evictions());
453
EXPECT_EQ(4, s.lru_evictions());
455
// Wait until records have expired.
456
while (chrono::system_clock::now() <= later)
458
this_thread::sleep_for(chrono::milliseconds(5));
461
// Add a single record. That must evict both expired entries, even though evicting one would be enough.
462
c->put("9", string(300, 'a'));
463
EXPECT_EQ(903, c->size_in_bytes());
465
EXPECT_EQ(3, s.size());
466
EXPECT_EQ(2, s.ttl_evictions());
467
EXPECT_EQ(4, s.lru_evictions());