diff --git a/CMakeLists.txt b/CMakeLists.txt index 81c12f8..21199ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(LMDBAL - VERSION 0.3.1 + VERSION 0.4.0 DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer" LANGUAGES CXX ) diff --git a/src/cache.h b/src/cache.h index e6342cf..41b3e06 100644 --- a/src/cache.h +++ b/src/cache.h @@ -57,6 +57,9 @@ protected: virtual void transactionStarted(TransactionID txn, bool readOnly) const override; virtual void transactionCommited(TransactionID txn) override; virtual void transactionAborted(TransactionID txn) const override; + + virtual void discoveredRecord(const K& key, const V& value) const override; + virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const override; private: void handleMode() const; @@ -70,6 +73,7 @@ private: void handleReplaceAll(std::map* data); void handleAddRecords(const std::map& data, bool overwrite, SizeType newSize); void handleDrop(); + void appendToCache(const K& key, const V& value) const; public: using Storage::drop; diff --git a/src/cache.hpp b/src/cache.hpp index 6563d60..c82820d 100644 --- a/src/cache.hpp +++ b/src/cache.hpp @@ -235,8 +235,7 @@ void LMDBAL::Cache::getRecord(const K& key, V& out) const { try { Storage::getRecord(key, out); - cache->insert(std::make_pair(key, out)); - handleMode(); + appendToCache(key, out); return; } catch (const NotFound& error) { if (mode != Mode::full) @@ -337,10 +336,9 @@ void LMDBAL::Cache::getRecord(const K& key, V& out, TransactionID txn) con try { Storage::getRecord(key, out, txn); - if (!currentTransaction) { - cache->insert(std::make_pair(key, out)); - handleMode(); - } + if (!currentTransaction) + appendToCache(key, out); + return; } catch (const NotFound& error) { if (!currentTransaction && mode != Mode::full) @@ -350,6 +348,18 @@ void LMDBAL::Cache::getRecord(const K& key, V& out, TransactionID txn) con } } +template +void LMDBAL::Cache::discoveredRecord(const K& key, const V& value) const { + appendToCache(key, value); +} + +template +void LMDBAL::Cache::discoveredRecord(const K& key, const V& value, TransactionID txn) const { + typename TransactionCache::const_iterator tc = transactionCache->find(txn); + if (tc == transactionCache->end()) //there is a way to look though all the records in transaction log and cache the new pair + discoveredRecord(key, value); //if there is nothing in transaction log about it, but it seems like too much for a small gain +} + template bool LMDBAL::Cache::checkRecord(const K& key) const { iStorage::ensureOpened(iStorage::checkRecordMethodName); @@ -363,8 +373,8 @@ bool LMDBAL::Cache::checkRecord(const K& key) const { try { V value = Storage::getRecord(key); - cache->insert(std::make_pair(key, value)); - handleMode(); + appendToCache(key, value); + return true; } catch (const NotFound& error) { if (mode != Mode::full) @@ -438,10 +448,9 @@ bool LMDBAL::Cache::checkRecord(const K& key, TransactionID txn) const { try { V value = Storage::getRecord(key, txn); - if (!currentTransaction) { - cache->insert(std::make_pair(key, value)); - handleMode(); - } + if (!currentTransaction) + appendToCache(key, value); + return true; } catch (const NotFound& error) { if (!currentTransaction && mode != Mode::full) @@ -451,6 +460,13 @@ bool LMDBAL::Cache::checkRecord(const K& key, TransactionID txn) const { } } +template +void LMDBAL::Cache::appendToCache(const K& key, const V& value) const { + typename std::pair::const_iterator, bool> pair = cache->insert(std::make_pair(key, value)); + if (pair.second) + handleMode(); +} + template std::map LMDBAL::Cache::readAll() const { iStorage::ensureOpened(iStorage::readAllMethodName); diff --git a/test/storagecursor.cpp b/test/storagecursor.cpp index 8a08724..513c9e0 100644 --- a/test/storagecursor.cpp +++ b/test/storagecursor.cpp @@ -34,6 +34,7 @@ protected: static LMDBAL::Base* db; static LMDBAL::Cursor* cursor; static LMDBAL::Cursor* emptyCursor; + static LMDBAL::TransactionID transaction; LMDBAL::Storage* table; LMDBAL::Storage* emptyTable; @@ -42,6 +43,7 @@ protected: LMDBAL::Base* StorageCursorTest::db = nullptr; LMDBAL::Cursor* StorageCursorTest::cursor = nullptr; LMDBAL::Cursor* StorageCursorTest::emptyCursor = nullptr; +LMDBAL::TransactionID StorageCursorTest::transaction = nullptr; static const std::map data({ {245665783, "bothering nerds"}, @@ -167,9 +169,106 @@ TEST_F(StorageCursorTest, Destruction) { EXPECT_THROW(cursor->next(), LMDBAL::CursorNotReady); EXPECT_THROW(cursor->prev(), LMDBAL::CursorNotReady); EXPECT_THROW(cursor->current(), LMDBAL::CursorNotReady); + + EXPECT_THROW(emptyTable->destroyCursor(cursor), LMDBAL::Unknown); + table->destroyCursor(cursor); + + cursor = table->createCursor(); +} + +TEST_F(StorageCursorTest, FirstPublic) { + transaction = db->beginReadOnlyTransaction(); + + cursor->open(transaction); + std::pair element = cursor->first(); + std::map::const_iterator reference = data.begin(); + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); +} + +TEST_F(StorageCursorTest, NextPublic) { + std::map::const_iterator reference = data.begin(); + + reference++; + for (; reference != data.end(); ++reference) { + std::pair element = cursor->next(); + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); + } + + EXPECT_THROW(cursor->next(), LMDBAL::NotFound); + + std::pair element = cursor->first(); + reference = data.begin(); + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); +} + +TEST_F(StorageCursorTest, LastPublic) { + std::pair element = cursor->last(); + std::map::const_reverse_iterator reference = data.rbegin(); + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); +} + +TEST_F(StorageCursorTest, PrevPublic) { + std::map::const_reverse_iterator reference = data.rbegin(); + + reference++; + for (; reference != data.rend(); ++reference) { + std::pair element = cursor->prev(); + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); + } + + EXPECT_THROW(cursor->prev(), LMDBAL::NotFound); + + std::pair element = cursor->last(); + reference = data.rbegin(); + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); +} + +TEST_F(StorageCursorTest, CurrentPublic) { + std::pair element = cursor->first(); + std::map::const_iterator reference = data.begin(); + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); + + element = cursor->current(); + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); + + cursor->next(); + element = cursor->current(); + ++reference; + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); + + cursor->next(); + cursor->next(); + cursor->prev(); + element = cursor->current(); + ++reference; + ++reference; + --reference; + + EXPECT_EQ(element.first, reference->first); + EXPECT_EQ(element.second, reference->second); } TEST_F(StorageCursorTest, CornerCases) { + db->abortTransaction(transaction); + EXPECT_THROW(cursor->current(), LMDBAL::Unknown); + cursor->close(); + emptyCursor->open(); EXPECT_THROW(emptyCursor->first(), LMDBAL::NotFound); EXPECT_THROW(emptyCursor->last(), LMDBAL::NotFound);