Files
MapLibreParticleLab/test/storage/cache_size.cpp
Bruno de Oliveira Abinader 2f86467586 Cleanup std::chrono usage
Use mbgl::Duration and mbgl::{,Milli}Seconds whenever possible.
2016-01-25 15:11:18 +02:00

242 lines
6.7 KiB
C++

#include "storage.hpp"
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/sqlite_cache.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/timer.hpp>
#include <mbgl/util/chrono.hpp>
#include <memory>
#include <random>
bool tileIsCached(mbgl::SQLiteCache* cache, unsigned id) {
using namespace mbgl;
auto url = std::string("http://tile") + mbgl::util::toString(id);
bool replied = false;
std::unique_ptr<Response> response;
auto callback = [&] (std::unique_ptr<Response> res) {
replied = true;
response = std::move(res);
};
Resource resource{ Resource::Kind::Tile, url };
auto req = cache->get(resource, callback);
while (!replied) {
util::RunLoop::Get()->runOnce();
}
return response != nullptr;
}
void insertTile(mbgl::SQLiteCache* cache, unsigned id, uint64_t size) {
using namespace mbgl;
auto url = std::string("http://tile") + mbgl::util::toString(id);
Response response;
response.modified = SystemClock::now();
response.expires = SystemClock::now() + Seconds(5000);
response.etag = url;
auto data = std::make_shared<std::string>(size, 0);
// Fill data with garbage so SQLite won't try to
// optimize allocation by reusing pages.
static std::mt19937 generator;
std::generate_n(data->begin(), size, generator);
response.data = data;
Resource resource{ Resource::Kind::Tile, url };
cache->put(resource, response);
}
void refreshTile(mbgl::SQLiteCache* cache, unsigned id) {
using namespace mbgl;
auto url = std::string("http://tile") + mbgl::util::toString(id);
Response response;
response.modified = SystemClock::now();
response.expires = SystemClock::now() + Seconds(5000);
response.notModified = true;
Resource resource{ Resource::Kind::Tile, url };
cache->put(resource, response);
}
uint64_t cacheSize(mbgl::SQLiteCache* cache, unsigned entryCount, uint64_t entrySize) {
uint64_t total = 0;
for (unsigned i = 0; i < entryCount; ++i) {
if (tileIsCached(cache, i)) {
total += entrySize;
}
}
return total;
}
TEST_F(Storage, CacheEntrySizeLimit) {
using namespace mbgl;
util::RunLoop loop;
SQLiteCache cache(":memory:");
const uint64_t entrySize = 5 * 1024 * 1024; // 5 MB
insertTile(&cache, 0, entrySize);
EXPECT_TRUE(tileIsCached(&cache, 0));
insertTile(&cache, 1, entrySize + 1);
EXPECT_FALSE(tileIsCached(&cache, 1));
insertTile(&cache, 2, entrySize - 1);
EXPECT_TRUE(tileIsCached(&cache, 2));
// Setting a new size should not delete existing entries.
cache.setMaximumCacheEntrySize(entrySize / 2);
EXPECT_TRUE(tileIsCached(&cache, 2));
insertTile(&cache, 3, entrySize / 2 - 1);
EXPECT_TRUE(tileIsCached(&cache, 3));
insertTile(&cache, 4, entrySize);
EXPECT_FALSE(tileIsCached(&cache, 4));
cache.setMaximumCacheEntrySize(entrySize * 2);
insertTile(&cache, 5, entrySize);
EXPECT_TRUE(tileIsCached(&cache, 5));
}
TEST_F(Storage, CacheSizeSetNewLimit) {
using namespace mbgl;
util::RunLoop loop;
SQLiteCache cache(":memory:");
const unsigned entryCount = 800;
const uint64_t entrySize = 10 * 1024; // 10 KB
cache.setMaximumCacheEntrySize(entrySize + 1);
// Cache size defaults to unlimited, all these
// inserts should work.
for (unsigned i = 0; i < entryCount; ++i) {
insertTile(&cache, i, entrySize);
}
for (unsigned i = 0; i < entryCount; ++i) {
EXPECT_TRUE(tileIsCached(&cache, i));
}
uint64_t expectedCacheSize = entryCount * entrySize;
EXPECT_EQ(cacheSize(&cache, entryCount, entrySize), expectedCacheSize);
// Setting a new size should remove records until the new
// size limit is satisfied.
cache.setMaximumCacheSize(expectedCacheSize / 2);
EXPECT_LT(cacheSize(&cache, entryCount, entrySize), expectedCacheSize / 2);
// Cache size 1 should practically clean the cache and
// prevent adding any record, although it makes no sense
// to use such size limit IRL.
cache.setMaximumCacheSize(1);
EXPECT_EQ(cacheSize(&cache, entryCount, entrySize), 0);
insertTile(&cache, 1000, entrySize);
EXPECT_FALSE(tileIsCached(&cache, 1000));
// Zero should be treated as unlimited.
cache.setMaximumCacheSize(0);
for (unsigned i = 0; i < entryCount; ++i) {
insertTile(&cache, i, entrySize);
}
EXPECT_EQ(cacheSize(&cache, entryCount, entrySize), expectedCacheSize);
}
TEST_F(Storage, CacheSizePruneLeastAccessed) {
using namespace mbgl;
util::RunLoop loop;
SQLiteCache cache(":memory:");
const unsigned entryCount = 400;
const uint64_t entrySize = 10 * 1024; // 10 KB
cache.setMaximumCacheEntrySize(entrySize + 1);
cache.setMaximumCacheSize(entrySize * 350);
for (unsigned i = 0; i < entryCount; ++i) {
insertTile(&cache, i, entrySize);
if (i == entryCount / 2) {
// We need to sleep for 1s here because
// that is the time resolution for the
// `accessed` time. Then we 'ping' the
// entry, that should update the
// `accessed` time, so it won't get
// pruned when we need more space.
bool done = false;
util::Timer timer;
timer.start(Milliseconds(1300),
Duration::zero(),
[&done] { done = true; });
while (!done) {
loop.runOnce();
}
EXPECT_TRUE(tileIsCached(&cache, 7));
// Refresh should also update the `accessed`
// time of a tile.
refreshTile(&cache, 9);
}
}
EXPECT_FALSE(tileIsCached(&cache, 6));
EXPECT_FALSE(tileIsCached(&cache, 8));
EXPECT_FALSE(tileIsCached(&cache, 10));
EXPECT_TRUE(tileIsCached(&cache, 7));
EXPECT_TRUE(tileIsCached(&cache, 9));
}
TEST_F(Storage, CacheSizeStress) {
using namespace mbgl;
util::RunLoop loop;
SQLiteCache cache(":memory:");
const unsigned entryCount = 2000;
const uint64_t entrySize = 10 * 1024; // 10 KB
cache.setMaximumCacheEntrySize(entrySize + 1);
cache.setMaximumCacheSize(entrySize * 300);
for (unsigned i = 0; i < entryCount; ++i) {
insertTile(&cache, i, entrySize);
}
// Should not be in the cache as they were
// first inserted.
EXPECT_FALSE(tileIsCached(&cache, 0));
EXPECT_FALSE(tileIsCached(&cache, 99));
EXPECT_FALSE(tileIsCached(&cache, 199));
EXPECT_FALSE(tileIsCached(&cache, 299));
EXPECT_FALSE(tileIsCached(&cache, 399));
EXPECT_TRUE(tileIsCached(&cache, entryCount - 1));
EXPECT_LT(cacheSize(&cache, entryCount, entrySize), entrySize * 300);
}