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
## LMDBAL 0.5.1 (UNRELEASED)
### Improvements
- RAII transactions
- reduced overhead for private transaction finctions
## LMDBAL 0.5.0 (October 15, 2023)
### New Features
- duplicates support (only for table)

View File

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

View File

@ -1,6 +1,6 @@
# Maintainer: Yury Gubich <blue@macaw.me>
pkgname=lmdbal
pkgver=0.5.0
pkgver=0.5.1
pkgrel=1
pkgdesc="LMDB Abstraction Layer, qt5 version"
arch=('i686' 'x86_64')
@ -11,7 +11,7 @@ makedepends=('cmake>=3.16' 'gcc')
optdepends=()
source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz")
sha256sums=('df1a9687d81d609d160754285f2613d7e07fc6deb781d0fb0084e4857ea82e95')
sha256sums=('SKIP')
build() {
cd "$srcdir/$pkgname"
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
storage.cpp
base.cpp
transaction.cpp
)
set(HEADERS
@ -29,11 +30,9 @@ set(HEADERS
serializer_qstring.hpp
serializer_qbytearray.hpp
operators.hpp
transaction.h
)
target_sources(${PROJECT_NAME} PRIVATE
${SOURCES}
${HEADERS}
)
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
install(FILES ${HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_LOW})

View File

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

View File

@ -36,6 +36,8 @@
namespace LMDBAL {
class iStorage;
class Transaction;
class WriteTransaction;
template<class T>
class Serializer;
@ -51,6 +53,8 @@ typedef uint32_t SizeType; /**<\brief All LMDBAL si
class Base {
friend class iStorage;
friend class Transaction;
friend class WriteTransaction;
public:
Base(const QString& name, uint16_t mapSize = 10);
@ -62,12 +66,11 @@ public:
bool removeDirectory();
QString createDirectory();
QString getName() const;
QString getPath() const;
void drop();
TransactionID beginReadOnlyTransaction() const;
TransactionID beginTransaction() const;
void commitTransaction(TransactionID id);
void abortTransaction(TransactionID id) const;
Transaction beginReadOnlyTransaction() const;
WriteTransaction beginTransaction();
template <class K, class V>
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::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 beginTransaction(const std::string& storageName) const;
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() override;
virtual void handleDrop() override;
virtual void transactionStarted(TransactionID txn, bool readOnly) const override;
virtual void transactionCommited(TransactionID txn) override;
virtual void transactionAborted(TransactionID txn) const override;
virtual void discoveredRecord(const K& key, const V& value) const override;
virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) const override;
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:
void handleMode() const;
@ -72,37 +87,34 @@ private:
void handleForceRecord(const K& key, const V& value, bool added);
void handleReplaceAll(std::map<K, V>* data);
void handleAddRecords(const std::map<K, V>& data, bool overwrite, SizeType newSize);
void handleDrop();
void appendToCache(const K& key, const V& value) const;
public:
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, TransactionID txn) 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, TransactionID txn) 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, TransactionID txn) 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, TransactionID txn) 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(TransactionID txn) 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, 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, TransactionID txn, bool overwrite = false) override;
protected:
/**

View File

@ -75,8 +75,6 @@ void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value) {
template<class K, class V>
void LMDBAL::Cache<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
iStorage::ensureOpened(iStorage::addRecordMethodName);
if (cache->count(key) > 0)
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>
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);
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>
void LMDBAL::Cache<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
iStorage::ensureOpened(iStorage::changeRecordMethodName);
if (mode == Mode::full) {
typename std::map<K, V>::iterator itr = cache->find(key);
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>
V LMDBAL::Cache<K, V>::getRecord(const K& key, TransactionID txn) const {
iStorage::ensureOpened(iStorage::getRecordMethodName);
V value;
Cache<K, V>::getRecord(key, value, txn);
return value;
@ -257,8 +249,6 @@ V LMDBAL::Cache<K, V>::getRecord(const K& key, TransactionID txn) const {
template<class K, class V>
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
//I will be able to see them among pending changes
//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>
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
//I will be able to see them among pending changes
//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>
std::map<K, V> LMDBAL::Cache<K, V>::readAll(TransactionID txn) const {
iStorage::ensureOpened(iStorage::readAllMethodName);
std::map<K, V> out;
readAll(out, txn);
@ -507,8 +493,6 @@ std::map<K, V> LMDBAL::Cache<K, V>::readAll(TransactionID txn) const {
template<class K, class V>
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);
if (tc != transactionCache->end()) {
Queue& queue = tc->second;
@ -676,8 +660,6 @@ void LMDBAL::Cache<K, V>::removeRecord(const K& key) {
template<class K, class V>
void LMDBAL::Cache<K, V>::removeRecord(const K& key, TransactionID txn) {
iStorage::ensureOpened(iStorage::removeRecordMethodName);
bool noKey = false;
if (mode != Mode::full)
noKey = cache->count(key) == 0;
@ -729,7 +711,6 @@ uint32_t LMDBAL::Cache<K, V>::count() const {
template<class K, class V>
uint32_t LMDBAL::Cache<K, V>::count(TransactionID txn) const {
int32_t diff = 0;
bool currentTransaction = false;
typename TransactionCache::const_iterator tc = transactionCache->find(txn);
@ -800,10 +781,15 @@ void LMDBAL::Cache<K, V>::handleMode() const {
}
template<class K, class V>
int LMDBAL::Cache<K, V>::drop(TransactionID transaction) {
int res = Storage<K, V>::drop(transaction);
int LMDBAL::Cache<K, V>::drop(const WriteTransaction& 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())
tc->second.emplace_back(Operation::drop, nullptr);

View File

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

View File

@ -20,6 +20,7 @@
#define LMDBAL_CURSOR_HPP
#include "cursor.h"
#include <iostream>
/**
* \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!
* 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
* \throws LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb, or to begin a transaction
* \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, or to begin a transaction
*/
template<class K, class V>
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!
* 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
* \throws LMDBAL::Unknown thrown if there was a problem opening the cursor by the lmdb
* \param[in] transaction - a transaction, can be read only
*
* \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>
void LMDBAL::Cursor<K, V>::open (TransactionID txn) const {
void LMDBAL::Cursor<K, V>::open (const Transaction& transaction) const {
storage->ensureOpened(openCursorMethodName);
TransactionID txn = storage->extractTransactionId(transaction, openCursorMethodName);
switch (state) {
case closed: {
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
*
* \throws 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::Closed thrown if you try to renew the cursor on a closed database
* \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>
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
*
* \throws 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::Closed thrown if you try to renew the cursor on a closed database
* \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>
void LMDBAL::Cursor<K, V>::renew (TransactionID txn) const {
void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) const {
storage->ensureOpened(renewCursorMethodName);
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
switch (state) {
case openedPrivate: {
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] 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
* \throws 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::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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] 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
* \throws 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::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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] 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
* \throws 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::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if the cursor already was on the last element or if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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] 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
* \throws 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::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if the cursor already was on the first element or if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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] 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
* \throws 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::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound probably never thrown but there might be still some corner case I don't know about
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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
*
* \throws 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
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
* \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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
*
* \throws 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
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
* \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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
*
* \throws 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
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
* \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if the cursor already was on the last element or if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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
*
* \throws 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
* \throws LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
* \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound thrown if the cursor already was on the first element or if there are no elements in the storage
* \exception LMDBAL::Unknown thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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
*
* \throws 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
* \throws LMDBAL::Unknown thrown if there was no positioning operation before of if there was some unexpected problem with lmdb
* \exception LMDBAL::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound probably never thrown but there might be still some corner case I don't know about
* \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>
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] 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
* \throws 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::CursorNotReady thrown if you try to call this method on a closed cursor
* \exception LMDBAL::NotFound mostly thrown if the query wasn't found
* \exception LMDBAL::Unknown mostly thrown if there was some unexpected problem with lmdb
*/
template<class K, class V>
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";
}
LMDBAL::Exist::Exist(
const std::string& p_key,
const std::string& p_dbName,
@ -130,6 +129,29 @@ std::string LMDBAL::Exist::getMessage() const {
+ " 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(
const std::string& p_dbName,
const std::string& message,

View File

@ -177,6 +177,27 @@ private:
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
*/

View File

@ -56,6 +56,23 @@ void LMDBAL::iStorage::close() {
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
@ -68,7 +85,7 @@ void LMDBAL::iStorage::close() {
void LMDBAL::iStorage::drop() {
ensureOpened(dropMethodName);
TransactionID txn = db->beginTransaction();
TransactionID txn = beginTransaction();
int rc = iStorage::drop(txn);
if (rc != MDB_SUCCESS) {
abortTransaction(txn);
@ -76,6 +93,7 @@ void LMDBAL::iStorage::drop() {
}
db->commitTransaction(txn);
handleDrop();
}
/**
@ -90,6 +108,21 @@ int LMDBAL::iStorage::drop(TransactionID transaction) {
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
*
@ -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
* \returns amount of records in the storage
@ -143,6 +176,21 @@ LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const {
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)
*
@ -391,5 +439,4 @@ void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
UNUSED(txn);}
void LMDBAL::iStorage::handleDrop() {}

View File

@ -25,6 +25,7 @@
#include "base.h"
#include "serializer.h"
#include "cursor.h"
#include "transaction.h"
class BaseTest;
class DuplicatesTest;
@ -46,6 +47,7 @@ protected:
*/
virtual int open(MDB_txn * transaction) = 0;
virtual void close();
virtual void handleDrop();
bool isDBOpened() const;
const std::string& dbName() const;
@ -62,6 +64,7 @@ protected:
void throwNotFound(const std::string& key) const;
void throwCursorNotReady(const std::string& method) const;
TransactionID extractTransactionId(const Transaction& txn, const std::string& action = "") const;
TransactionID beginReadOnlyTransaction() const;
TransactionID beginTransaction() const;
void commitTransaction(TransactionID id);
@ -69,12 +72,14 @@ protected:
virtual void transactionStarted(TransactionID txn, bool readOnly) const;
virtual void transactionCommited(TransactionID txn);
virtual void transactionAborted(TransactionID txn) const;
virtual int drop(TransactionID transaction);
virtual SizeType count(TransactionID txn) const;
public:
virtual void drop();
virtual int drop(TransactionID transaction);
virtual int drop(const WriteTransaction& txn);
virtual SizeType count() const;
virtual SizeType count(TransactionID txn) const;
virtual SizeType count(const Transaction& txn) const;
protected:
MDB_dbi dbi; /**<\brief lmdb storage handle*/
@ -118,30 +123,42 @@ protected:
virtual void discoveredRecord(const K& key, const V& value, TransactionID txn) 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:
using iStorage::drop;
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, 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, 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, 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, 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, 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, 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(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, 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, 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, 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();
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)
*
* 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().
* \brief Adds a key-value record to the storage (private transaction variant)
*
* 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.
@ -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!
*
* \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
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
ensureOpened(addRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key);
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));
}
/**
* \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
*
@ -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.
* 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.
* 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!
* \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
*/
template<class K, class V>
bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) {
ensureOpened(forceRecordMethodName);
bool added;
if (duplicates) {
try {
@ -216,6 +230,33 @@ bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, Transactio
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.
*
@ -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.
* 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
*
* 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::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
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
ensureOpened(changeRecordMethodName);
MDB_cursor* cursor;
int rc = mdb_cursor_open(txn, dbi, &cursor);
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));
}
/**
* \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
@ -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)
*
* 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().
* \brief Gets the record from the database (private transaction variant)
*
* 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
*
* \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
*/
template<class K, class V>
V LMDBAL::Storage<K, V>::getRecord(const K& key, TransactionID txn) const {
ensureOpened(getRecordMethodName);
V value;
Storage<K, V>::getRecord(key, value, txn);
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().
*
* 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"
*
* \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[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
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn) const {
ensureOpened(getRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key);
MDB_val lmdbData;
@ -440,6 +524,36 @@ void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn)
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
*
@ -467,22 +581,16 @@ bool LMDBAL::Storage<K, V>::checkRecord(const K& key) const {
}
/**
* \brief Chechs if storage has value (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().
* \brief Chechs if storage has value (private transaction variant)
*
* \param[in] key key of the record you look for
* \param[in] txn transaction ID, 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
*/
template<class K, class V>
bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const {
ensureOpened(checkRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key);
MDB_val lmdbData;
@ -496,6 +604,26 @@ bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const {
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
*
@ -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
* 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>
* (see LMDBAL::Storage::getRecord() description) is returned in the resulting map
*
* \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
*/
template<class K, class V>
std::map<K, V> LMDBAL::Storage<K, V>::readAll(TransactionID txn) const {
ensureOpened(readAllMethodName);
std::map<K, V> result;
Storage<K, V>::readAll(result, txn);
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
* 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>
* (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[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
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) const {
ensureOpened(readAllMethodName);
MDB_cursor* cursor;
MDB_val lmdbKey, lmdbData;
@ -610,6 +748,29 @@ void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) c
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
*
@ -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
* 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] 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
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, TransactionID txn) {
ensureOpened(replaceAllMethodName);
int rc = drop(txn);
if (rc != MDB_SUCCESS)
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
*
@ -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.
* You can obtain LMDBAL::TransactionID calling LMDBAL::Base::beginTransaction().
*
* \param[in] data the data to be added
* \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
* \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
*/
template<class K, class V>
uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite) {
ensureOpened(addRecordsMethodName);
MDB_val lmdbKey, lmdbData;
int rc;
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;
}
/**
* \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
*
@ -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
* 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] 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::Unknown thrown if something unexpected happend within lmdb
*/
template<class K, class V>
void LMDBAL::Storage<K, V>::removeRecord(const K& key, TransactionID txn) {
ensureOpened(removeRecordMethodName);
MDB_val lmdbKey = keySerializer.setData(key);
int rc = mdb_del(txn, dbi, &lmdbKey, NULL);
if (rc != MDB_SUCCESS)
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
*

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() {
transaction.terminate();
db->close();
db->removeDirectory();
delete db;
@ -35,7 +36,7 @@ protected:
static LMDBAL::Base* db;
static LMDBAL::Cursor<uint64_t, std::string>* cursor;
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>* emptyCache;
@ -44,7 +45,7 @@ protected:
LMDBAL::Base* CacheCursorTest::db = nullptr;
LMDBAL::Cursor<uint64_t, std::string>* CacheCursorTest::cursor = 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({
{245665783, "bothering nerds"},
@ -288,7 +289,7 @@ TEST_F(CacheCursorTest, CurrentPublic) {
}
TEST_F(CacheCursorTest, CornerCases) {
db->abortTransaction(transaction);
transaction.terminate();
EXPECT_THROW(cursor->current(), LMDBAL::Unknown);
cursor->close();

View File

@ -64,7 +64,7 @@ TEST_F(CacheTransactionsTest, Adding) {
EXPECT_EQ(c1->count(), 0);
EXPECT_EQ(c2->count(), 0);
LMDBAL::TransactionID txn = db->beginTransaction();
LMDBAL::WriteTransaction txn = db->beginTransaction();
c1->addRecord(5, 13, txn);
c1->addRecord(-53, 782, txn);
c1->addRecord(5892, -37829, txn);
@ -76,7 +76,7 @@ TEST_F(CacheTransactionsTest, Adding) {
EXPECT_EQ(c1->count(), 0);
EXPECT_EQ(c2->count(), 0);
db->commitTransaction(txn);
txn.commit();
EXPECT_EQ(c1->count(), 3);
EXPECT_EQ(c1->getRecord(5), 13);
@ -95,7 +95,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
LMDBAL::SizeType s1 = c1->count();
LMDBAL::SizeType s2 = c2->count();
LMDBAL::TransactionID txn = db->beginTransaction();
LMDBAL::WriteTransaction txn = db->beginTransaction();
c1->addRecord(18, 40, txn);
c1->addRecord(85, -4, txn);
c1->addRecord(-5, -3, txn);
@ -107,7 +107,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
EXPECT_EQ(c1->count(), s1);
EXPECT_EQ(c2->count(), s2);
db->abortTransaction(txn);
txn.abort();
EXPECT_EQ(c1->count(), s1);
EXPECT_EQ(c2->count(), s2);
@ -116,7 +116,7 @@ TEST_F(CacheTransactionsTest, Aborting) {
TEST_F(CacheTransactionsTest, Reading) {
EXPECT_EQ(db->ready(), true);
LMDBAL::TransactionID txn = db->beginReadOnlyTransaction();
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
EXPECT_EQ(c1->count(txn), 3);
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("prevent recovery", txn), -64.64);
db->abortTransaction(txn);
txn.terminate();
}
TEST_F(CacheTransactionsTest, ConcurentReading) {
EXPECT_EQ(db->ready(), true);
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), 13);
@ -160,7 +160,7 @@ TEST_F(CacheTransactionsTest, ConcurentReading) {
EXPECT_EQ(c1->count(txn), 1);
EXPECT_EQ(c1->count(), size);
db->commitTransaction(txn);
txn.commit();
EXPECT_FALSE(c1->checkRecord(5));
EXPECT_EQ(c1->count(), 1);
@ -183,7 +183,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
int pid = fork();
if (pid == 0) { // I am the child
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
std::cout << "checking result of the first transaction value" << std::endl;
EXPECT_EQ(c1->getRecord(5, txn2), 812);
@ -198,13 +198,13 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
EXPECT_EQ(c1->getRecord(5), 812);
std::cout << "commiting second transaction" << std::endl;
db->commitTransaction(txn2);
txn2.commit();
std::cout << "quitting child thread" << std::endl;
exit(testing::Test::HasFailure());
} else { // I am the parent
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;
usleep(5);
@ -219,7 +219,7 @@ TEST_F(CacheTransactionsTest, ConcurentModification) {
EXPECT_FALSE(c1->checkRecord(5));
std::cout << "commiting first transaction" << std::endl;
db->commitTransaction(txn1);
txn1.commit();
std::cout << "waiting for the other thread to finish" << std::endl;
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) {
LMDBAL::TransactionID txn = db->beginReadOnlyTransaction();
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
bool cycle;
LMDBAL::SizeType iterations;
@ -462,6 +462,5 @@ TEST_F(DuplicatesTest, GettingAllRecords) {
EXPECT_NE(iterations, 0);
EXPECT_NE(k4.size(), 0);
db->abortTransaction(txn);
txn.terminate();
}

View File

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

View File

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