RAII transactions
Main LMDBAL workfow / Archlinux (push) Successful in 40s Details

This commit is contained in:
Blue 2023-10-17 18:06:11 -03:00
parent a9aa6b549f
commit de741eda21
Signed by: blue
GPG Key ID: 9B203B252A63EE38
22 changed files with 689 additions and 222 deletions

View File

@ -1,5 +1,10 @@
# Changelog # Changelog
## LMDBAL 0.5.1 (UNRELEASED)
### Improvements
- RAII transactions
- reduced overhead for private transaction finctions
## LMDBAL 0.5.0 (October 15, 2023) ## LMDBAL 0.5.0 (October 15, 2023)
### New Features ### New Features
- duplicates support (only for table) - duplicates support (only for table)

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(LMDBAL project(LMDBAL
VERSION 0.5.0 VERSION 0.5.1
DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer" DESCRIPTION "LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer"
LANGUAGES CXX LANGUAGES CXX
) )

View File

@ -1,6 +1,6 @@
# Maintainer: Yury Gubich <blue@macaw.me> # Maintainer: Yury Gubich <blue@macaw.me>
pkgname=lmdbal pkgname=lmdbal
pkgver=0.5.0 pkgver=0.5.1
pkgrel=1 pkgrel=1
pkgdesc="LMDB Abstraction Layer, qt5 version" pkgdesc="LMDB Abstraction Layer, qt5 version"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
@ -11,7 +11,7 @@ makedepends=('cmake>=3.16' 'gcc')
optdepends=() optdepends=()
source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz")
sha256sums=('df1a9687d81d609d160754285f2613d7e07fc6deb781d0fb0084e4857ea82e95') sha256sums=('SKIP')
build() { build() {
cd "$srcdir/$pkgname" cd "$srcdir/$pkgname"
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release -D QT_VERSION_MAJOR=5 cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release -D QT_VERSION_MAJOR=5

View File

@ -2,6 +2,7 @@ set(SOURCES
exceptions.cpp exceptions.cpp
storage.cpp storage.cpp
base.cpp base.cpp
transaction.cpp
) )
set(HEADERS set(HEADERS
@ -29,11 +30,9 @@ set(HEADERS
serializer_qstring.hpp serializer_qstring.hpp
serializer_qbytearray.hpp serializer_qbytearray.hpp
operators.hpp operators.hpp
transaction.h
) )
target_sources(${PROJECT_NAME} PRIVATE target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
${SOURCES}
${HEADERS}
)
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW}) install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})

View File

@ -19,6 +19,7 @@
#include "base.h" #include "base.h"
#include "exceptions.h" #include "exceptions.h"
#include "storage.h" #include "storage.h"
#include "transaction.h"
#define UNUSED(x) (void)(x) #define UNUSED(x) (void)(x)
@ -120,8 +121,7 @@ bool LMDBAL::Base::removeDirectory() {
if (opened) if (opened)
throw Opened(name, "remove database directory"); throw Opened(name, "remove database directory");
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); QString path = getPath();
path += "/" + getName();
QDir cache(path); QDir cache(path);
if (cache.exists()) if (cache.exists())
@ -148,10 +148,8 @@ QString LMDBAL::Base::createDirectory() {
if (opened) if (opened)
throw Opened(name, "create database directory"); throw Opened(name, "create database directory");
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); QString path = getPath();
path += "/" + getName();
QDir cache(path); QDir cache(path);
if (!cache.exists()) { if (!cache.exists()) {
bool res = cache.mkpath(path); bool res = cache.mkpath(path);
if (!res) if (!res)
@ -169,6 +167,17 @@ QString LMDBAL::Base::createDirectory() {
QString LMDBAL::Base::getName() const { QString LMDBAL::Base::getName() const {
return QString::fromStdString(name);} return QString::fromStdString(name);}
/**
* \brief Returns database name
*
* \returns database path
*/
QString LMDBAL::Base::getPath() const {
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + getName();
return path;
}
/** /**
* \brief Returns database state * \brief Returns database state
@ -190,38 +199,45 @@ void LMDBAL::Base::drop() {
if (!opened) if (!opened)
throw Closed("drop", name); throw Closed("drop", name);
TransactionID txn = beginTransaction(); TransactionID txn = beginPrivateTransaction(emptyName);
for (const std::pair<const std::string, iStorage*>& pair : storages) { for (const std::pair<const std::string, iStorage*>& pair : storages) {
int rc = pair.second->drop(txn); int rc = pair.second->drop(txn);
if (rc != MDB_SUCCESS) { if (rc != MDB_SUCCESS) {
abortTransaction(txn); abortPrivateTransaction(txn, emptyName);
throw Unknown(name, mdb_strerror(rc), pair.first); throw Unknown(name, mdb_strerror(rc), pair.first);
} }
} }
commitTransaction(txn);
commitPrivateTransaction(txn, emptyName);
for (const std::pair<const std::string, iStorage*>& pair : storages)
pair.second->handleDrop();
} }
/** /**
* \brief Begins read-only transaction * \brief Begins read-only transaction
* *
* \returns read-only transaction ID * \returns read-only transaction
* *
* \exception LMDBAL::Closed - thrown if the database is closed * \exception LMDBAL::Closed - thrown if the database is closed
* \exception LMDBAL::Unknown - thrown if something unexpected happened * \exception LMDBAL::Unknown - thrown if something unexpected happened
*/ */
LMDBAL::TransactionID LMDBAL::Base::beginReadOnlyTransaction() const { LMDBAL::Transaction LMDBAL::Base::beginReadOnlyTransaction() const {
return beginReadOnlyTransaction(emptyName);} TransactionID id = beginReadOnlyTransaction(emptyName);
return Transaction(id, this);
}
/** /**
* \brief Begins writable transaction * \brief Begins writable transaction
* *
* \returns writable transaction ID * \returns writable transaction
* *
* \exception LMDBAL::Closed - thrown if the database is closed * \exception LMDBAL::Closed - thrown if the database is closed
* \exception LMDBAL::Unknown - thrown if something unexpected happened * \exception LMDBAL::Unknown - thrown if something unexpected happened
*/ */
LMDBAL::TransactionID LMDBAL::Base::beginTransaction() const { LMDBAL::WriteTransaction LMDBAL::Base::beginTransaction() {
return beginTransaction(emptyName);} TransactionID id = beginTransaction(emptyName);
return WriteTransaction(id, this);
}
/** /**
* \brief Aborts transaction * \brief Aborts transaction

View File

@ -36,6 +36,8 @@
namespace LMDBAL { namespace LMDBAL {
class iStorage; class iStorage;
class Transaction;
class WriteTransaction;
template<class T> template<class T>
class Serializer; class Serializer;
@ -51,6 +53,8 @@ typedef uint32_t SizeType; /**<\brief All LMDBAL si
class Base { class Base {
friend class iStorage; friend class iStorage;
friend class Transaction;
friend class WriteTransaction;
public: public:
Base(const QString& name, uint16_t mapSize = 10); Base(const QString& name, uint16_t mapSize = 10);
@ -62,12 +66,11 @@ public:
bool removeDirectory(); bool removeDirectory();
QString createDirectory(); QString createDirectory();
QString getName() const; QString getName() const;
QString getPath() const;
void drop(); void drop();
TransactionID beginReadOnlyTransaction() const; Transaction beginReadOnlyTransaction() const;
TransactionID beginTransaction() const; WriteTransaction beginTransaction();
void commitTransaction(TransactionID id);
void abortTransaction(TransactionID id) const;
template <class K, class V> template <class K, class V>
LMDBAL::Storage<K, V>* addStorage(const std::string& storageName, bool duplicates = false); LMDBAL::Storage<K, V>* addStorage(const std::string& storageName, bool duplicates = false);
@ -85,6 +88,8 @@ private:
typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/ typedef std::map<std::string, LMDBAL::iStorage*> Storages; /**<\brief Storage and Cache pointers are saved in the std::map*/
typedef std::set<TransactionID> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/ typedef std::set<TransactionID> Transactions; /**<\brief Piblic transaction IDs are saved in the std::set*/
void commitTransaction(TransactionID id);
void abortTransaction(TransactionID id) const;
TransactionID beginReadOnlyTransaction(const std::string& storageName) const; TransactionID beginReadOnlyTransaction(const std::string& storageName) const;
TransactionID beginTransaction(const std::string& storageName) const; TransactionID beginTransaction(const std::string& storageName) const;
void commitTransaction(TransactionID id, const std::string& storageName); void commitTransaction(TransactionID id, const std::string& storageName);

View File

@ -54,12 +54,27 @@ protected:
Cache(Base* parent, const std::string& name, bool duplicates = false); Cache(Base* parent, const std::string& name, bool duplicates = false);
~Cache() override; ~Cache() override;
virtual void handleDrop() override;
virtual void transactionStarted(TransactionID txn, bool readOnly) const override; virtual void transactionStarted(TransactionID txn, bool readOnly) const override;
virtual void transactionCommited(TransactionID txn) override; virtual void transactionCommited(TransactionID txn) override;
virtual void transactionAborted(TransactionID txn) const 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) const override;
virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const override; virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const override;
virtual SizeType count(TransactionID txn) const override;
virtual void addRecord(const K& key, const V& value, TransactionID txn) override;
virtual bool forceRecord(const K& key, const V& value, TransactionID txn) override;
virtual void changeRecord(const K& key, const V& value, TransactionID txn) override;
virtual void removeRecord(const K& key, TransactionID txn) override;
virtual bool checkRecord(const K& key, TransactionID txn) const override;
virtual void getRecord(const K& key, V& out, TransactionID txn) const override;
virtual V getRecord(const K& key, TransactionID txn) const override;
virtual std::map<K, V> readAll(TransactionID txn) const override;
virtual void readAll(std::map<K, V>& out, TransactionID txn) const override;
virtual void replaceAll(const std::map<K, V>& data, TransactionID txn) override;
virtual SizeType addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false) override;
private: private:
void handleMode() const; void handleMode() const;
@ -72,37 +87,34 @@ private:
void handleForceRecord(const K& key, const V& value, bool added); void handleForceRecord(const K& key, const V& value, bool added);
void handleReplaceAll(std::map<K, V>* data); void handleReplaceAll(std::map<K, V>* data);
void handleAddRecords(const std::map<K, V>& data, bool overwrite, SizeType newSize); void handleAddRecords(const std::map<K, V>& data, bool overwrite, SizeType newSize);
void handleDrop();
void appendToCache(const K& key, const V& value) const; void appendToCache(const K& key, const V& value) const;
public: public:
using Storage<K, V>::drop; using Storage<K, V>::drop;
virtual int drop(TransactionID transaction) override; using Storage<K, V>::addRecord;
using Storage<K, V>::forceRecord;
using Storage<K, V>::changeRecord;
using Storage<K, V>::removeRecord;
using Storage<K, V>::checkRecord;
using Storage<K, V>::getRecord;
using Storage<K, V>::readAll;
using Storage<K, V>::replaceAll;
using Storage<K, V>::addRecords;
using Storage<K, V>::count;
virtual int drop(const WriteTransaction& transaction) override;
virtual void addRecord(const K& key, const V& value) override; virtual void addRecord(const K& key, const V& value) override;
virtual void addRecord(const K& key, const V& value, TransactionID txn) override;
virtual bool forceRecord(const K& key, const V& value) override; virtual bool forceRecord(const K& key, const V& value) override;
virtual bool forceRecord(const K& key, const V& value, TransactionID txn) override;
virtual void changeRecord(const K& key, const V& value) override; virtual void changeRecord(const K& key, const V& value) override;
virtual void changeRecord(const K& key, const V& value, TransactionID txn) override;
virtual void removeRecord(const K& key) override; virtual void removeRecord(const K& key) override;
virtual void removeRecord(const K& key, TransactionID txn) override;
virtual bool checkRecord(const K& key) const override; virtual bool checkRecord(const K& key) const override;
virtual bool checkRecord(const K& key, TransactionID txn) const override;
virtual void getRecord(const K& key, V& out) const override; virtual void getRecord(const K& key, V& out) const override;
virtual void getRecord(const K& key, V& out, TransactionID txn) const override;
virtual V getRecord(const K& key) const override; virtual V getRecord(const K& key) const override;
virtual V getRecord(const K& key, TransactionID txn) const override;
virtual SizeType count() const override; virtual SizeType count() const override;
virtual SizeType count(TransactionID txn) const override;
virtual std::map<K, V> readAll() const override; virtual std::map<K, V> readAll() const override;
virtual std::map<K, V> readAll(TransactionID txn) const override;
virtual void readAll(std::map<K, V>& out) const override; virtual void readAll(std::map<K, V>& out) const override;
virtual void readAll(std::map<K, V>& out, TransactionID txn) const override;
virtual void replaceAll(const std::map<K, V>& data) override; virtual void replaceAll(const std::map<K, V>& data) override;
virtual void replaceAll(const std::map<K, V>& data, TransactionID txn) override;
virtual SizeType addRecords(const std::map<K, V>& data, bool overwrite = false) override; virtual SizeType addRecords(const std::map<K, V>& data, bool overwrite = false) override;
virtual SizeType addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false) override;
protected: protected:
/** /**

View File

@ -75,8 +75,6 @@ void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
template<class K, class V> template<class K, class V>
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) { void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
iStorage::ensureOpened(iStorage::addRecordMethodName);
if (cache->count(key) > 0) if (cache->count(key) > 0)
iStorage::throwDuplicate(iStorage::toString(key)); iStorage::throwDuplicate(iStorage::toString(key));
@ -109,8 +107,6 @@ bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value) {
template<class K, class V> template<class K, class V>
bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) { bool LMDBAL::Cache<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) {
iStorage::ensureOpened(iStorage::forceRecordMethodName);
bool added = Storage<K, V>::forceRecord(key, value, txn); bool added = Storage<K, V>::forceRecord(key, value, txn);
typename TransactionCache::iterator tc = transactionCache->find(txn); typename TransactionCache::iterator tc = transactionCache->find(txn);
@ -171,8 +167,6 @@ void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value) {
template<class K, class V> template<class K, class V>
void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) { void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
iStorage::ensureOpened(iStorage::changeRecordMethodName);
if (mode == Mode::full) { if (mode == Mode::full) {
typename std::map<K, V>::iterator itr = cache->find(key); typename std::map<K, V>::iterator itr = cache->find(key);
if (itr == cache->end()) if (itr == cache->end())
@ -248,8 +242,6 @@ void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out) const {
template<class K, class V> template<class K, class V>
V LMDBAL::Cache<K, V>::getRecord(const K& key, TransactionID txn) const { V LMDBAL::Cache<K, V>::getRecord(const K& key, TransactionID txn) const {
iStorage::ensureOpened(iStorage::getRecordMethodName);
V value; V value;
Cache<K, V>::getRecord(key, value, txn); Cache<K, V>::getRecord(key, value, txn);
return value; return value;
@ -257,8 +249,6 @@ V LMDBAL::Cache<K, V>::getRecord(const K& key, TransactionID txn) const {
template<class K, class V> template<class K, class V>
void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) const { void LMDBAL::Cache<K, V>::getRecord(const K& key, V& out, TransactionID txn) const {
iStorage::ensureOpened(iStorage::getRecordMethodName);
//if there are any changes made within this transaction //if there are any changes made within this transaction
//I will be able to see them among pending changes //I will be able to see them among pending changes
//so, I'm going to go through them in reverse order //so, I'm going to go through them in reverse order
@ -387,8 +377,6 @@ bool LMDBAL::Cache<K, V>::checkRecord(const K& key) const {
template<class K, class V> template<class K, class V>
bool LMDBAL::Cache<K, V>::checkRecord(const K& key, TransactionID txn) const { bool LMDBAL::Cache<K, V>::checkRecord(const K& key, TransactionID txn) const {
iStorage::ensureOpened(iStorage::checkRecordMethodName);
//if there are any changes made within this transaction //if there are any changes made within this transaction
//I will be able to see them among pending changes //I will be able to see them among pending changes
//so, I'm going to go through them in reverse order //so, I'm going to go through them in reverse order
@ -497,8 +485,6 @@ void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out) const {
template<class K, class V> template<class K, class V>
std::map<K, V> LMDBAL::Cache<K, V>::readAll(TransactionID txn) const { std::map<K, V> LMDBAL::Cache<K, V>::readAll(TransactionID txn) const {
iStorage::ensureOpened(iStorage::readAllMethodName);
std::map<K, V> out; std::map<K, V> out;
readAll(out, txn); readAll(out, txn);
@ -507,8 +493,6 @@ std::map<K, V> LMDBAL::Cache<K, V>::readAll(TransactionID txn) const {
template<class K, class V> template<class K, class V>
void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out, TransactionID txn) const { void LMDBAL::Cache<K, V>::readAll(std::map<K, V>& out, TransactionID txn) const {
iStorage::ensureOpened(iStorage::readAllMethodName);
typename TransactionCache::iterator tc = transactionCache->find(txn); typename TransactionCache::iterator tc = transactionCache->find(txn);
if (tc != transactionCache->end()) { if (tc != transactionCache->end()) {
Queue& queue = tc->second; Queue& queue = tc->second;
@ -676,8 +660,6 @@ void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
template<class K, class V> template<class K, class V>
void LMDBAL::Cache<K, V>::removeRecord(const K& key, TransactionID txn) { void LMDBAL::Cache<K, V>::removeRecord(const K& key, TransactionID txn) {
iStorage::ensureOpened(iStorage::removeRecordMethodName);
bool noKey = false; bool noKey = false;
if (mode != Mode::full) if (mode != Mode::full)
noKey = cache->count(key) == 0; noKey = cache->count(key) == 0;
@ -729,7 +711,6 @@ uint32_t LMDBAL::Cache<K, V>::count() const {
template<class K, class V> template<class K, class V>
uint32_t LMDBAL::Cache<K, V>::count(TransactionID txn) const { uint32_t LMDBAL::Cache<K, V>::count(TransactionID txn) const {
int32_t diff = 0; int32_t diff = 0;
bool currentTransaction = false; bool currentTransaction = false;
typename TransactionCache::const_iterator tc = transactionCache->find(txn); typename TransactionCache::const_iterator tc = transactionCache->find(txn);
@ -800,10 +781,15 @@ void LMDBAL::Cache<K, V>::handleMode() const {
} }
template<class K, class V> template<class K, class V>
int LMDBAL::Cache<K, V>::drop(TransactionID transaction) { int LMDBAL::Cache<K, V>::drop(const WriteTransaction& transaction) {
int res = Storage<K, V>::drop(transaction); iStorage::ensureOpened(iStorage::dropMethodName);
TransactionID txn = iStorage::extractTransactionId(transaction, iStorage::dropMethodName);
int res = Storage<K, V>::drop(txn);
typename TransactionCache::iterator tc = transactionCache->find(transaction); if (res != MDB_SUCCESS)
return res;
typename TransactionCache::iterator tc = transactionCache->find(txn);
if (tc != transactionCache->end()) if (tc != transactionCache->end())
tc->second.emplace_back(Operation::drop, nullptr); tc->second.emplace_back(Operation::drop, nullptr);

View File

@ -24,6 +24,7 @@
#include "lmdb.h" #include "lmdb.h"
#include "base.h" #include "base.h"
#include "storage.h" #include "storage.h"
#include "transaction.h"
namespace LMDBAL { namespace LMDBAL {
@ -42,9 +43,9 @@ private:
public: public:
void open() const; void open() const;
void open(TransactionID txn) const; void open(const Transaction& transaction) const;
void renew() const; void renew() const;
void renew(TransactionID txn) const; void renew(const Transaction& transaction) const;
void close() const; void close() const;
bool opened() const; bool opened() const;

View File

@ -20,6 +20,7 @@
#define LMDBAL_CURSOR_HPP #define LMDBAL_CURSOR_HPP
#include "cursor.h" #include "cursor.h"
#include <iostream>
/** /**
* \class LMDBAL::Cursor * \class LMDBAL::Cursor
@ -79,8 +80,8 @@ void LMDBAL::Cursor<K, V>::terminated () const {
* This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor! * This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor!
* It will do nothing to a cursor that was already opened (no matter what way). * It will do nothing to a cursor that was already opened (no matter what way).
* *
* \throws LMDBAL::Closed thrown if you try to open the cursor on a closed database * \exception LMDBAL::Closed thrown if you try to open the cursor on a closed database
* \throws LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb, or to begin a transaction * \exception LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb, or to begin a transaction
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::open () const { void LMDBAL::Cursor<K, V>::open () const {
@ -109,12 +110,16 @@ void LMDBAL::Cursor<K, V>::open () const {
* This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor! * This function should be called when the LMDBAL::Storage is already opened and before any query with this cursor!
* It will do nothing to a cursor that was already opened (no matter what way). * It will do nothing to a cursor that was already opened (no matter what way).
* *
* \throws LMDBAL::Closed thrown if you try to open the cursor on a closed database * \param[in] transaction - a transaction, can be read only
* \throws LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb *
* \exception LMDBAL::Closed thrown if you try to open the cursor on a closed database
* \exception LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::open (TransactionID txn) const { void LMDBAL::Cursor<K, V>::open (const Transaction& transaction) const {
storage->ensureOpened(openCursorMethodName); storage->ensureOpened(openCursorMethodName);
TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName);
switch (state) { switch (state) {
case closed: { case closed: {
int result = mdb_cursor_open(txn, storage->dbi, &cursor); int result = mdb_cursor_open(txn, storage->dbi, &cursor);
@ -141,8 +146,8 @@ void LMDBAL::Cursor<K, V>::open (TransactionID txn) const {
* *
* This function does nothing if the cursor is closed * This function does nothing if the cursor is closed
* *
* \throws LMDBAL::Closed thrown if you try to renew the cursor on a closed database * \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database
* \throws LMDBAL::Unknown thrown if there was a problem beginning new transaction or if there was a problem renewing the cursor by lmdb * \exception LMDBAL::Unknown thrown if there was a problem beginning new transaction or if there was a problem renewing the cursor by lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::renew () const { void LMDBAL::Cursor<K, V>::renew () const {
@ -183,12 +188,14 @@ void LMDBAL::Cursor<K, V>::renew () const {
* *
* \param[in] txn a transaction you wish this cursor to be bound to * \param[in] txn a transaction you wish this cursor to be bound to
* *
* \throws LMDBAL::Closed thrown if you try to renew the cursor on a closed database * \exception LMDBAL::Closed thrown if you try to renew the cursor on a closed database
* \throws LMDBAL::Unknown thrown if there was a problem renewing the cursor by lmdb * \exception LMDBAL::Unknown thrown if there was a problem renewing the cursor by lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::renew (TransactionID txn) const { void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) const {
storage->ensureOpened(renewCursorMethodName); storage->ensureOpened(renewCursorMethodName);
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
switch (state) { switch (state) {
case openedPrivate: { case openedPrivate: {
TransactionID txn = mdb_cursor_txn(cursor); TransactionID txn = mdb_cursor_txn(cursor);
@ -255,9 +262,9 @@ bool LMDBAL::Cursor<K, V>::opened () const {
* \param[out] key a reference to an object the key of queried element is going to be assigned * \param[out] key a reference to an object the key of queried element is going to be assigned
* \param[out] value a reference to an object the value of queried element is going to be assigned * \param[out] value a reference to an object the value of queried element is going to be assigned
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if there are no elements in the storage * \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::first (K& key, V& value) const { void LMDBAL::Cursor<K, V>::first (K& key, V& value) const {
@ -272,9 +279,9 @@ void LMDBAL::Cursor<K, V>::first (K& key, V& value) const {
* \param[out] key a reference to an object the key of queried element is going to be assigned * \param[out] key a reference to an object the key of queried element is going to be assigned
* \param[out] value a reference to an object the value of queried element is going to be assigned * \param[out] value a reference to an object the value of queried element is going to be assigned
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if there are no elements in the storage * \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::last (K& key, V& value) const { void LMDBAL::Cursor<K, V>::last (K& key, V& value) const {
@ -295,9 +302,9 @@ void LMDBAL::Cursor<K, V>::last (K& key, V& value) const {
* \param[out] key a reference to an object the key of queried element is going to be assigned * \param[out] key a reference to an object the key of queried element is going to be assigned
* \param[out] value a reference to an object the value of queried element is going to be assigned * \param[out] value a reference to an object the value of queried element is going to be assigned
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if the cursor already was on the last element or if there are no elements in the storage * \exception LMDBAL::NotFound thrown if the cursor already was on the last element or if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::next (K& key, V& value) const { void LMDBAL::Cursor<K, V>::next (K& key, V& value) const {
@ -318,9 +325,9 @@ void LMDBAL::Cursor<K, V>::next (K& key, V& value) const {
* \param[out] key a reference to an object the key of queried element is going to be assigned * \param[out] key a reference to an object the key of queried element is going to be assigned
* \param[out] value a reference to an object the value of queried element is going to be assigned * \param[out] value a reference to an object the value of queried element is going to be assigned
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if the cursor already was on the first element or if there are no elements in the storage * \exception LMDBAL::NotFound thrown if the cursor already was on the first element or if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::prev (K& key, V& value) const { void LMDBAL::Cursor<K, V>::prev (K& key, V& value) const {
@ -337,9 +344,9 @@ void LMDBAL::Cursor<K, V>::prev (K& key, V& value) const {
* \param[out] key a reference to an object the key of queried element is going to be assigned * \param[out] key a reference to an object the key of queried element is going to be assigned
* \param[out] value a reference to an object the value of queried element is going to be assigned * \param[out] value a reference to an object the value of queried element is going to be assigned
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound probably never thrown but there might be still some corner case I don't know about * \exception LMDBAL::NotFound probably never thrown but there might be still some corner case I don't know about
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::current (K& key, V& value) const { void LMDBAL::Cursor<K, V>::current (K& key, V& value) const {
@ -353,9 +360,9 @@ void LMDBAL::Cursor<K, V>::current (K& key, V& value) const {
* *
* \returns std::pair where first is element key and second is element value * \returns std::pair where first is element key and second is element value
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if there are no elements in the storage * \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
std::pair<K, V> LMDBAL::Cursor<K, V>::first () const { std::pair<K, V> LMDBAL::Cursor<K, V>::first () const {
@ -371,9 +378,9 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::first () const {
* *
* \returns std::pair where first is element key and second is element value * \returns std::pair where first is element key and second is element value
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if there are no elements in the storage * \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
std::pair<K, V> LMDBAL::Cursor<K, V>::last () const { std::pair<K, V> LMDBAL::Cursor<K, V>::last () const {
@ -395,9 +402,9 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::last () const {
* *
* \returns std::pair where first is element key and second is element value * \returns std::pair where first is element key and second is element value
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if the cursor already was on the last element or if there are no elements in the storage * \exception LMDBAL::NotFound thrown if the cursor already was on the last element or if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
std::pair<K, V> LMDBAL::Cursor<K, V>::next () const { std::pair<K, V> LMDBAL::Cursor<K, V>::next () const {
@ -419,9 +426,9 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::next () const {
* *
* \returns std::pair where first is element key and second is element value * \returns std::pair where first is element key and second is element value
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound thrown if the cursor already was on the first element or if there are no elements in the storage * \exception LMDBAL::NotFound thrown if the cursor already was on the first element or if there are no elements in the storage
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
std::pair<K, V> LMDBAL::Cursor<K, V>::prev () const { std::pair<K, V> LMDBAL::Cursor<K, V>::prev () const {
@ -439,9 +446,9 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::prev () const {
* *
* \returns std::pair where first is element key and second is element value * \returns std::pair where first is element key and second is element value
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound probably never thrown but there might be still some corner case I don't know about * \exception LMDBAL::NotFound probably never thrown but there might be still some corner case I don't know about
* \throws LMDBAL::Unknown thrown if there was no positioning operation before of if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown thrown if there was no positioning operation before of if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
std::pair<K, V> LMDBAL::Cursor<K, V>::current () const { std::pair<K, V> LMDBAL::Cursor<K, V>::current () const {
@ -461,9 +468,9 @@ std::pair<K, V> LMDBAL::Cursor<K, V>::current () const {
* \param[in] methodName a name of the method you called it from, just for the exception message if something goes not as expected * \param[in] methodName a name of the method you called it from, just for the exception message if something goes not as expected
* \param[in] operationName a name of the opeartion, just for the exception message if something goes not as expected * \param[in] operationName a name of the opeartion, just for the exception message if something goes not as expected
* *
* \throws LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor * \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \throws LMDBAL::NotFound mostly thrown if the query wasn't found * \exception LMDBAL::NotFound mostly thrown if the query wasn't found
* \throws LMDBAL::Unknown mostly thrown if there was some unexpected problem with lmdb * \exception LMDBAL::Unknown mostly thrown if there was some unexpected problem with lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Cursor<K, V>::operateCursorRead( void LMDBAL::Cursor<K, V>::operateCursorRead(

View File

@ -112,7 +112,6 @@ std::string LMDBAL::StorageDuplicate::getMessage() const {
+ " but the database already has a storage with given name"; + " but the database already has a storage with given name";
} }
LMDBAL::Exist::Exist( LMDBAL::Exist::Exist(
const std::string& p_key, const std::string& p_key,
const std::string& p_dbName, const std::string& p_dbName,
@ -130,6 +129,29 @@ std::string LMDBAL::Exist::getMessage() const {
+ " but it already has an element with given id"; + " but it already has an element with given id";
} }
LMDBAL::TransactionTerminated::TransactionTerminated(
const std::string& dbName,
const std::string& tableName,
const std::string& action
) :
dbName(dbName),
tableName(tableName),
action(action)
{}
std::string LMDBAL::TransactionTerminated::getMessage() const {
std::string result = "Error";
if (!action.empty())
result += " perfoming action " + action;
result += " in database " + dbName;
result += ", table " + tableName;
result += ". The transaction is already terminated";
return result;
}
LMDBAL::Unknown::Unknown( LMDBAL::Unknown::Unknown(
const std::string& p_dbName, const std::string& p_dbName,
const std::string& message, const std::string& message,

View File

@ -177,6 +177,27 @@ private:
std::string tableName; std::string tableName;
}; };
/**
* Thrown if there was an attempt to perform action using terminated transaction
*/
class TransactionTerminated : public Exception {
public:
/**
* \brief Creates exception
*
* \param dbName - name of the database
* \param tableName - name of the storage that was operated with
* \param action - optional action, just to enrich the exception message
*/
TransactionTerminated(const std::string& dbName, const std::string& tableName, const std::string& action = "");
std::string getMessage() const;
private:
std::string dbName;
std::string tableName;
std::string action;
};
/** /**
* \brief Thrown if something unexpected happened * \brief Thrown if something unexpected happened
*/ */

View File

@ -56,6 +56,23 @@ void LMDBAL::iStorage::close() {
mdb_dbi_close(db->environment, dbi); mdb_dbi_close(db->environment, dbi);
} }
/**
* \brief Checks if the transaction is still active, returns inner TransactionID
*
* This method is for internal usage only
*
* \param[in] txn - a transaction, you want to extract ID from
* \param[in] action - a description of what you're going to do, in case of exception the error description will be verbose
* \returns - Transaction inner TransactionID
*
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction& txn, const std::string& action) const {
if (!txn.isActive())
throw TransactionTerminated(db->name, name, action);
return txn.txn;
}
/** /**
* \brief Drops content of a storage interface * \brief Drops content of a storage interface
@ -68,7 +85,7 @@ void LMDBAL::iStorage::close() {
void LMDBAL::iStorage::drop() { void LMDBAL::iStorage::drop() {
ensureOpened(dropMethodName); ensureOpened(dropMethodName);
TransactionID txn = db->beginTransaction(); TransactionID txn = beginTransaction();
int rc = iStorage::drop(txn); int rc = iStorage::drop(txn);
if (rc != MDB_SUCCESS) { if (rc != MDB_SUCCESS) {
abortTransaction(txn); abortTransaction(txn);
@ -76,6 +93,7 @@ void LMDBAL::iStorage::drop() {
} }
db->commitTransaction(txn); db->commitTransaction(txn);
handleDrop();
} }
/** /**
@ -90,6 +108,21 @@ int LMDBAL::iStorage::drop(TransactionID transaction) {
return mdb_drop(transaction, dbi, 0); return mdb_drop(transaction, dbi, 0);
} }
/**
* \brief Drops content of a storage interface (public transaction variant)
*
* Just performs content drop
*
* \param[in] txn - transaction ID, must be writable transaction!
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
*
* \exception LMDBAL::TransactionTerminated thrown if the transaction was not active
*/
int LMDBAL::iStorage::drop(const WriteTransaction& txn) {
ensureOpened(dropMethodName);
return drop(extractTransactionId(txn, dropMethodName));
}
/** /**
* \brief Helper function, thows exception if the database is not opened * \brief Helper function, thows exception if the database is not opened
* *
@ -127,7 +160,7 @@ LMDBAL::SizeType LMDBAL::iStorage::count() const {
} }
/** /**
* \brief Storage size (transaction variant) * \brief Storage size (private transaction variant)
* *
* \param[in] txn - transaction ID, can be read-only transaction * \param[in] txn - transaction ID, can be read-only transaction
* \returns amount of records in the storage * \returns amount of records in the storage
@ -143,6 +176,21 @@ LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const {
return stat.ms_entries; return stat.ms_entries;
} }
/**
* \brief Storage size (public transaction variant)
*
* \param[in] txn - transaction, can be read-only transaction
* \returns amount of records in the storage
*
* \exception LMDBAL::Closed thrown if the database was closed
* \exception LMDBAL::Unknown thrown if something unexpected happened
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const {
ensureOpened(countMethodName);
return count(extractTransactionId(txn, countMethodName));
}
/** /**
* \brief Throws LMDBAL::Exist or LMDBAL::Unknown (transaction vairiant) * \brief Throws LMDBAL::Exist or LMDBAL::Unknown (transaction vairiant)
* *
@ -391,5 +439,4 @@ void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const { void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
UNUSED(txn);} UNUSED(txn);}
void LMDBAL::iStorage::handleDrop() {}

View File

@ -25,6 +25,7 @@
#include "base.h" #include "base.h"
#include "serializer.h" #include "serializer.h"
#include "cursor.h" #include "cursor.h"
#include "transaction.h"
class BaseTest; class BaseTest;
class DuplicatesTest; class DuplicatesTest;
@ -46,6 +47,7 @@ protected:
*/ */
virtual int open(MDB_txn * transaction) = 0; virtual int open(MDB_txn * transaction) = 0;
virtual void close(); virtual void close();
virtual void handleDrop();
bool isDBOpened() const; bool isDBOpened() const;
const std::string& dbName() const; const std::string& dbName() const;
@ -62,6 +64,7 @@ protected:
void throwNotFound(const std::string& key) const; void throwNotFound(const std::string& key) const;
void throwCursorNotReady(const std::string& method) const; void throwCursorNotReady(const std::string& method) const;
TransactionID extractTransactionId(const Transaction& txn, const std::string& action = "") const;
TransactionID beginReadOnlyTransaction() const; TransactionID beginReadOnlyTransaction() const;
TransactionID beginTransaction() const; TransactionID beginTransaction() const;
void commitTransaction(TransactionID id); void commitTransaction(TransactionID id);
@ -69,12 +72,14 @@ protected:
virtual void transactionStarted(TransactionID txn, bool readOnly) const; virtual void transactionStarted(TransactionID txn, bool readOnly) const;
virtual void transactionCommited(TransactionID txn); virtual void transactionCommited(TransactionID txn);
virtual void transactionAborted(TransactionID txn) const; virtual void transactionAborted(TransactionID txn) const;
virtual int drop(TransactionID transaction);
virtual SizeType count(TransactionID txn) const;
public: public:
virtual void drop(); virtual void drop();
virtual int drop(TransactionID transaction); virtual int drop(const WriteTransaction& txn);
virtual SizeType count() const; virtual SizeType count() const;
virtual SizeType count(TransactionID txn) const; virtual SizeType count(const Transaction& txn) const;
protected: protected:
MDB_dbi dbi; /**<\brief lmdb storage handle*/ MDB_dbi dbi; /**<\brief lmdb storage handle*/
@ -118,30 +123,42 @@ protected:
virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const; virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const;
uint32_t flags() const; uint32_t flags() const;
virtual void addRecord(const K& key, const V& value, TransactionID txn);
virtual bool forceRecord(const K& key, const V& value, TransactionID txn);
virtual void changeRecord(const K& key, const V& value, TransactionID txn);
virtual void removeRecord(const K& key, TransactionID txn);
virtual bool checkRecord(const K& key, TransactionID txn) const;
virtual void getRecord(const K& key, V& value, TransactionID txn) const;
virtual V getRecord(const K& key, TransactionID txn) const;
virtual std::map<K, V> readAll(TransactionID txn) const;
virtual void readAll(std::map<K, V>& result, TransactionID txn) const;
virtual void replaceAll(const std::map<K, V>& data, TransactionID txn);
virtual uint32_t addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false);
public: public:
using iStorage::drop; using iStorage::drop;
virtual void addRecord(const K& key, const V& value); virtual void addRecord(const K& key, const V& value);
virtual void addRecord(const K& key, const V& value, TransactionID txn); virtual void addRecord(const K& key, const V& value, const WriteTransaction& txn);
virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change virtual bool forceRecord(const K& key, const V& value); //returns true if there was addition, false if change
virtual bool forceRecord(const K& key, const V& value, TransactionID txn); virtual bool forceRecord(const K& key, const V& value, const WriteTransaction& txn);
virtual void changeRecord(const K& key, const V& value); virtual void changeRecord(const K& key, const V& value);
virtual void changeRecord(const K& key, const V& value, TransactionID txn); virtual void changeRecord(const K& key, const V& value, const WriteTransaction& txn);
virtual void removeRecord(const K& key); virtual void removeRecord(const K& key);
virtual void removeRecord(const K& key, TransactionID txn); virtual void removeRecord(const K& key, const WriteTransaction& txn);
virtual bool checkRecord(const K& key) const; //checks if there is a record with given key virtual bool checkRecord(const K& key) const; //checks if there is a record with given key
virtual bool checkRecord(const K& key, TransactionID txn) const; virtual bool checkRecord(const K& key, const Transaction& txn) const;
virtual void getRecord(const K& key, V& value) const; virtual void getRecord(const K& key, V& value) const;
virtual void getRecord(const K& key, V& value, TransactionID txn) const; virtual void getRecord(const K& key, V& value, const Transaction& txn) const;
virtual V getRecord(const K& key) const; virtual V getRecord(const K& key) const;
virtual V getRecord(const K& key, TransactionID txn) const; virtual V getRecord(const K& key, const Transaction& txn) const;
virtual std::map<K, V> readAll() const; virtual std::map<K, V> readAll() const;
virtual std::map<K, V> readAll(TransactionID txn) const; virtual std::map<K, V> readAll(const Transaction& txn) const;
virtual void readAll(std::map<K, V>& result) const; virtual void readAll(std::map<K, V>& result) const;
virtual void readAll(std::map<K, V>& result, TransactionID txn) const; virtual void readAll(std::map<K, V>& result, const Transaction& txn) const;
virtual void replaceAll(const std::map<K, V>& data); virtual void replaceAll(const std::map<K, V>& data);
virtual void replaceAll(const std::map<K, V>& data, TransactionID txn); virtual void replaceAll(const std::map<K, V>& data, const WriteTransaction& txn);
virtual uint32_t addRecords(const std::map<K, V>& data, bool overwrite = false); virtual uint32_t addRecords(const std::map<K, V>& data, bool overwrite = false);
virtual uint32_t addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite = false); virtual uint32_t addRecords(const std::map<K, V>& data, const WriteTransaction& txn, bool overwrite = false);
Cursor<K, V>* createCursor(); Cursor<K, V>* createCursor();
void destroyCursor(Cursor<K, V>* cursor); void destroyCursor(Cursor<K, V>* cursor);

View File

@ -91,10 +91,7 @@ void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value) {
} }
/** /**
* \brief Adds a key-value record to the storage (transaction variant) * \brief Adds a key-value record to the storage (private transaction variant)
*
* This method schedules an addition of a key-value record, but doesn't immidiately adds it.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
* *
* Take a note that if the storage already had a record you want to add LMDBAL::Exist is thrown. * Take a note that if the storage already had a record you want to add LMDBAL::Exist is thrown.
* If your storage doesn't support duplicates LMDBAL::Exist is thrown if the record with the same key already exists in the database. * If your storage doesn't support duplicates LMDBAL::Exist is thrown if the record with the same key already exists in the database.
@ -105,13 +102,10 @@ void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value) {
* \param[in] txn transaction ID, needs to be a writable transaction! * \param[in] txn transaction ID, needs to be a writable transaction!
* *
* \exception LMDBAL::Exist thrown if the storage already has a record with the given key * \exception LMDBAL::Exist thrown if the storage already has a record with the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionID txn) { void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
ensureOpened(addRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer.setData(key);
MDB_val lmdbData = valueSerializer.setData(value); MDB_val lmdbData = valueSerializer.setData(value);
@ -126,6 +120,31 @@ void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionI
throwDuplicateOrUnknown(rc, toString(key)); throwDuplicateOrUnknown(rc, toString(key));
} }
/**
* \brief Adds a key-value record to the storage (public transaction variant)
*
* This method schedules an addition of a key-value record, but doesn't immidiately adds it.
* You can obtain LMDBAL::WriteTransaction calling LMDBAL::Base::beginTransaction().
*
* Take a note that if the storage already had a record you want to add LMDBAL::Exist is thrown.
* If your storage doesn't support duplicates LMDBAL::Exist is thrown if the record with the same key already exists in the database.
* If your storage supports duplicates LMDBAL::Exist is thrown only if the record with the same key <b>AND</b> already exists in the database.
*
* \param[in] key key of the record
* \param[in] value value of the record
* \param[in] txn transaction
*
* \exception LMDBAL::Exist thrown if the storage already has a record with the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, const WriteTransaction& txn) {
ensureOpened(addRecordMethodName);
addRecord(key, value, extractTransactionId(txn, addRecordMethodName));
}
/** /**
* \brief Adds a key-value record to the storage, overwrites if it already exists * \brief Adds a key-value record to the storage, overwrites if it already exists
* *
@ -160,11 +179,9 @@ bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value) {
} }
/** /**
* \brief Adds a key-value record to the storage, overwrites if it already exists (transaction variant) * \brief Adds a key-value record to the storage, overwrites if it already exists (private transaction variant)
* *
* This method schedules an addition of a key-value record, but doesn't immidiately add it. * This method schedules an addition of a key-value record, but doesn't immidiately add it.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
* If the record did already exist in the database the actual overwrite will be done only after calling LMDBAL::Base::commitTransaction().
* *
* This method is mostly useless in duplicates mode. * This method is mostly useless in duplicates mode.
* In this mode it basically does the same thing LMDBAL::Storage::addRecord() does, * In this mode it basically does the same thing LMDBAL::Storage::addRecord() does,
@ -176,13 +193,10 @@ bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value) {
* \param[in] txn transaction ID, needs to be a writable transaction! * \param[in] txn transaction ID, needs to be a writable transaction!
* \returns true if the record was added, false otherwise * \returns true if the record was added, false otherwise
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) { bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) {
ensureOpened(forceRecordMethodName);
bool added; bool added;
if (duplicates) { if (duplicates) {
try { try {
@ -216,6 +230,33 @@ bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, Transactio
return added; return added;
} }
/**
* \brief Adds a key-value record to the storage, overwrites if it already exists (public transaction variant)
*
* This method schedules an addition of a key-value record, but doesn't immidiately add it.
* You can obtain LMDBAL::WriteTransaction calling LMDBAL::Base::beginTransaction().
* If the record did already exist in the database the actual overwrite will be done only after calling LMDBAL::WriteTransaction::commit().
*
* This method is mostly useless in duplicates mode.
* In this mode it basically does the same thing LMDBAL::Storage::addRecord() does,
* but suppresses LMDBAL::Exist exception if the record with the same key-value pair existed in the storage.
* In this case just false is returned from the method.
*
* \param[in] key key of the record
* \param[in] value value of the record
* \param[in] txn transaction
* \returns true if the record was added, false otherwise
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, const WriteTransaction& txn) {
ensureOpened(forceRecordMethodName);
return forceRecord(key, value, extractTransactionId(txn, forceRecordMethodName));
}
/** /**
* \brief Changes key-value record to the storage. * \brief Changes key-value record to the storage.
* *
@ -251,11 +292,9 @@ void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value) {
} }
/** /**
* \brief Changes key-value record to the storage (transaction variant) * \brief Changes key-value record to the storage (private transaction variant)
* *
* This method schedules a modification of a key-value record, but doesn't immidiately changes it. * This method schedules a modification of a key-value record, but doesn't immidiately changes it.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
*
* Take a note that if the storage didn't have a record you want to change LMDBAL::NotFound is thrown * Take a note that if the storage didn't have a record you want to change LMDBAL::NotFound is thrown
* *
* If duplicates mode is enabled this function will find the first entry of the key * If duplicates mode is enabled this function will find the first entry of the key
@ -270,13 +309,10 @@ void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value) {
* *
* \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key * \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key
* \exception LMDBAL::Exist thrown in duplicates mode when the given value matches some of existing values for the given key * \exception LMDBAL::Exist thrown in duplicates mode when the given value matches some of existing values for the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) { void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
ensureOpened(changeRecordMethodName);
MDB_cursor* cursor; MDB_cursor* cursor;
int rc = mdb_cursor_open(txn, dbi, &cursor); int rc = mdb_cursor_open(txn, dbi, &cursor);
if (rc != MDB_SUCCESS) if (rc != MDB_SUCCESS)
@ -313,6 +349,37 @@ void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, Transacti
throwDuplicateOrUnknown(rc, toString(key)); throwDuplicateOrUnknown(rc, toString(key));
} }
/**
* \brief Changes key-value record to the storage (public transaction variant)
*
* This method schedules a modification of a key-value record, but doesn't immidiately changes it.
* You can obtain LMDBAL::WriteTransaction calling LMDBAL::Base::beginTransaction().
*
* Take a note that if the storage didn't have a record you want to change LMDBAL::NotFound is thrown
*
* If duplicates mode is enabled this function will find the first entry of the key
* (which is pretty couterintuitive, see LMDBAL::Storage::getRecord() description)
* and change it's value to the given one.
* If the given value matches some of the other values for given key the method will throw LMDBAL::Exist,
* if no key was found it will still throw LMDBAL::NotFound.
*
* \param[in] key key of the record
* \param[in] value new value of the record
* \param[in] txn transaction
*
* \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key
* \exception LMDBAL::Exist thrown in duplicates mode when the given value matches some of existing values for the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, const WriteTransaction& txn) {
ensureOpened(changeRecordMethodName);
changeRecord(key, value, extractTransactionId(txn, changeRecordMethodName));
}
/** /**
* \brief Gets the record from the database * \brief Gets the record from the database
@ -374,10 +441,7 @@ void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value) const {
} }
/** /**
* \brief Gets the record from the database (transaction variant) * \brief Gets the record from the database (private transaction variant)
*
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
* *
* Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown * Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown
* *
@ -392,22 +456,19 @@ void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value) const {
* \returns the value from the storage * \returns the value from the storage
* *
* \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key * \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
V LMDBAL::Storage<K, V>::getRecord(const K& key, TransactionID txn) const { V LMDBAL::Storage<K, V>::getRecord(const K& key, TransactionID txn) const {
ensureOpened(getRecordMethodName);
V value; V value;
Storage<K, V>::getRecord(key, value, txn); Storage<K, V>::getRecord(key, value, txn);
return value; return value;
} }
/** /**
* \brief Gets the record from the database (transaction, reference variant) * \brief Gets the record from the database (public transaction variant)
* *
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction(). * You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction(). * If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
* *
* Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown * Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown
@ -419,17 +480,40 @@ V LMDBAL::Storage<K, V>::getRecord(const K& key, TransactionID txn) const {
* - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50" * - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50"
* *
* \param[in] key key of the record you look for * \param[in] key key of the record you look for
* \param[in] txn transaction ID, can be read only transaction
* \returns the value from the storage
*
* \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
V LMDBAL::Storage<K, V>::getRecord(const K& key, const Transaction& txn) const {
ensureOpened(getRecordMethodName);
return getRecord(key, extractTransactionId(txn, getRecordMethodName));
}
/**
* \brief Gets the record from the database (private transaction, reference variant)
*
* Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown
*
* If the storage supports duplicates the exact value returned from it depends on comparison function of lmdb.
* It's not very straight forward, so, you shouldn't really use this method if you use duplicates and you rely on exact result.
* Anyway:
* - if your values are signed or unsigned integer of any size the LOWEST value is returned compared as <b>UNSIGNED</b>. For example for storage with int32_t as value, from the same key, from the set of values {-33, -1, 5573, 77753} 5573 is returned as it is the lowest by <b>UNSIGNED</b> comparison.
* - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50"
*
* \param[in] key key of the record you look for
* \param[out] value the value from the storage * \param[out] value the value from the storage
* \param[in] txn transaction ID, can be read only transaction * \param[in] txn transaction ID, can be read only transaction
* *
* \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key * \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn) const { void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn) const {
ensureOpened(getRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer.setData(key);
MDB_val lmdbData; MDB_val lmdbData;
@ -440,6 +524,36 @@ void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn)
valueSerializer.deserialize(lmdbData, value); valueSerializer.deserialize(lmdbData, value);
} }
/**
* \brief Gets the record from the database (public transaction, reference variant)
*
* You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
*
* Take a note that if the storage didn't have a record you want to get LMDBAL::NotFound is thrown
*
* If the storage supports duplicates the exact value returned from it depends on comparison function of lmdb.
* It's not very straight forward, so, you shouldn't really use this method if you use duplicates and you rely on exact result.
* Anyway:
* - if your values are signed or unsigned integer of any size the LOWEST value is returned compared as <b>UNSIGNED</b>. For example for storage with int32_t as value, from the same key, from the set of values {-33, -1, 5573, 77753} 5573 is returned as it is the lowest by <b>UNSIGNED</b> comparison.
* - if your values are anything else - they are compared byte by byte as if they are strings, it makes it especially complicated to predict the exact value for float or double templated storages. For strings if makes a bit more sence: if the choise is from "50" and "100" - "100" is returned, because the first byte of the "100" is lower than the first byte of the "50"
*
* \param[in] key key of the record you look for
* \param[out] value the value from the storage
* \param[in] txn transaction ID, can be read only transaction
*
* \exception LMDBAL::NotFound thrown if the storage doesn't have a record with the given key
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, const Transaction& txn) const {
ensureOpened(getRecordMethodName);
getRecord(key, value, extractTransactionId(txn, getRecordMethodName));
}
/** /**
* \brief Chechs if storage has value * \brief Chechs if storage has value
* *
@ -467,22 +581,16 @@ bool LMDBAL::Storage<K, V>::checkRecord(const K& key) const {
} }
/** /**
* \brief Chechs if storage has value (transaction variant) * \brief Chechs if storage has value (private transaction variant)
*
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
* *
* \param[in] key key of the record you look for * \param[in] key key of the record you look for
* \param[in] txn transaction ID, can be read only transaction * \param[in] txn transaction ID, can be read only transaction
* \returns true if there was a record with given key, false otherwise * \returns true if there was a record with given key, false otherwise
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const { bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const {
ensureOpened(checkRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer.setData(key);
MDB_val lmdbData; MDB_val lmdbData;
@ -496,6 +604,26 @@ bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const {
return false; return false;
} }
/**
* \brief Chechs if storage has value (public transaction variant)
*
* You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
*
* \param[in] key key of the record you look for
* \param[in] txn transaction, can be read only transaction
* \returns true if there was a record with given key, false otherwise
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
bool LMDBAL::Storage<K, V>::checkRecord(const K& key, const Transaction& txn) const {
ensureOpened(checkRecordMethodName);
return checkRecord(key, extractTransactionId(txn, checkRecordMethodName));
}
/** /**
* \brief Reads whole storage into a map * \brief Reads whole storage into a map
* *
@ -545,31 +673,25 @@ void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result) const {
} }
/** /**
* \brief Reads whole storage into a map (transaction variant) * \brief Reads whole storage into a map (private transaction variant)
* *
* Basically just reads all database in an std::map, usefull when you store small storages * Basically just reads all database in an std::map, usefull when you store small storages
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
*
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b> * In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map * (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
* *
* \param[in] txn transaction ID, can be read only transaction * \param[in] txn transaction ID, can be read only transaction
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
std::map<K, V> LMDBAL::Storage<K, V>::readAll(TransactionID txn) const { std::map<K, V> LMDBAL::Storage<K, V>::readAll(TransactionID txn) const {
ensureOpened(readAllMethodName);
std::map<K, V> result; std::map<K, V> result;
Storage<K, V>::readAll(result, txn); Storage<K, V>::readAll(result, txn);
return result; return result;
} }
/** /**
* \brief Reads whole storage into a map (transaction, reference variant) * \brief Reads whole storage into a map (public transaction variant)
* *
* Basically just reads all database in an std::map, usefull when you store small storages * Basically just reads all database in an std::map, usefull when you store small storages
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction(). * You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
@ -578,16 +700,32 @@ std::map<K, V> LMDBAL::Storage<K, V>::readAll(TransactionID txn) const {
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b> * In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map * (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
* *
* \param[in] txn transaction, can be read only transaction
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
std::map<K, V> LMDBAL::Storage<K, V>::readAll(const Transaction& txn) const {
ensureOpened(readAllMethodName);
return readAll(extractTransactionId(txn, readAllMethodName));
}
/**
* \brief Reads whole storage into a map (private transaction, reference variant)
*
* Basically just reads all database in an std::map, usefull when you store small storages
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
*
* \param[out] result a map that is going to contain all data * \param[out] result a map that is going to contain all data
* \param[in] txn transaction ID, can be read only transaction * \param[in] txn transaction ID, can be read only transaction
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) const { void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) const {
ensureOpened(readAllMethodName);
MDB_cursor* cursor; MDB_cursor* cursor;
MDB_val lmdbKey, lmdbData; MDB_val lmdbKey, lmdbData;
@ -610,6 +748,29 @@ void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) c
throwUnknown(rc); throwUnknown(rc);
} }
/**
* \brief Reads whole storage into a map (public transaction, reference variant)
*
* Basically just reads all database in an std::map, usefull when you store small storages
* You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginReadOnlyTransaction() or LMDBAL::Base::beginTransaction().
* If you just want to read data you should prefer LMDBAL::Base::beginReadOnlyTransaction().
*
* In case storage supports duplicates <b>only what lmdb considered to be lowest value</b>
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
*
* \param[out] result a map that is going to contain all data
* \param[in] txn transaction, can be read only transaction
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, const Transaction& txn) const {
ensureOpened(readAllMethodName);
readAll(result, extractTransactionId(txn, readAllMethodName));
}
/** /**
* \brief Replaces the content of the whole storage with the given * \brief Replaces the content of the whole storage with the given
* *
@ -636,22 +797,17 @@ void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data) {
} }
/** /**
* \brief Replaces the content of the whole storage with the given (transaction variant) * \brief Replaces the content of the whole storage with the given (private transaction variant)
* *
* Basically this method drops the database and adds all the records from the given map * Basically this method drops the database and adds all the records from the given map
* This method schedules a data replacement, but doesn't immidiately execute it.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
* *
* \param[in] data new data of the storage * \param[in] data new data of the storage
* \param[in] txn transaction ID, needs to be a writable transaction! * \param[in] txn transaction ID, needs to be a writable transaction!
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, TransactionID txn) { void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, TransactionID txn) {
ensureOpened(replaceAllMethodName);
int rc = drop(txn); int rc = drop(txn);
if (rc != MDB_SUCCESS) if (rc != MDB_SUCCESS)
throwUnknown(rc); throwUnknown(rc);
@ -667,6 +823,26 @@ void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, TransactionID
} }
} }
/**
* \brief Replaces the content of the whole storage with the given (public transaction variant)
*
* Basically this method drops the database and adds all the records from the given map
* This method schedules a data replacement, but doesn't immidiately execute it.
* You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginTransaction().
*
* \param[in] data new data of the storage
* \param[in] txn transaction
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, const WriteTransaction& txn) {
ensureOpened(replaceAllMethodName);
replaceAll(data, extractTransactionId(txn, replaceAllMethodName));
}
/** /**
* \brief Adds records in bulk * \brief Adds records in bulk
* *
@ -696,24 +872,20 @@ uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, bool over
} }
/** /**
* \brief Adds records in bulk (transaction variant) * \brief Adds records in bulk (private transaction variant)
* *
* This method schedules a data addition, but doesn't immidiately execute it. * This method schedules a data addition, but doesn't immidiately execute it.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
* *
* \param[in] data the data to be added * \param[in] data the data to be added
* \param[in] txn transaction ID, needs to be a writable transaction! * \param[in] txn transaction ID, needs to be a writable transaction!
* \param[in] overwrite if false method throws LMDBAL::Exist on repeated key, if true - overwrites it * \param[in] overwrite if false method throws LMDBAL::Exist on repeated key, if true - overwrites it
* \returns new actual amount of records in the storage * \returns new actual amount of records in the storage
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Exist thrown if overwrite==false and at least one of the keys of data already exists in the storage * \exception LMDBAL::Exist thrown if overwrite==false and at least one of the keys of data already exists in the storage
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite) { uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite) {
ensureOpened(addRecordsMethodName);
MDB_val lmdbKey, lmdbData; MDB_val lmdbKey, lmdbData;
int rc; int rc;
for (const std::pair<const K, V>& pair : data) { for (const std::pair<const K, V>& pair : data) {
@ -736,6 +908,28 @@ uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, Transacti
return stat.ms_entries; return stat.ms_entries;
} }
/**
* \brief Adds records in bulk (public transaction variant)
*
* This method schedules a data addition, but doesn't immidiately execute it.
* You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginTransaction().
*
* \param[in] data the data to be added
* \param[in] txn transaction
* \param[in] overwrite if false method throws LMDBAL::Exist on repeated key, if true - overwrites it
* \returns new actual amount of records in the storage
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::Exist thrown if overwrite==false and at least one of the keys of data already exists in the storage
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, const WriteTransaction& txn, bool overwrite) {
ensureOpened(addRecordsMethodName);
return addRecords(data, extractTransactionId(txn, addRecordsMethodName), overwrite);
}
/** /**
* \brief Removes one of the records * \brief Removes one of the records
* *
@ -763,29 +957,46 @@ void LMDBAL::Storage<K, V>::removeRecord(const K& key) {
} }
/** /**
* \brief Removes one of the records (transaction variant) * \brief Removes one of the records (private transaction variant)
* *
* Take a note that if the storage didn't have a record you want to remove LMDBAL::NotFound is thrown * Take a note that if the storage didn't have a record you want to remove LMDBAL::NotFound is thrown
* This method schedules a record removal, but doesn't immidiately execute it. * This method schedules a record removal, but doesn't immidiately execute it.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
* *
* \param[in] key key of the record you wish to be removed * \param[in] key key of the record you wish to be removed
* \param[in] txn transaction ID, needs to be a writable transaction! * \param[in] txn transaction ID, needs to be a writable transaction!
* *
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::NotFound thrown if the record with given key wasn't found * \exception LMDBAL::NotFound thrown if the record with given key wasn't found
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb * \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
*/ */
template<class K, class V> template<class K, class V>
void LMDBAL::Storage<K, V>::removeRecord(const K& key, TransactionID txn) { void LMDBAL::Storage<K, V>::removeRecord(const K& key, TransactionID txn) {
ensureOpened(removeRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key); MDB_val lmdbKey = keySerializer.setData(key);
int rc = mdb_del(txn, dbi, &lmdbKey, NULL); int rc = mdb_del(txn, dbi, &lmdbKey, NULL);
if (rc != MDB_SUCCESS) if (rc != MDB_SUCCESS)
throwNotFoundOrUnknown(rc, toString(key)); throwNotFoundOrUnknown(rc, toString(key));
} }
/**
* \brief Removes one of the records (transaction variant)
*
* Take a note that if the storage didn't have a record you want to remove LMDBAL::NotFound is thrown
* This method schedules a record removal, but doesn't immidiately execute it.
* You can obtain LMDBAL::Transaction calling LMDBAL::Base::beginTransaction().
*
* \param[in] key key of the record you wish to be removed
* \param[in] txn transaction ID
*
* \exception LMDBAL::Closed thrown if the database was not opened
* \exception LMDBAL::NotFound thrown if the record with given key wasn't found
* \exception LMDBAL::Unknown thrown if something unexpected happend within lmdb
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::removeRecord(const K& key, const WriteTransaction& txn) {
ensureOpened(removeRecordMethodName);
removeRecord(key, extractTransactionId(txn, removeRecordMethodName));
}
/** /**
* \brief A private virtual method I need to open each storage in the database * \brief A private virtual method I need to open each storage in the database
* *

71
src/transaction.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "transaction.h"
LMDBAL::Transaction::Transaction():
txn(nullptr),
active(false),
parent(nullptr)
{}
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
txn(txn),
active(true),
parent(parent)
{}
LMDBAL::Transaction::Transaction(Transaction&& other):
txn(other.txn),
active(other.active),
parent(other.parent)
{
other.active = false;
}
LMDBAL::Transaction::~Transaction() {
terminate();
}
LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
terminate();
txn = other.txn;
active = other.active;
parent = other.parent;
other.active = false;
return *this;
}
void LMDBAL::Transaction::terminate() {
if (active) {
parent->abortTransaction(txn);
active = false;
}
}
bool LMDBAL::Transaction::isActive() const {
return active; //todo may be it's better if I query it from DB?
}
LMDBAL::WriteTransaction::WriteTransaction(TransactionID txn, Base* parent):
Transaction(txn, parent)
{}
LMDBAL::WriteTransaction::WriteTransaction():
Transaction()
{}
LMDBAL::WriteTransaction::WriteTransaction(WriteTransaction&& other):
Transaction(std::move(other))
{}
void LMDBAL::WriteTransaction::abort() {
terminate();
}
void LMDBAL::WriteTransaction::commit() {
if (active) {
const_cast<Base*>(parent)->commitTransaction(txn);
active = false;
}
}

46
src/transaction.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "base.h"
namespace LMDBAL {
class iStorage;
class Transaction {
friend class Base;
friend class iStorage;
public:
explicit Transaction();
explicit Transaction(Transaction&& other);
Transaction(const Transaction& other) = delete;
Transaction& operator = (const Transaction& other) = delete;
Transaction& operator = (Transaction&& other);
virtual ~Transaction();
void terminate();
bool isActive() const;
protected:
Transaction(TransactionID txn, const Base* parent);
protected:
TransactionID txn;
bool active;
const Base* parent;
};
class WriteTransaction : public Transaction {
friend class Base;
public:
explicit WriteTransaction();
explicit WriteTransaction(WriteTransaction&& other);
WriteTransaction(const WriteTransaction& other) = delete;
WriteTransaction& operator = (const WriteTransaction& other) = delete;
void commit();
void abort();
protected:
WriteTransaction(TransactionID txn, Base* parent);
};
}

View File

@ -24,6 +24,7 @@ protected:
} }
static void TearDownTestSuite() { static void TearDownTestSuite() {
transaction.terminate();
db->close(); db->close();
db->removeDirectory(); db->removeDirectory();
delete db; delete db;
@ -35,7 +36,7 @@ protected:
static LMDBAL::Base* db; static LMDBAL::Base* db;
static LMDBAL::Cursor<uint64_t, std::string>* cursor; static LMDBAL::Cursor<uint64_t, std::string>* cursor;
static LMDBAL::Cursor<uint64_t, std::string>* emptyCursor; static LMDBAL::Cursor<uint64_t, std::string>* emptyCursor;
static LMDBAL::TransactionID transaction; static LMDBAL::Transaction transaction;
LMDBAL::Cache<uint64_t, std::string>* cache; LMDBAL::Cache<uint64_t, std::string>* cache;
LMDBAL::Cache<uint64_t, std::string>* emptyCache; LMDBAL::Cache<uint64_t, std::string>* emptyCache;
@ -44,7 +45,7 @@ protected:
LMDBAL::Base* CacheCursorTest::db = nullptr; LMDBAL::Base* CacheCursorTest::db = nullptr;
LMDBAL::Cursor<uint64_t, std::string>* CacheCursorTest::cursor = nullptr; LMDBAL::Cursor<uint64_t, std::string>* CacheCursorTest::cursor = nullptr;
LMDBAL::Cursor<uint64_t, std::string>* CacheCursorTest::emptyCursor = nullptr; LMDBAL::Cursor<uint64_t, std::string>* CacheCursorTest::emptyCursor = nullptr;
LMDBAL::TransactionID CacheCursorTest::transaction = nullptr; LMDBAL::Transaction CacheCursorTest::transaction;
static const std::map<uint64_t, std::string> data({ static const std::map<uint64_t, std::string> data({
{245665783, "bothering nerds"}, {245665783, "bothering nerds"},
@ -288,7 +289,7 @@ TEST_F(CacheCursorTest, CurrentPublic) {
} }
TEST_F(CacheCursorTest, CornerCases) { TEST_F(CacheCursorTest, CornerCases) {
db->abortTransaction(transaction); transaction.terminate();
EXPECT_THROW(cursor->current(), LMDBAL::Unknown); EXPECT_THROW(cursor->current(), LMDBAL::Unknown);
cursor->close(); cursor->close();

View File

@ -64,7 +64,7 @@ TEST_F(CacheTransactionsTest, Adding) {
EXPECT_EQ(c1->count(), 0); EXPECT_EQ(c1->count(), 0);
EXPECT_EQ(c2->count(), 0); EXPECT_EQ(c2->count(), 0);
LMDBAL::TransactionID txn = db->beginTransaction(); LMDBAL::WriteTransaction txn = db->beginTransaction();
c1->addRecord(5, 13, txn); c1->addRecord(5, 13, txn);
c1->addRecord(-53, 782, txn); c1->addRecord(-53, 782, txn);
c1->addRecord(5892, -37829, txn); c1->addRecord(5892, -37829, txn);
@ -76,7 +76,7 @@ TEST_F(CacheTransactionsTest, Adding) {
EXPECT_EQ(c1->count(), 0); EXPECT_EQ(c1->count(), 0);
EXPECT_EQ(c2->count(), 0); EXPECT_EQ(c2->count(), 0);
db->commitTransaction(txn); txn.commit();
EXPECT_EQ(c1->count(), 3); EXPECT_EQ(c1->count(), 3);
EXPECT_EQ(c1->getRecord(5), 13); EXPECT_EQ(c1->getRecord(5), 13);
@ -95,7 +95,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
LMDBAL::SizeType s1 = c1->count(); LMDBAL::SizeType s1 = c1->count();
LMDBAL::SizeType s2 = c2->count(); LMDBAL::SizeType s2 = c2->count();
LMDBAL::TransactionID txn = db->beginTransaction(); LMDBAL::WriteTransaction txn = db->beginTransaction();
c1->addRecord(18, 40, txn); c1->addRecord(18, 40, txn);
c1->addRecord(85, -4, txn); c1->addRecord(85, -4, txn);
c1->addRecord(-5, -3, txn); c1->addRecord(-5, -3, txn);
@ -107,7 +107,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
EXPECT_EQ(c1->count(), s1); EXPECT_EQ(c1->count(), s1);
EXPECT_EQ(c2->count(), s2); EXPECT_EQ(c2->count(), s2);
db->abortTransaction(txn); txn.abort();
EXPECT_EQ(c1->count(), s1); EXPECT_EQ(c1->count(), s1);
EXPECT_EQ(c2->count(), s2); EXPECT_EQ(c2->count(), s2);
@ -116,7 +116,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
TEST_F(CacheTransactionsTest, Reading) { TEST_F(CacheTransactionsTest, Reading) {
EXPECT_EQ(db->ready(), true); EXPECT_EQ(db->ready(), true);
LMDBAL::TransactionID txn = db->beginReadOnlyTransaction(); LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
EXPECT_EQ(c1->count(txn), 3); EXPECT_EQ(c1->count(txn), 3);
EXPECT_EQ(c1->getRecord(5, txn), 13); EXPECT_EQ(c1->getRecord(5, txn), 13);
@ -128,14 +128,14 @@ TEST_F(CacheTransactionsTest, Reading) {
EXPECT_FLOAT_EQ(c2->getRecord("decallence", txn), 8532.48); EXPECT_FLOAT_EQ(c2->getRecord("decallence", txn), 8532.48);
EXPECT_FLOAT_EQ(c2->getRecord("prevent recovery", txn), -64.64); EXPECT_FLOAT_EQ(c2->getRecord("prevent recovery", txn), -64.64);
db->abortTransaction(txn); txn.terminate();
} }
TEST_F(CacheTransactionsTest, ConcurentReading) { TEST_F(CacheTransactionsTest, ConcurentReading) {
EXPECT_EQ(db->ready(), true); EXPECT_EQ(db->ready(), true);
LMDBAL::SizeType size = c1->count(); LMDBAL::SizeType size = c1->count();
LMDBAL::TransactionID txn = db->beginTransaction(); LMDBAL::WriteTransaction txn = db->beginTransaction();
EXPECT_EQ(c1->getRecord(5, txn), 13); EXPECT_EQ(c1->getRecord(5, txn), 13);
EXPECT_EQ(c1->getRecord(5), 13); EXPECT_EQ(c1->getRecord(5), 13);
@ -160,7 +160,7 @@ TEST_F(CacheTransactionsTest, ConcurentReading) {
EXPECT_EQ(c1->count(txn), 1); EXPECT_EQ(c1->count(txn), 1);
EXPECT_EQ(c1->count(), size); EXPECT_EQ(c1->count(), size);
db->commitTransaction(txn); txn.commit();
EXPECT_FALSE(c1->checkRecord(5)); EXPECT_FALSE(c1->checkRecord(5));
EXPECT_EQ(c1->count(), 1); EXPECT_EQ(c1->count(), 1);
@ -183,7 +183,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
int pid = fork(); int pid = fork();
if (pid == 0) { // I am the child if (pid == 0) { // I am the child
std::cout << "beggining second transaction" << std::endl; std::cout << "beggining second transaction" << std::endl;
LMDBAL::TransactionID txn2 = db->beginTransaction(); //<--- this is where the execution should pause LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
//and wait for the first transaction to get finished //and wait for the first transaction to get finished
std::cout << "checking result of the first transaction value" << std::endl; std::cout << "checking result of the first transaction value" << std::endl;
EXPECT_EQ(c1->getRecord(5, txn2), 812); EXPECT_EQ(c1->getRecord(5, txn2), 812);
@ -198,13 +198,13 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
EXPECT_EQ(c1->getRecord(5), 812); EXPECT_EQ(c1->getRecord(5), 812);
std::cout << "commiting second transaction" << std::endl; std::cout << "commiting second transaction" << std::endl;
db->commitTransaction(txn2); txn2.commit();
std::cout << "quitting child thread" << std::endl; std::cout << "quitting child thread" << std::endl;
exit(testing::Test::HasFailure()); exit(testing::Test::HasFailure());
} else { // I am the parent } else { // I am the parent
std::cout << "beggining first transaction" << std::endl; std::cout << "beggining first transaction" << std::endl;
LMDBAL::TransactionID txn1 = db->beginTransaction(); LMDBAL::WriteTransaction txn1 = db->beginTransaction();
std::cout << "putting parent thread to sleep for 5 ms" << std::endl; std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
usleep(5); usleep(5);
@ -219,7 +219,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
EXPECT_FALSE(c1->checkRecord(5)); EXPECT_FALSE(c1->checkRecord(5));
std::cout << "commiting first transaction" << std::endl; std::cout << "commiting first transaction" << std::endl;
db->commitTransaction(txn1); txn1.commit();
std::cout << "waiting for the other thread to finish" << std::endl; std::cout << "waiting for the other thread to finish" << std::endl;
ASSERT_EQ(0, waitForChildFork(pid)); //child process should have no problems ASSERT_EQ(0, waitForChildFork(pid)); //child process should have no problems

View File

@ -331,7 +331,7 @@ TEST_F(DuplicatesTest, Changing) {
} }
TEST_F(DuplicatesTest, GettingAllRecords) { TEST_F(DuplicatesTest, GettingAllRecords) {
LMDBAL::TransactionID txn = db->beginReadOnlyTransaction(); LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
bool cycle; bool cycle;
LMDBAL::SizeType iterations; LMDBAL::SizeType iterations;
@ -462,6 +462,5 @@ TEST_F(DuplicatesTest, GettingAllRecords) {
EXPECT_NE(iterations, 0); EXPECT_NE(iterations, 0);
EXPECT_NE(k4.size(), 0); EXPECT_NE(k4.size(), 0);
txn.terminate();
db->abortTransaction(txn);
} }

View File

@ -23,6 +23,7 @@ protected:
} }
static void TearDownTestSuite() { static void TearDownTestSuite() {
transaction.terminate();
db->close(); db->close();
db->removeDirectory(); db->removeDirectory();
delete db; delete db;
@ -34,7 +35,7 @@ protected:
static LMDBAL::Base* db; static LMDBAL::Base* db;
static LMDBAL::Cursor<uint64_t, std::string>* cursor; static LMDBAL::Cursor<uint64_t, std::string>* cursor;
static LMDBAL::Cursor<uint64_t, std::string>* emptyCursor; static LMDBAL::Cursor<uint64_t, std::string>* emptyCursor;
static LMDBAL::TransactionID transaction; static LMDBAL::Transaction transaction;
LMDBAL::Storage<uint64_t, std::string>* table; LMDBAL::Storage<uint64_t, std::string>* table;
LMDBAL::Storage<uint64_t, std::string>* emptyTable; LMDBAL::Storage<uint64_t, std::string>* emptyTable;
@ -43,7 +44,7 @@ protected:
LMDBAL::Base* StorageCursorTest::db = nullptr; LMDBAL::Base* StorageCursorTest::db = nullptr;
LMDBAL::Cursor<uint64_t, std::string>* StorageCursorTest::cursor = nullptr; LMDBAL::Cursor<uint64_t, std::string>* StorageCursorTest::cursor = nullptr;
LMDBAL::Cursor<uint64_t, std::string>* StorageCursorTest::emptyCursor = nullptr; LMDBAL::Cursor<uint64_t, std::string>* StorageCursorTest::emptyCursor = nullptr;
LMDBAL::TransactionID StorageCursorTest::transaction = nullptr; LMDBAL::Transaction StorageCursorTest::transaction = LMDBAL::Transaction();
static const std::map<uint64_t, std::string> data({ static const std::map<uint64_t, std::string> data({
{245665783, "bothering nerds"}, {245665783, "bothering nerds"},
@ -265,7 +266,7 @@ TEST_F(StorageCursorTest, CurrentPublic) {
} }
TEST_F(StorageCursorTest, CornerCases) { TEST_F(StorageCursorTest, CornerCases) {
db->abortTransaction(transaction); transaction.terminate();
EXPECT_THROW(cursor->current(), LMDBAL::Unknown); EXPECT_THROW(cursor->current(), LMDBAL::Unknown);
cursor->close(); cursor->close();

View File

@ -64,7 +64,7 @@ TEST_F(StorageTransactionsTest, Adding) {
EXPECT_EQ(t1->count(), 0); EXPECT_EQ(t1->count(), 0);
EXPECT_EQ(t2->count(), 0); EXPECT_EQ(t2->count(), 0);
LMDBAL::TransactionID txn = db->beginTransaction(); LMDBAL::WriteTransaction txn = db->beginTransaction();
t1->addRecord(5, 13, txn); t1->addRecord(5, 13, txn);
t1->addRecord(-53, 782, txn); t1->addRecord(-53, 782, txn);
t1->addRecord(5892, -37829, txn); t1->addRecord(5892, -37829, txn);
@ -76,7 +76,7 @@ TEST_F(StorageTransactionsTest, Adding) {
EXPECT_EQ(t1->count(), 0); EXPECT_EQ(t1->count(), 0);
EXPECT_EQ(t2->count(), 0); EXPECT_EQ(t2->count(), 0);
db->commitTransaction(txn); txn.commit();
EXPECT_EQ(t1->count(), 3); EXPECT_EQ(t1->count(), 3);
EXPECT_EQ(t1->getRecord(5), 13); EXPECT_EQ(t1->getRecord(5), 13);
@ -95,7 +95,7 @@ TEST_F(StorageTransactionsTest, Aborting) {
LMDBAL::SizeType s1 = t1->count(); LMDBAL::SizeType s1 = t1->count();
LMDBAL::SizeType s2 = t2->count(); LMDBAL::SizeType s2 = t2->count();
LMDBAL::TransactionID txn = db->beginTransaction(); LMDBAL::WriteTransaction txn = db->beginTransaction();
t1->addRecord(18, 40, txn); t1->addRecord(18, 40, txn);
t1->addRecord(85, -4, txn); t1->addRecord(85, -4, txn);
t1->addRecord(-5, -3, txn); t1->addRecord(-5, -3, txn);
@ -107,7 +107,7 @@ TEST_F(StorageTransactionsTest, Aborting) {
EXPECT_EQ(t1->count(), s1); EXPECT_EQ(t1->count(), s1);
EXPECT_EQ(t2->count(), s2); EXPECT_EQ(t2->count(), s2);
db->abortTransaction(txn); txn.abort();
EXPECT_EQ(t1->count(), s1); EXPECT_EQ(t1->count(), s1);
EXPECT_EQ(t2->count(), s2); EXPECT_EQ(t2->count(), s2);
@ -116,7 +116,7 @@ TEST_F(StorageTransactionsTest, Aborting) {
TEST_F(StorageTransactionsTest, Reading) { TEST_F(StorageTransactionsTest, Reading) {
EXPECT_EQ(db->ready(), true); EXPECT_EQ(db->ready(), true);
LMDBAL::TransactionID txn = db->beginReadOnlyTransaction(); LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
EXPECT_EQ(t1->count(txn), 3); EXPECT_EQ(t1->count(txn), 3);
EXPECT_EQ(t1->getRecord(5, txn), 13); EXPECT_EQ(t1->getRecord(5, txn), 13);
@ -128,14 +128,14 @@ TEST_F(StorageTransactionsTest, Reading) {
EXPECT_FLOAT_EQ(t2->getRecord("decallence", txn), 8532.48); EXPECT_FLOAT_EQ(t2->getRecord("decallence", txn), 8532.48);
EXPECT_FLOAT_EQ(t2->getRecord("prevent recovery", txn), -64.64); EXPECT_FLOAT_EQ(t2->getRecord("prevent recovery", txn), -64.64);
db->abortTransaction(txn); txn.terminate();
} }
TEST_F(StorageTransactionsTest, ConcurentReading) { TEST_F(StorageTransactionsTest, ConcurentReading) {
EXPECT_EQ(db->ready(), true); EXPECT_EQ(db->ready(), true);
LMDBAL::SizeType size = t1->count(); LMDBAL::SizeType size = t1->count();
LMDBAL::TransactionID txn = db->beginTransaction(); LMDBAL::WriteTransaction txn = db->beginTransaction();
EXPECT_EQ(t1->getRecord(5, txn), 13); EXPECT_EQ(t1->getRecord(5, txn), 13);
EXPECT_EQ(t1->getRecord(5), 13); EXPECT_EQ(t1->getRecord(5), 13);
@ -160,7 +160,7 @@ TEST_F(StorageTransactionsTest, ConcurentReading) {
EXPECT_EQ(t1->count(txn), 1); EXPECT_EQ(t1->count(txn), 1);
EXPECT_EQ(t1->count(), size); EXPECT_EQ(t1->count(), size);
db->commitTransaction(txn); txn.commit();
EXPECT_FALSE(t1->checkRecord(5)); EXPECT_FALSE(t1->checkRecord(5));
EXPECT_EQ(t1->count(), 1); EXPECT_EQ(t1->count(), 1);
@ -182,7 +182,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
int pid = fork(); int pid = fork();
if (pid == 0) { // I am the child if (pid == 0) { // I am the child
std::cout << "beggining second transaction" << std::endl; std::cout << "beggining second transaction" << std::endl;
LMDBAL::TransactionID txn2 = db->beginTransaction(); //<--- this is where the execution should pause LMDBAL::WriteTransaction txn2 = db->beginTransaction(); //<--- this is where the execution should pause
//and wait for the first transaction to get finished //and wait for the first transaction to get finished
std::cout << "checking result of the first transaction value" << std::endl; std::cout << "checking result of the first transaction value" << std::endl;
EXPECT_EQ(t1->getRecord(5, txn2), 812); EXPECT_EQ(t1->getRecord(5, txn2), 812);
@ -197,13 +197,13 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
EXPECT_EQ(t1->getRecord(5), 812); EXPECT_EQ(t1->getRecord(5), 812);
std::cout << "commiting second transaction" << std::endl; std::cout << "commiting second transaction" << std::endl;
db->commitTransaction(txn2); txn2.commit();
std::cout << "quitting child thread" << std::endl; std::cout << "quitting child thread" << std::endl;
exit(testing::Test::HasFailure()); exit(testing::Test::HasFailure());
} else { // I am the parent } else { // I am the parent
std::cout << "beggining first transaction" << std::endl; std::cout << "beggining first transaction" << std::endl;
LMDBAL::TransactionID txn1 = db->beginTransaction(); LMDBAL::WriteTransaction txn1 = db->beginTransaction();
std::cout << "putting parent thread to sleep for 5 ms" << std::endl; std::cout << "putting parent thread to sleep for 5 ms" << std::endl;
usleep(5); usleep(5);
@ -218,7 +218,7 @@ TEST_F(StorageTransactionsTest, ConcurentModification) {
EXPECT_FALSE(t1->checkRecord(5)); EXPECT_FALSE(t1->checkRecord(5));
std::cout << "commiting first transaction" << std::endl; std::cout << "commiting first transaction" << std::endl;
db->commitTransaction(txn1); txn1.commit();
std::cout << "waiting for the other thread to finish" << std::endl; std::cout << "waiting for the other thread to finish" << std::endl;
ASSERT_EQ(0, waitForChildFork(pid)); //child process should have no problems ASSERT_EQ(0, waitForChildFork(pid)); //child process should have no problems