// Squawk messenger. // Copyright (C) 2019 Yury Gubich // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef DATABASE_CACHE_HPP #define DATABASE_CACHE_HPP #include "cache.h" #include "exceptions.h" template DataBase::Cache::Cache(const std::string& p_name, DataBase* parent): DataBase::Table(p_name, parent), mode(new Mode), cache(new std::map()), abscent(new std::set()), sizeDifference(new uint32_t) { *mode = Mode::nothing; *sizeDifference = 0; } template DataBase::Cache::~Cache() { delete sizeDifference; delete mode; delete cache; delete abscent; } template void DataBase::Cache::addRecord(const K& key, const V& value) { if (!DataBase::Table::db->opened) { throw Closed("addRecord", DataBase::Table::db->name, DataBase::Table::name); } if (cache->count(key) > 0) { throw Exist(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } Table::addRecord(key, value); cache->insert(std::make_pair(key, value)); if (*mode != Mode::full) { abscent->erase(key); } } template bool DataBase::Cache::forceRecord(const K& key, const V& value) { if (!DataBase::Table::db->opened) { throw Closed("forceRecord", DataBase::Table::db->name, DataBase::Table::name); } bool added = Table::forceRecord(key, value); if (*mode == Mode::full) { (*cache)[key] = value; } else { if (added) { abscent->erase(key); } std::pair::iterator, bool> result = cache->insert(std::make_pair(key, value)); if (!result.second) { result.first->second = value; } else if (!added) { //this way database had value but cache didn't, so, need to decrease sizeDifference handleMode(); } } return added; } template void DataBase::Cache::changeRecord(const K& key, const V& value) { if (!DataBase::Table::db->opened) { throw Closed("changeRecord", DataBase::Table::db->name, DataBase::Table::name); } if (*mode == Mode::full) { typename std::map::iterator itr = cache->find(key); if (itr == cache->end()) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } Table::changeRecord(key, value); itr->second = value; } else { if (abscent->count(key) > 0) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } try { Table::changeRecord(key, value); typename std::pair::iterator, bool> res = cache->insert(std::make_pair(key, value)); if (!res.second) { res.first->second = value; } else { handleMode(); } } catch (const NotFound& error) { abscent->insert(key); throw error; } } } template V DataBase::Cache::getRecord(const K& key) const { if (!DataBase::Table::db->opened) { throw Closed("getRecord", DataBase::Table::db->name, DataBase::Table::name); } typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) { return itr->second; } if (*mode == Mode::full || abscent->count(key) != 0) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } try { V value = Table::getRecord(key); cache->insert(std::make_pair(key, value)); handleMode(); return value; } catch (const NotFound& error) { if (*mode != Mode::full) { abscent->insert(key); } throw error; } } template bool DataBase::Cache::checkRecord(const K& key) const { if (!DataBase::Table::db->opened) { throw Closed("checkRecord", DataBase::Table::db->name, DataBase::Table::name); } typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) { return true; } if (*mode == Mode::full || abscent->count(key) != 0) { return false; } try { V value = Table::getRecord(key); cache->insert(std::make_pair(key, value)); handleMode(); return true; } catch (const NotFound& error) { if (*mode != Mode::full) { abscent->insert(key); } return false; } } template std::map DataBase::Cache::readAll() const { if (!DataBase::Table::db->opened) { throw Closed("readAll", DataBase::Table::db->name, DataBase::Table::name); } if (*mode != Mode::full) { //there is a room for optimization *mode = Mode::full; //I can read and deserialize only those values *cache = DataBase::Table::readAll(); //that are missing in the cache abscent->clear(); *sizeDifference = 0; } return *cache; } template void DataBase::Cache::replaceAll(const std::map& data) { DataBase::Table::replaceAll(data); *cache = data; } template uint32_t DataBase::Cache::addRecords(const std::map& data, bool overwrite) { uint32_t newSize = DataBase::Table::addRecords(data, overwrite); Mode& m = *mode; if (m == Mode::nothing) { m = Mode::size; } std::map& c = *cache; std::set& a = *abscent; for (const std::pair& pair : data) { std::pair::iterator, bool> res = c.insert(pair); if (!res.second) { if (overwrite) { res.first->second = pair.second; } } else if (m != Mode::full) { a.erase(pair.first); } } if (m != Mode::full) { *sizeDifference = newSize - c.size(); if (*sizeDifference == 0) { *mode = Mode::full; abscent->clear(); } } return newSize; } template void DataBase::Cache::removeRecord(const K& key) { if (!DataBase::Table::db->opened) { throw Closed("removeRecord", DataBase::Table::db->name, DataBase::Table::name); } typename std::pair::const_iterator, bool> pair = abscent->insert(key); if (!pair.second) { throw NotFound(DataBase::_Table::toString(key), DataBase::Table::db->name, DataBase::Table::name); } Table::removeRecord(key); if (cache->erase(key) == 0) { //if it was not cached and we are now in size mode then the sizeDifference would decrease handleMode(); } if (*mode != Mode::full) { abscent->insert(key); } } template uint32_t DataBase::Cache::count() const { switch (*mode) { case Mode::nothing: { uint32_t sz = DataBase::Table::count(); *sizeDifference = sz - cache->size(); if (sz == 0) { *mode = Mode::full; abscent->clear(); } else { *mode = Mode::size; } return sz; } case Mode::size: return cache->size() + *sizeDifference; case Mode::full: return cache->size(); } } template void DataBase::Cache::handleMode() const { if (*mode == Mode::size) { --(*sizeDifference); if (*sizeDifference == 0) { *mode = Mode::full; abscent->clear(); } } } template int DataBase::Cache::drop(MDB_txn * transaction) { int res = DataBase::Table::drop(transaction); cache->clear(); abscent->clear(); *mode = Mode::full; *sizeDifference = 0; return res; } #endif //DATABASE_CACHE_HPP