diff --git a/src/base.cpp b/src/base.cpp index 48f7dcf..0951bd6 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -120,13 +120,15 @@ void LMDBAL::Base::drop() { if (!opened) throw Closed("drop", name); - TransactionID txn = beginPrivateTransaction(emptyName); + TransactionID txn = beginTransaction(); for (const std::pair& pair : storages) { int rc = pair.second->drop(txn); - if (rc) + if (rc != MDB_SUCCESS) { + abortTransaction(txn); throw Unknown(name, mdb_strerror(rc), pair.first); + } } - commitPrivateTransaction(txn, emptyName); + commitTransaction(txn); } LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction() const { @@ -138,7 +140,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginTransaction() const { void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id) const { return abortTransaction(id, emptyName);} -void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) const { +void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id) { return commitTransaction(id, emptyName);} LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction(const std::string& storageName) const { @@ -180,7 +182,7 @@ void LMDBAL::Base::abortTransaction(LMDBAL::TransactionID id, const std::string& transactions->erase(itr); } -void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string& storageName) const { +void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string& storageName) { if (!opened) throw Closed("abortTransaction", name, storageName); @@ -198,7 +200,7 @@ void LMDBAL::Base::commitTransaction(LMDBAL::TransactionID id, const std::string LMDBAL::TransactionID LMDBAL::Base::beginPrivateReadOnlyTransaction(const std::string& storageName) const { MDB_txn* txn; int rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - if (rc) { + if (rc != MDB_SUCCESS) { mdb_txn_abort(txn); throw Unknown(name, mdb_strerror(rc), storageName); } @@ -208,7 +210,7 @@ LMDBAL::TransactionID LMDBAL::Base::beginPrivateReadOnlyTransaction(const std::s LMDBAL::TransactionID LMDBAL::Base::beginPrivateTransaction(const std::string& storageName) const { MDB_txn* txn; int rc = mdb_txn_begin(environment, NULL, 0, &txn); - if (rc) { + if (rc != MDB_SUCCESS) { mdb_txn_abort(txn); throw Unknown(name, mdb_strerror(rc), storageName); } @@ -220,7 +222,7 @@ void LMDBAL::Base::abortPrivateTransaction(LMDBAL::TransactionID id, const std:: mdb_txn_abort(id); } -void LMDBAL::Base::commitPrivateTransaction(LMDBAL::TransactionID id, const std::string& storageName) const { +void LMDBAL::Base::commitPrivateTransaction(LMDBAL::TransactionID id, const std::string& storageName) { int rc = mdb_txn_commit(id); if (rc != MDB_SUCCESS) throw Unknown(name, mdb_strerror(rc), storageName); diff --git a/src/base.h b/src/base.h index c1110cb..349aed4 100644 --- a/src/base.h +++ b/src/base.h @@ -65,7 +65,7 @@ public: TransactionID beginReadOnlyTransaction() const; TransactionID beginTransaction() const; - void commitTransaction(TransactionID id) const; + void commitTransaction(TransactionID id); void abortTransaction(TransactionID id) const; template @@ -86,12 +86,12 @@ private: TransactionID beginReadOnlyTransaction(const std::string& storageName) const; TransactionID beginTransaction(const std::string& storageName) const; - void commitTransaction(TransactionID id, const std::string& storageName) const; + void commitTransaction(TransactionID id, const std::string& storageName); void abortTransaction(TransactionID id, const std::string& storageName) const; TransactionID beginPrivateReadOnlyTransaction(const std::string& storageName) const; TransactionID beginPrivateTransaction(const std::string& storageName) const; - void commitPrivateTransaction(TransactionID id, const std::string& storageName) const; + void commitPrivateTransaction(TransactionID id, const std::string& storageName); void abortPrivateTransaction(TransactionID id, const std::string& storageName) const; private: diff --git a/src/cache.h b/src/cache.h index 368c801..2a460b6 100644 --- a/src/cache.h +++ b/src/cache.h @@ -21,6 +21,7 @@ #include #include +#include #include "storage.h" @@ -34,33 +35,63 @@ class Cache : public Storage { size, // - know just an amount of records full // - shure that our cache is equal to the database on disk }; + + enum class Operation { + add, + remove, + change, + force, + drop, + replace, + addMany + }; + + typedef std::pair Entry; + typedef std::list Queue; + typedef std::map TransactionCache; + protected: Cache(const std::string& name, Base* parent); ~Cache() override; + virtual void transactionStarted(TransactionID txn, bool readOnly) const override; + virtual void transactionCommited(TransactionID txn) override; + virtual void transactionAborted(TransactionID txn) const override; private: void handleMode() const; + void handleTransactionEntry(const Entry& entry); + void destroyTransactionEntry(const Entry& entry) const; + + void handleAddRecord(const K& key, const V& value); + void handleRemoveRecord(const K& key); + void handleChangeRecord(const K& key, const V& value); + void handleForceRecord(const K& key, const V& value, bool added); + void handleReplaceAll(std::map* data); + void handleAddRecords(const std::map& data, bool overwrite, SizeType newSize); + void handleDrop(); + public: + using Storage::drop; + virtual int drop(TransactionID transaction) override; virtual void addRecord(const K& key, const V& value) override; virtual bool forceRecord(const K& key, const V& value) override; virtual void changeRecord(const K& key, const V& value) override; virtual void removeRecord(const K& key) override; virtual bool checkRecord(const K& key) const override; virtual V getRecord(const K& key) const override; - virtual uint32_t count() const override; + virtual SizeType count() const override; - using Storage::drop; - virtual int drop(MDB_txn * transaction) override; virtual std::map readAll() const override; virtual void replaceAll(const std::map& data) override; - virtual uint32_t addRecords(const std::map& data, bool overwrite = false) override; + virtual SizeType addRecords(const std::map& data, bool overwrite = false) override; protected: Mode* mode; std::map* cache; std::set* abscent; - uint32_t* sizeDifference; + SizeType* sizeDifference; + TransactionCache* transactionCache; }; } diff --git a/src/cache.hpp b/src/cache.hpp index 4838ee6..d92b0f6 100644 --- a/src/cache.hpp +++ b/src/cache.hpp @@ -28,7 +28,8 @@ LMDBAL::Cache::Cache(const std::string& p_name, Base* parent): mode(new Mode), cache(new std::map()), abscent(new std::set()), - sizeDifference(new uint32_t) + sizeDifference(new uint32_t), + transactionCache(new TransactionCache) { *mode = Mode::nothing; *sizeDifference = 0; @@ -36,6 +37,7 @@ LMDBAL::Cache::Cache(const std::string& p_name, Base* parent): template LMDBAL::Cache::~Cache() { + delete transactionCache; delete sizeDifference; delete mode; delete cache; @@ -50,6 +52,11 @@ void LMDBAL::Cache::addRecord(const K& key, const V& value) { iStorage::throwDuplicate(iStorage::toString(key)); Storage::addRecord(key, value); + handleAddRecord(key, value); +} + +template +void LMDBAL::Cache::handleAddRecord(const K& key, const V& value) { cache->insert(std::make_pair(key, value)); if (*mode != Mode::full) @@ -61,6 +68,13 @@ bool LMDBAL::Cache::forceRecord(const K& key, const V& value) { iStorage::ensureOpened(iStorage::forceRecordMethodName); bool added = Storage::forceRecord(key, value); + handleForceRecord(key, value, added); + + return added; +} + +template +void LMDBAL::Cache::handleForceRecord(const K& key, const V& value, bool added) { if (*mode == Mode::full) { (*cache)[key] = value; } else { @@ -73,8 +87,6 @@ bool LMDBAL::Cache::forceRecord(const K& key, const V& value) { else if (!added) //this way database had value but cache didn't, so, need to decrease sizeDifference handleMode(); } - - return added; } template @@ -106,6 +118,19 @@ void LMDBAL::Cache::changeRecord(const K& key, const V& value) { } } +template +void LMDBAL::Cache::handleChangeRecord(const K& key, const V& value) { + if (*mode == Mode::full) { + cache->at(key) = value; + } else { + typename std::pair::iterator, bool> res = cache->insert(std::make_pair(key, value)); + if (!res.second) + res.first->second = value; + else + handleMode(); + } +} + template V LMDBAL::Cache::getRecord(const K& key) const { iStorage::ensureOpened(iStorage::getRecordMethodName); @@ -181,9 +206,27 @@ void LMDBAL::Cache::replaceAll(const std::map& data) { } template -uint32_t LMDBAL::Cache::addRecords(const std::map& data, bool overwrite) { - uint32_t newSize = Storage::addRecords(data, overwrite); +void LMDBAL::Cache::handleReplaceAll(std::map* data) { + delete cache; + cache = data; + if (*mode != Mode::full) { + *mode = Mode::full; + abscent->clear(); + *sizeDifference = 0; + } +} + +template +LMDBAL::SizeType LMDBAL::Cache::addRecords(const std::map& data, bool overwrite) { + SizeType newSize = Storage::addRecords(data, overwrite); + handleAddRecords(data, overwrite, newSize); + + return newSize; +} + +template +void LMDBAL::Cache::handleAddRecords(const std::map& data, bool overwrite, SizeType newSize) { Mode& m = *mode; if (m == Mode::nothing) m = Mode::size; @@ -207,19 +250,27 @@ uint32_t LMDBAL::Cache::addRecords(const std::map& data, bool overwr 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) + bool noKey = false; + if (*mode != Mode::full) + noKey = cache->count(key) == 0; + else + noKey = abscent->count(key) > 0; + + if (noKey) iStorage::throwNotFound(iStorage::toString(key)); Storage::removeRecord(key); + handleRemoveRecord(key); +} + +template +void LMDBAL::Cache::handleRemoveRecord(const K& key) { if (cache->erase(key) == 0) //if it was not cached and we are now in size mode then the sizeDifference would decrease handleMode(); @@ -263,13 +314,120 @@ void LMDBAL::Cache::handleMode() const { } template -int LMDBAL::Cache::drop(MDB_txn * transaction) { +int LMDBAL::Cache::drop(TransactionID transaction) { int res = Storage::drop(transaction); + + transactionCache->at(transaction).emplace_back(Operation::drop, nullptr); + + return res; +} + +template +void LMDBAL::Cache::handleDrop() { cache->clear(); abscent->clear(); *mode = Mode::full; *sizeDifference = 0; - return res; +} + +template +void LMDBAL::Cache::transactionStarted(TransactionID txn, bool readOnly) const { + if (!readOnly) + transactionCache->emplace(txn, Queue()); +} + +template +void LMDBAL::Cache::transactionCommited(TransactionID txn) { + typename TransactionCache::iterator itr = transactionCache->find(txn); + if (itr != transactionCache->end()) { + Queue& queue = itr->second; + for (const Entry& entry : queue) + handleTransactionEntry(entry); + + transactionCache->erase(itr); + } +} + +template +void LMDBAL::Cache::transactionAborted(TransactionID txn) const { + typename TransactionCache::iterator itr = transactionCache->find(txn); + if (itr != transactionCache->end()) { + Queue& queue = itr->second; + for (const Entry& entry : queue) + destroyTransactionEntry(entry); + + transactionCache->erase(itr); + } +} + +template +void LMDBAL::Cache::handleTransactionEntry(const Entry& entry) { + switch (entry.first) { + case Operation::add: { + std::pair* pair = static_cast*>(entry.second); + handleAddRecord(pair->first, pair->second); + delete pair; + } + break; + case Operation::remove: { + K* key = static_cast(entry.second); + handleRemoveRecord(*key); + delete key; + } + + break; + case Operation::change: { + std::pair* pair = static_cast*>(entry.second); + handleChangeRecord(pair->first, pair->second); + delete pair; + } + case Operation::force: { + std::tuple* tuple = static_cast*>(entry.second); + const std::tuple& t = *tuple; + handleForceRecord(std::get<1>(t), std::get<2>(t), std::get<0>(t)); + delete tuple; + } + break; + case Operation::drop: + handleDrop(); + break; + case Operation::replace: + handleReplaceAll(static_cast*>(entry.second)); //I take ownership, no need to delete + break; + case Operation::addMany: { + std::tuple>* tuple = static_cast>*>(entry.second); + const std::tuple>& t = * tuple; + handleAddRecords(std::get<2>(t), std::get<0>(t), std::get<1>(t)); + delete tuple; + } + break; + } +} + +template +void LMDBAL::Cache::destroyTransactionEntry(const Entry& entry) const { + switch (entry.first) { + case Operation::add: + delete static_cast*>(entry.second); + break; + case Operation::remove: + delete static_cast(entry.second); + break; + case Operation::change: + delete static_cast*>(entry.second); + break; + case Operation::force: + delete static_cast*>(entry.second); + break; + case Operation::drop: + break; + case Operation::replace: + delete static_cast*>(entry.second); + break; + case Operation::addMany: + delete static_cast>*>(entry.second); + break; + } } #endif //LMDBAL_CACHE_HPP diff --git a/src/storage.cpp b/src/storage.cpp index 8768654..5949bc7 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -31,31 +31,20 @@ LMDBAL::iStorage::~iStorage() {} void LMDBAL::iStorage::drop() { ensureOpened(dropMethodName); - MDB_txn *txn; - int rc = mdb_txn_begin(db->environment, NULL, 0, &txn); - if (rc) { - mdb_txn_abort(txn); - throw Unknown(db->name, mdb_strerror(rc), name); - } - rc = drop(txn); - if (rc) { - mdb_txn_abort(txn); + TransactionID txn = db->beginTransaction(); + int rc = drop(txn); + if (rc != MDB_SUCCESS) { + abortTransaction(txn); throw Unknown(db->name, mdb_strerror(rc), name); } - mdb_txn_commit(txn); + db->commitTransaction(txn); } -int LMDBAL::iStorage::drop(MDB_txn* transaction) { +int LMDBAL::iStorage::drop(TransactionID transaction) { return mdb_drop(transaction, dbi, 0); } -const std::string & LMDBAL::iStorage::dbName() const { - return db->name;} - -bool LMDBAL::iStorage::isDBOpened() const { - return db->opened;} - void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const { if (!db->opened) throw Closed(methodName, db->name, name); @@ -80,10 +69,8 @@ LMDBAL::SizeType LMDBAL::iStorage::count() const { LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const { MDB_stat stat; int rc = mdb_stat(txn, dbi, &stat); - if (rc) { - mdb_txn_abort(txn); + if (rc != MDB_SUCCESS) throw Unknown(db->name, mdb_strerror(rc), name); - } return stat.ms_entries; } @@ -117,6 +104,12 @@ void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const { throwUnknown(rc); } +const std::string & LMDBAL::iStorage::dbName() const { + return db->name;} + +bool LMDBAL::iStorage::isDBOpened() const { + return db->opened;} + void LMDBAL::iStorage::throwUnknown(int rc) const { throw Unknown(db->name, mdb_strerror(rc), name);} @@ -135,19 +128,18 @@ LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const { void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const { db->abortPrivateTransaction(id, name);} -void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) const { +void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) { db->commitPrivateTransaction(id, name);} void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const { UNUSED(txn); UNUSED(readOnly); } -void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) const { - UNUSED(txn); -} +void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) { + UNUSED(txn);} + void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const { - UNUSED(txn); -} + UNUSED(txn);} diff --git a/src/storage.h b/src/storage.h index 3986c20..eb3a808 100644 --- a/src/storage.h +++ b/src/storage.h @@ -35,7 +35,6 @@ protected: virtual ~iStorage(); virtual int createStorage(MDB_txn * transaction) = 0; - virtual int drop(MDB_txn * transaction); bool isDBOpened() const; const std::string& dbName() const; @@ -52,14 +51,15 @@ protected: TransactionID beginReadOnlyTransaction() const; TransactionID beginTransaction() const; - void commitTransaction(TransactionID id) const; + void commitTransaction(TransactionID id); void abortTransaction(TransactionID id) const; - void transactionStarted(TransactionID txn, bool readOnly) const; - void transactionCommited(TransactionID txn) const; - void transactionAborted(TransactionID txn) const; + virtual void transactionStarted(TransactionID txn, bool readOnly) const; + virtual void transactionCommited(TransactionID txn); + virtual void transactionAborted(TransactionID txn) const; public: virtual void drop(); + virtual int drop(TransactionID transaction); virtual SizeType count() const; virtual SizeType count(TransactionID txn) const;