/* * LMDB Abstraction Layer. * Copyright (C) 2023 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 LMDBAL_CACHE_HPP #define LMDBAL_CACHE_HPP #include "cache.h" #include "exceptions.h" template LMDBAL::Cache::Cache(const std::string& p_name, Base* parent): Storage(p_name, parent), mode(new Mode), cache(new std::map()), abscent(new std::set()), sizeDifference(new uint32_t) { *mode = Mode::nothing; *sizeDifference = 0; } template LMDBAL::Cache::~Cache() { delete sizeDifference; delete mode; delete cache; delete abscent; } template void LMDBAL::Cache::addRecord(const K& key, const V& value) { iStorage::ensureOpened(iStorage::addRecordMethodName); if (cache->count(key) > 0) iStorage::throwDuplicate(iStorage::toString(key)); Storage::addRecord(key, value); cache->insert(std::make_pair(key, value)); if (*mode != Mode::full) abscent->erase(key); } template bool LMDBAL::Cache::forceRecord(const K& key, const V& value) { iStorage::ensureOpened(iStorage::forceRecordMethodName); bool added = Storage::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 LMDBAL::Cache::changeRecord(const K& key, const V& value) { iStorage::ensureOpened(iStorage::changeRecordMethodName); if (*mode == Mode::full) { typename std::map::iterator itr = cache->find(key); if (itr == cache->end()) iStorage::throwNotFound(iStorage::toString(key)); Storage::changeRecord(key, value); itr->second = value; } else { if (abscent->count(key) > 0) iStorage::throwNotFound(iStorage::toString(key)); try { Storage::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 LMDBAL::Cache::getRecord(const K& key) const { iStorage::ensureOpened(iStorage::getRecordMethodName); typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) return itr->second; if (*mode == Mode::full || abscent->count(key) != 0) iStorage::throwNotFound(iStorage::toString(key)); try { V value = Storage::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 LMDBAL::Cache::checkRecord(const K& key) const { iStorage::ensureOpened(iStorage::checkRecordMethodName); 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 = Storage::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 LMDBAL::Cache::readAll() const { iStorage::ensureOpened(iStorage::readAllMethodName); if (*mode != Mode::full) { //there is a room for optimization *mode = Mode::full; //I can read and deserialize only those values *cache = Storage::readAll(); //that are missing in the cache abscent->clear(); *sizeDifference = 0; } return *cache; } template void LMDBAL::Cache::replaceAll(const std::map& data) { Storage::replaceAll(data); *cache = data; if (*mode != Mode::full) { *mode = Mode::full; abscent->clear(); *sizeDifference = 0; } } template uint32_t LMDBAL::Cache::addRecords(const std::map& data, bool overwrite) { uint32_t newSize = Storage::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 LMDBAL::Cache::removeRecord(const K& key) { iStorage::ensureOpened(iStorage::removeRecordMethodName); typename std::pair::const_iterator, bool> pair = abscent->insert(key); if (!pair.second) iStorage::throwNotFound(iStorage::toString(key)); Storage::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 LMDBAL::Cache::count() const { switch (*mode) { case Mode::nothing: { uint32_t sz = Storage::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(); default: return 0; //unreachable, no such state, just to suppress the waring } } template void LMDBAL::Cache::handleMode() const { if (*mode == Mode::size) { --(*sizeDifference); if (*sizeDifference == 0) { *mode = Mode::full; abscent->clear(); } } } template int LMDBAL::Cache::drop(MDB_txn * transaction) { int res = Storage::drop(transaction); cache->clear(); abscent->clear(); *mode = Mode::full; *sizeDifference = 0; return res; } #endif //LMDBAL_CACHE_HPP