1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-12-12 11:01:17 +03:00
Files
mariadb-columnstore-engine/src/Cache.cpp
2019-03-07 13:18:38 -06:00

282 lines
7.2 KiB
C++

#include "Cache.h"
#include "Config.h"
#include "Downloader.h"
#include <iostream>
#include <syslog.h>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace std;
using namespace boost::filesystem;
namespace storagemanager
{
Cache::Cache() : currentCacheSize(0)
{
Config *conf = Config::get();
logger = SMLogging::get();
sync = Synchronizer::get();
string stmp = conf->getValue("Cache", "cache_size");
if (stmp.empty())
{
logger->log(LOG_CRIT, "Cache/cache_size is not set");
throw runtime_error("Please set Cache/cache_size in the storagemanager.cnf file");
}
try
{
maxCacheSize = stoul(stmp);
}
catch (invalid_argument &)
{
logger->log(LOG_CRIT, "Cache/cache_size is not a number");
throw runtime_error("Please set Cache/cache_size to a number");
}
//cout << "Cache got cache size " << maxCacheSize << endl;
stmp = conf->getValue("ObjectStorage", "object_size");
if (stmp.empty())
{
logger->log(LOG_CRIT, "ObjectStorage/object_size is not set");
throw runtime_error("Please set ObjectStorage/object_size in the storagemanager.cnf file");
}
try
{
objectSize = stoul(stmp);
}
catch (invalid_argument &)
{
logger->log(LOG_CRIT, "ObjectStorage/object_size is not a number");
throw runtime_error("Please set ObjectStorage/object_size to a number");
}
prefix = conf->getValue("Cache", "path");
if (prefix.empty())
{
logger->log(LOG_CRIT, "Cache/path is not set");
throw runtime_error("Please set Cache/path in the storagemanager.cnf file");
}
try
{
boost::filesystem::create_directories(prefix);
}
catch (exception &e)
{
syslog(LOG_CRIT, "Failed to create %s, got: %s", prefix.string().c_str(), e.what());
throw e;
}
//cout << "Cache got prefix " << prefix << endl;
downloader.setDownloadPath(prefix.string());
/* todo: populate structures with existing files in the cache path */
}
Cache::~Cache()
{
}
void Cache::read(const vector<string> &keys)
{
/*
move existing keys to a do-not-evict map
fetch keys that do not exist
after fetching, move all keys from do-not-evict to the back of the LRU
*/
boost::unique_lock<boost::mutex> s(lru_mutex);
vector<const string *> keysToFetch;
uint i;
M_LRU_t::iterator mit;
for (const string &key : keys)
{
mit = m_lru.find(key);
if (mit != m_lru.end())
// it's in the cache, add the entry to the do-not-evict set
addToDNE(mit->lit);
else
// not in the cache, put it in the list to download
keysToFetch.push_back(&key);
}
// TODO: get the sizes of the objects to download and make space
// For now using an estimate
makeSpace(keys.size() * objectSize);
s.unlock();
// start downloading the keys to fetch
int dl_err;
vector<int> dl_errnos;
if (!keysToFetch.empty())
dl_err = downloader.download(keysToFetch, &dl_errnos);
s.lock();
// move all keys to the back of the LRU
for (i = 0; i < keys.size(); i++)
{
mit = m_lru.find(keys[i]);
if (mit != m_lru.end())
{
lru.splice(lru.end(), lru, mit->lit);
removeFromDNE(lru.end());
}
else if (dl_errnos[i] == 0) // successful download
{
lru.push_back(keys[i]);
m_lru.insert(M_LRU_element_t(&(lru.back()), lru.end()--));
}
else
{
// Downloader already logged it, anything to do here?
/* brainstorming options for handling it.
1) Should it be handled? The caller will log a file-not-found error, and there will be a download
failure in the log already.
2) Can't really DO anything can it?
*/
}
}
}
Cache::DNEElement::DNEElement(const LRU_t::iterator &k) : key(k), refCount(1)
{
}
void Cache::addToDNE(const LRU_t::iterator &key)
{
DNEElement e(key);
DNE_t::iterator it = doNotEvict.find(e);
if (it != doNotEvict.end())
{
DNEElement &dnee = const_cast<DNEElement &>(*it);
++dnee.refCount;
}
else
doNotEvict.insert(e);
}
void Cache::removeFromDNE(const LRU_t::iterator &key)
{
DNEElement e(key);
DNE_t::iterator it = doNotEvict.find(e);
if (it == doNotEvict.end())
return;
DNEElement &dnee = const_cast<DNEElement &>(*it);
if (--dnee.refCount == 0)
doNotEvict.erase(it);
}
const boost::filesystem::path & Cache::getCachePath()
{
return prefix;
}
void Cache::exists(const vector<string> &keys, vector<bool> *out)
{
out->resize(keys.size());
boost::unique_lock<boost::mutex> s(lru_mutex);
for (int i = 0; i < keys.size(); i++)
(*out)[i] = (m_lru.find(keys[i]) == m_lru.end());
}
void Cache::newObject(const string &key, size_t size)
{
}
void Cache::deletedObject(const string &key, size_t size)
{
}
void Cache::setMaxCacheSize(size_t size)
{
}
// call this holding lru_mutex
void Cache::makeSpace(size_t size)
{
ssize_t thisMuch = currentCacheSize + size - maxCacheSize;
if (thisMuch <= 0)
return;
struct stat statbuf;
LRU_t::iterator it = lru.begin();
while (it != lru.end() && thisMuch > 0)
{
if (doNotEvict.find(it) != doNotEvict.end())
{
++it;
continue; // it's in the do-not-evict list
}
boost::filesystem::path cachedFile = prefix / *it;
int err = stat(cachedFile.string().c_str(), &statbuf);
if (err)
{
logger->log(LOG_WARNING, "Downloader: There seems to be a cached file that couldn't be stat'ed: %s", cachedFile.string().c_str());
++it;
continue;
}
/*
TODO: tell Synchronizer that this key will be evicted
delete the file
remove it from our structs
update current size
*/
assert(currentCacheSize >= statbuf.st_size);
currentCacheSize -= statbuf.st_size;
thisMuch -= statbuf.st_size;
sync->flushObject(*it);
boost::filesystem::remove(cachedFile);
LRU_t::iterator toRemove = it++;
lru.erase(toRemove);
m_lru.erase(*toRemove);
}
}
/* The helper classes */
Cache::M_LRU_element_t::M_LRU_element_t(const string *k) : key(k)
{}
Cache::M_LRU_element_t::M_LRU_element_t(const string &k) : key(&k)
{}
Cache::M_LRU_element_t::M_LRU_element_t(const string *k, const LRU_t::iterator &i) : key(k), lit(i)
{}
inline size_t Cache::KeyHasher::operator()(const M_LRU_element_t &l) const
{
return hash<string>()(*(l.key));
}
inline bool Cache::KeyEquals::operator()(const M_LRU_element_t &l1, const M_LRU_element_t &l2) const
{
return (*(l1.key) == *(l2.key));
}
inline size_t Cache::DNEHasher::operator()(const DNEElement &l) const
{
return hash<string>()(*(l.key));
}
inline bool Cache::DNEEquals::operator()(const DNEElement &l1, const DNEElement &l2) const
{
return (*(l1.key) == *(l2.key));
}
}