forked from blue/lmdbal
Cursors get closed after transaction that open them
This commit is contained in:
parent
68ea7df6a9
commit
e88efb458f
@ -4,6 +4,7 @@
|
|||||||
### Improvements
|
### Improvements
|
||||||
- Less dereferencing
|
- Less dereferencing
|
||||||
- Transactions are now closed with the database
|
- Transactions are now closed with the database
|
||||||
|
- Cursors are now closed if the public transaction that opened them got closed
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- SIGSEGV on closing database with opened transactions
|
- SIGSEGV on closing database with opened transactions
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
# LMDBAL - Lightning Memory Data Base Abstraction Level
|
# LMDBAL - Lightning Memory Data Base Abstraction Level
|
||||||
|
|
||||||
[![AUR license](https://img.shields.io/aur/license/lmdbal?style=flat-square)](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md)
|
[![AUR license](https://img.shields.io/aur/license/lmdbal?style=flat-square)](https://git.macaw.me/blue/lmdbal/raw/branch/master/LICENSE.md)
|
||||||
[![AUR version](https://img.shields.io/aur/version/lmdbal?style=flat-square)](https://aur.archlinux.org/packages/lmdbal/)
|
[![AUR qt5 version](https://img.shields.io/aur/version/lmdbal-qt5?style=flat-square)](https://aur.archlinux.org/packages/lmdbal-qt5/)
|
||||||
|
[![AUR qt6 version](https://img.shields.io/aur/version/lmdbal-qt6?style=flat-square)](https://aur.archlinux.org/packages/lmdbal-qt6/)
|
||||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||||
[![Documentation](https://img.shields.io/badge/Documentation-HTML-green)](https://macaw.me/lmdbal/doc/html)
|
[![Documentation](https://img.shields.io/badge/Documentation-HTML-green)](https://macaw.me/lmdbal/doc/html)
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- a compiler (c++ would do)
|
- a c++ compiler (g++ would do)
|
||||||
- Qt 5 or 6 or higher (qt5-base or qt6-base would do)
|
- Qt 5 or 6 or higher (qt5-base or qt6-base would do)
|
||||||
- lmdb
|
- lmdb
|
||||||
- CMake 3.16 or higher
|
- CMake 3.16 or higher
|
||||||
|
@ -16,6 +16,7 @@ set(HEADERS
|
|||||||
cache.hpp
|
cache.hpp
|
||||||
operators.hpp
|
operators.hpp
|
||||||
transaction.h
|
transaction.h
|
||||||
|
icursor.h
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES})
|
target_sources(${LMDBAL_NAME} PRIVATE ${SOURCES})
|
||||||
|
@ -24,11 +24,12 @@
|
|||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
#include "icursor.h"
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
|
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
class Cursor {
|
class Cursor : public iCursor {
|
||||||
friend class Storage<K, V>;
|
friend class Storage<K, V>;
|
||||||
private:
|
private:
|
||||||
enum State { /**<Cursor state:*/
|
enum State { /**<Cursor state:*/
|
||||||
@ -71,9 +72,9 @@ public:
|
|||||||
void current(K& key, V& value) const;
|
void current(K& key, V& value) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual void terminated() override;
|
||||||
void dropped();
|
void dropped();
|
||||||
void freed();
|
void freed();
|
||||||
void terminated();
|
|
||||||
void operateCursorRead(K& key, V& value, MDB_cursor_op operation, const std::string& methodName, const std::string& operationName) const;
|
void operateCursorRead(K& key, V& value, MDB_cursor_op operation, const std::string& methodName, const std::string& operationName) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +85,10 @@ LMDBAL::Cursor<K, V>::Cursor(Cursor&& other):
|
|||||||
if (id != 0)
|
if (id != 0)
|
||||||
storage->cursors[id] = this;
|
storage->cursors[id] = this;
|
||||||
|
|
||||||
|
if (state == openedPublic)
|
||||||
|
storage->attachCursorToTransaction(id, cursor, this);
|
||||||
|
|
||||||
|
|
||||||
other.freed();
|
other.freed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +115,9 @@ LMDBAL::Cursor<K, V>& LMDBAL::Cursor<K, V>::operator = (Cursor&& other) {
|
|||||||
other.state = closed;
|
other.state = closed;
|
||||||
|
|
||||||
storage->cursors[id] = this;
|
storage->cursors[id] = this;
|
||||||
|
|
||||||
|
if (state == openedPublic)
|
||||||
|
storage->attachCursorToTransaction(id, cursor, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@ -184,7 +192,20 @@ bool LMDBAL::Cursor<K, V>::empty () const {
|
|||||||
*/
|
*/
|
||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cursor<K, V>::terminated () {
|
void LMDBAL::Cursor<K, V>::terminated () {
|
||||||
close(); //for now it's the same, but if I ever going to make writable cursor - here is where it's gonna be different
|
switch (state) {
|
||||||
|
case openedPublic:
|
||||||
|
storage->_mdbCursorClose(cursor);
|
||||||
|
|
||||||
|
state = closed;
|
||||||
|
break;
|
||||||
|
case openedPrivate:
|
||||||
|
storage->closeCursorTransaction(cursor);
|
||||||
|
|
||||||
|
state = closed;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,17 +226,11 @@ void LMDBAL::Cursor<K, V>::open () {
|
|||||||
if (empty())
|
if (empty())
|
||||||
throw CursorEmpty(openCursorMethodName);
|
throw CursorEmpty(openCursorMethodName);
|
||||||
|
|
||||||
storage->ensureOpened(openCursorMethodName);
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case closed: {
|
case closed:
|
||||||
TransactionID txn = storage->beginReadOnlyTransaction();
|
storage->openCursorTransaction(&cursor);
|
||||||
int result = storage->_mdbCursorOpen(txn, &cursor);
|
|
||||||
if (result != MDB_SUCCESS)
|
|
||||||
storage->throwUnknown(result, txn);
|
|
||||||
|
|
||||||
storage->transactionStarted(txn, true);
|
|
||||||
state = openedPrivate;
|
state = openedPrivate;
|
||||||
} break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -250,6 +265,7 @@ void LMDBAL::Cursor<K, V>::open (const Transaction& transaction) {
|
|||||||
if (result != MDB_SUCCESS)
|
if (result != MDB_SUCCESS)
|
||||||
storage->throwUnknown(result);
|
storage->throwUnknown(result);
|
||||||
|
|
||||||
|
storage->attachCursorToTransaction(id, cursor, this);
|
||||||
state = openedPublic;
|
state = openedPublic;
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
@ -285,15 +301,24 @@ void LMDBAL::Cursor<K, V>::renew () {
|
|||||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
||||||
storage->abortTransaction(txn);
|
storage->abortTransaction(txn);
|
||||||
storage->transactionAborted(txn);
|
storage->transactionAborted(txn);
|
||||||
[[fallthrough]];
|
|
||||||
|
txn = storage->beginReadOnlyTransaction();
|
||||||
|
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||||
|
if (result != MDB_SUCCESS)
|
||||||
|
storage->throwUnknown(result, txn);
|
||||||
|
|
||||||
|
storage->transactionStarted(txn, true);
|
||||||
|
state = openedPrivate;
|
||||||
}
|
}
|
||||||
case openedPublic: {
|
case openedPublic: {
|
||||||
|
storage->disconnectCursorFromTransaction(id, cursor);
|
||||||
TransactionID txn = storage->beginReadOnlyTransaction();
|
TransactionID txn = storage->beginReadOnlyTransaction();
|
||||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||||
if (result != MDB_SUCCESS)
|
if (result != MDB_SUCCESS)
|
||||||
storage->throwUnknown(result, txn);
|
storage->throwUnknown(result, txn);
|
||||||
|
|
||||||
storage->transactionStarted(txn, true);
|
storage->transactionStarted(txn, true);
|
||||||
|
storage->attachCursorToTransaction(id, cursor, this);
|
||||||
state = openedPrivate;
|
state = openedPrivate;
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
@ -330,17 +355,24 @@ void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
|
|||||||
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
TransactionID txn = storage->extractTransactionId(transaction, renewCursorMethodName);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case openedPrivate: {
|
case openedPrivate: {
|
||||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
TransactionID txnOld = storage->_mdbCursorTxn(cursor);
|
||||||
storage->abortTransaction(txn);
|
storage->abortTransaction(txnOld);
|
||||||
storage->transactionAborted(txn);
|
storage->transactionAborted(txnOld);
|
||||||
[[fallthrough]];
|
|
||||||
}
|
|
||||||
case openedPublic: {
|
|
||||||
int result = storage->_mdbCursorRenew(txn, cursor);
|
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||||
if (result != MDB_SUCCESS)
|
if (result != MDB_SUCCESS)
|
||||||
storage->throwUnknown(result);
|
storage->throwUnknown(result);
|
||||||
|
|
||||||
state = openedPublic;
|
state = openedPublic;
|
||||||
|
}
|
||||||
|
case openedPublic: {
|
||||||
|
storage->disconnectCursorFromTransaction(id, cursor);
|
||||||
|
int result = storage->_mdbCursorRenew(txn, cursor);
|
||||||
|
if (result != MDB_SUCCESS)
|
||||||
|
storage->throwUnknown(result);
|
||||||
|
|
||||||
|
storage->attachCursorToTransaction(id, cursor, this);
|
||||||
|
state = openedPublic;
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -360,19 +392,17 @@ void LMDBAL::Cursor<K, V>::renew (const Transaction& transaction) {
|
|||||||
template<class K, class V>
|
template<class K, class V>
|
||||||
void LMDBAL::Cursor<K, V>::close () {
|
void LMDBAL::Cursor<K, V>::close () {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case openedPublic: {
|
case openedPublic:
|
||||||
|
storage->disconnectCursorFromTransaction(id, cursor);
|
||||||
storage->_mdbCursorClose(cursor);
|
storage->_mdbCursorClose(cursor);
|
||||||
|
|
||||||
state = closed;
|
state = closed;
|
||||||
} break;
|
break;
|
||||||
case openedPrivate: {
|
case openedPrivate:
|
||||||
TransactionID txn = storage->_mdbCursorTxn(cursor);
|
storage->closeCursorTransaction(cursor);
|
||||||
storage->_mdbCursorClose(cursor);
|
|
||||||
storage->abortTransaction(txn);
|
|
||||||
storage->transactionAborted(txn);
|
|
||||||
|
|
||||||
state = closed;
|
state = closed;
|
||||||
} break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
32
src/icursor.h
Normal file
32
src/icursor.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* LMDB Abstraction Layer.
|
||||||
|
* Copyright (C) 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace LMDBAL {
|
||||||
|
|
||||||
|
class Transaction;
|
||||||
|
|
||||||
|
class iCursor {
|
||||||
|
friend class Transaction;
|
||||||
|
protected:
|
||||||
|
virtual ~iCursor() {}
|
||||||
|
virtual void terminated() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -50,7 +50,7 @@ LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicate
|
|||||||
LMDBAL::iStorage::~iStorage() {}
|
LMDBAL::iStorage::~iStorage() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief A private virtual function I need to close each storage in the database
|
* \brief A private virtual function to close each storage in the database
|
||||||
*/
|
*/
|
||||||
void LMDBAL::iStorage::close() {
|
void LMDBAL::iStorage::close() {
|
||||||
mdb_dbi_close(db->environment, dbi);
|
mdb_dbi_close(db->environment, dbi);
|
||||||
@ -159,6 +159,73 @@ LMDBAL::SizeType LMDBAL::iStorage::count() const {
|
|||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Links cursor to the transaction it has been opened with
|
||||||
|
*
|
||||||
|
* This a service function is designed to be called by from LMDBAL::Cursor
|
||||||
|
* Cursor must be opened by a public transaction
|
||||||
|
*
|
||||||
|
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||||
|
*/
|
||||||
|
|
||||||
|
void LMDBAL::iStorage::attachCursorToTransaction (uint32_t cursorId, MDB_cursor* cursorHandle, iCursor* pointer) const {
|
||||||
|
TransactionID txnID = _mdbCursorTxn(cursorHandle);
|
||||||
|
Transaction* txn = db->transactions.at(txnID);
|
||||||
|
txn->cursors[cursorId] = pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Opens a transaction that is ment to be private to LMDBAL::Cursor, but public to the LMDBAL::Storage
|
||||||
|
*
|
||||||
|
* This method is ment to be called from LMDBAL::Cursor
|
||||||
|
*
|
||||||
|
* \param[out] cursor - cursor handle
|
||||||
|
*
|
||||||
|
* \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
|
||||||
|
*/
|
||||||
|
void LMDBAL::iStorage::openCursorTransaction (MDB_cursor** cursor) const {
|
||||||
|
ensureOpened(openCursorTransactionMethodName);
|
||||||
|
|
||||||
|
TransactionID txn = beginReadOnlyTransaction();
|
||||||
|
int result = _mdbCursorOpen(txn, cursor);
|
||||||
|
if (result != MDB_SUCCESS)
|
||||||
|
throwUnknown(result, txn);
|
||||||
|
|
||||||
|
transactionStarted(txn, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Closes transaction that is private to LMDBAL::Cursor, but public to the LMDBAL::Storage
|
||||||
|
*
|
||||||
|
* This method is ment to be called from LMDBAL::Cursor
|
||||||
|
*
|
||||||
|
* \param[in] cursor - cursor handle
|
||||||
|
*/
|
||||||
|
void LMDBAL::iStorage::closeCursorTransaction (MDB_cursor* cursor) const {
|
||||||
|
TransactionID txn = _mdbCursorTxn(cursor);
|
||||||
|
_mdbCursorClose(cursor);
|
||||||
|
abortTransaction(txn);
|
||||||
|
transactionAborted(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Disconnects cursor from the transaction it has been opened with
|
||||||
|
*
|
||||||
|
* This a service function is designed to be called by from LMDBAL::Cursor
|
||||||
|
* Cursor must be still opened by a public transaction
|
||||||
|
*
|
||||||
|
* \exception std::out_of_range thrown if LMDBAL::Transaction pointer wasn't found in the LMDBAL::Base
|
||||||
|
*/
|
||||||
|
|
||||||
|
void LMDBAL::iStorage::disconnectCursorFromTransaction (uint32_t cursorId, MDB_cursor* cursorHandle) const {
|
||||||
|
TransactionID txnID = _mdbCursorTxn(cursorHandle);
|
||||||
|
Transaction* txn = db->transactions.at(txnID);
|
||||||
|
txn->cursors.erase(cursorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Storage size (private transaction variant)
|
* \brief Storage size (private transaction variant)
|
||||||
*
|
*
|
||||||
|
@ -76,6 +76,11 @@ protected:
|
|||||||
virtual int drop(TransactionID transaction);
|
virtual int drop(TransactionID transaction);
|
||||||
virtual SizeType count(TransactionID txn) const;
|
virtual SizeType count(TransactionID txn) const;
|
||||||
|
|
||||||
|
void openCursorTransaction(MDB_cursor** cursor) const;
|
||||||
|
void closeCursorTransaction(MDB_cursor* cursor) const;
|
||||||
|
void attachCursorToTransaction(uint32_t cursorId, MDB_cursor* cursorHandle, iCursor* pointer) const;
|
||||||
|
void disconnectCursorFromTransaction(uint32_t cursorId, MDB_cursor* cursorHandle) const;
|
||||||
|
|
||||||
int _mdbOpen(MDB_txn* txn, unsigned int flags = 0);
|
int _mdbOpen(MDB_txn* txn, unsigned int flags = 0);
|
||||||
|
|
||||||
int _mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
int _mdbPut(MDB_txn* txn, MDB_val& key, MDB_val& data, unsigned int flags = 0);
|
||||||
@ -121,6 +126,7 @@ protected:
|
|||||||
inline static const std::string readAllMethodName = "readAllRecord"; /**<\brief member function name, just for exceptions*/
|
inline static const std::string readAllMethodName = "readAllRecord"; /**<\brief member function name, just for exceptions*/
|
||||||
inline static const std::string replaceAllMethodName = "replaceAll"; /**<\brief member function name, just for exceptions*/
|
inline static const std::string replaceAllMethodName = "replaceAll"; /**<\brief member function name, just for exceptions*/
|
||||||
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
|
inline static const std::string addRecordsMethodName = "addRecords"; /**<\brief member function name, just for exceptions*/
|
||||||
|
inline static const std::string openCursorTransactionMethodName = "openCursorTransaction"; /**<\brief member function name, just for exceptions*/
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <class K, class V>
|
template <class K, class V>
|
||||||
|
@ -40,7 +40,8 @@
|
|||||||
LMDBAL::Transaction::Transaction():
|
LMDBAL::Transaction::Transaction():
|
||||||
txn(nullptr),
|
txn(nullptr),
|
||||||
active(false),
|
active(false),
|
||||||
parent(nullptr)
|
parent(nullptr),
|
||||||
|
cursors()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +50,8 @@ LMDBAL::Transaction::Transaction():
|
|||||||
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
||||||
txn(txn),
|
txn(txn),
|
||||||
active(true),
|
active(true),
|
||||||
parent(parent)
|
parent(parent),
|
||||||
|
cursors()
|
||||||
{
|
{
|
||||||
parent->transactions[txn] = this;
|
parent->transactions[txn] = this;
|
||||||
}
|
}
|
||||||
@ -60,7 +62,8 @@ LMDBAL::Transaction::Transaction(TransactionID txn, const Base* parent) :
|
|||||||
LMDBAL::Transaction::Transaction(Transaction&& other):
|
LMDBAL::Transaction::Transaction(Transaction&& other):
|
||||||
txn(other.txn),
|
txn(other.txn),
|
||||||
active(other.active),
|
active(other.active),
|
||||||
parent(other.parent)
|
parent(other.parent),
|
||||||
|
cursors(other.cursors)
|
||||||
{
|
{
|
||||||
if (active) {
|
if (active) {
|
||||||
parent->transactions[txn] = this;
|
parent->transactions[txn] = this;
|
||||||
@ -88,6 +91,7 @@ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
|||||||
txn = other.txn;
|
txn = other.txn;
|
||||||
active = other.active;
|
active = other.active;
|
||||||
parent = other.parent;
|
parent = other.parent;
|
||||||
|
cursors = other.cursors;
|
||||||
|
|
||||||
if (active) {
|
if (active) {
|
||||||
parent->transactions[txn] = this;
|
parent->transactions[txn] = this;
|
||||||
@ -105,13 +109,14 @@ LMDBAL::Transaction& LMDBAL::Transaction::operator=(Transaction&& other) {
|
|||||||
*/
|
*/
|
||||||
void LMDBAL::Transaction::terminate() {
|
void LMDBAL::Transaction::terminate() {
|
||||||
if (active) {
|
if (active) {
|
||||||
|
closeCursors();
|
||||||
parent->abortTransaction(txn);
|
parent->abortTransaction(txn);
|
||||||
|
|
||||||
parent->transactions.erase(txn);
|
parent->transactions.erase(txn);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Resets inner transaction properties to inactive state
|
* \brief Resets inner transaction properties to inactive state
|
||||||
*/
|
*/
|
||||||
@ -119,8 +124,16 @@ void LMDBAL::Transaction::reset() {
|
|||||||
active = false;
|
active = false;
|
||||||
txn = nullptr;
|
txn = nullptr;
|
||||||
parent = nullptr;
|
parent = nullptr;
|
||||||
|
cursors.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Closes attached curors;
|
||||||
|
*/
|
||||||
|
void LMDBAL::Transaction::closeCursors () {
|
||||||
|
for (const std::pair<const uint32_t, iCursor*>& pair : cursors)
|
||||||
|
pair.second->terminated();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns transaction states
|
* \brief Returns transaction states
|
||||||
@ -197,8 +210,8 @@ void LMDBAL::WriteTransaction::abort() {
|
|||||||
*/
|
*/
|
||||||
void LMDBAL::WriteTransaction::commit() {
|
void LMDBAL::WriteTransaction::commit() {
|
||||||
if (active) {
|
if (active) {
|
||||||
|
closeCursors();
|
||||||
const_cast<Base*>(parent)->commitTransaction(txn);
|
const_cast<Base*>(parent)->commitTransaction(txn);
|
||||||
|
|
||||||
parent->transactions.erase(txn);
|
parent->transactions.erase(txn);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
#include "icursor.h"
|
||||||
|
|
||||||
namespace LMDBAL {
|
namespace LMDBAL {
|
||||||
class iStorage;
|
class iStorage;
|
||||||
@ -40,11 +41,13 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
Transaction(TransactionID txn, const Base* parent);
|
Transaction(TransactionID txn, const Base* parent);
|
||||||
void reset();
|
void reset();
|
||||||
|
void closeCursors();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TransactionID txn; /**<\brief Transaction inner handler*/
|
TransactionID txn; /**<\brief Transaction inner handler*/
|
||||||
bool active; /**<\brief Transaction state*/
|
bool active; /**<\brief Transaction state*/
|
||||||
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
const Base* parent; /**<\brief Pointer to the database this transaction belongs to*/
|
||||||
|
std::map<uint32_t, iCursor*> cursors; /**<\brief a collection of cursors curently opened under this transaction*/
|
||||||
};
|
};
|
||||||
|
|
||||||
class WriteTransaction : public Transaction {
|
class WriteTransaction : public Transaction {
|
||||||
|
@ -402,7 +402,7 @@ TEST_F(CacheCursorTest, CursorRAIIBehaviour) {
|
|||||||
|
|
||||||
TEST_F(CacheCursorTest, CornerCases) {
|
TEST_F(CacheCursorTest, CornerCases) {
|
||||||
transaction.terminate();
|
transaction.terminate();
|
||||||
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
|
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
||||||
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyCache->createCursor();
|
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyCache->createCursor();
|
||||||
|
@ -380,7 +380,7 @@ TEST_F(StorageCursorTest, CursorRAIIBehaviour) {
|
|||||||
TEST_F(StorageCursorTest, CornerCases) {
|
TEST_F(StorageCursorTest, CornerCases) {
|
||||||
EXPECT_EQ(getTableCursorsSize(), 1);
|
EXPECT_EQ(getTableCursorsSize(), 1);
|
||||||
transaction.terminate();
|
transaction.terminate();
|
||||||
EXPECT_THROW(cursor.current(), LMDBAL::Unknown);
|
EXPECT_THROW(cursor.current(), LMDBAL::CursorNotReady);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
|
|
||||||
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyTable->createCursor();
|
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = emptyTable->createCursor();
|
||||||
@ -414,4 +414,18 @@ TEST_F(StorageCursorTest, CornerCases) {
|
|||||||
EXPECT_EQ(element.first, reference->first);
|
EXPECT_EQ(element.first, reference->first);
|
||||||
EXPECT_EQ(element.second, reference->second);
|
EXPECT_EQ(element.second, reference->second);
|
||||||
EXPECT_THROW(cursor.prev(), LMDBAL::NotFound);
|
EXPECT_THROW(cursor.prev(), LMDBAL::NotFound);
|
||||||
|
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StorageCursorTest, TerminatedTransaction) {
|
||||||
|
LMDBAL::Cursor<uint64_t, std::string> emptyCursor = table->createCursor();
|
||||||
|
|
||||||
|
{
|
||||||
|
LMDBAL::Transaction txn = db->beginReadOnlyTransaction();
|
||||||
|
cursor.open(txn);
|
||||||
|
EXPECT_NO_THROW(cursor.first());
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_FALSE(cursor.opened());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user