aboutsummaryrefslogtreecommitdiff
path: root/src/blockchain_db
diff options
context:
space:
mode:
Diffstat (limited to 'src/blockchain_db')
-rw-r--r--src/blockchain_db/CMakeLists.txt1
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp93
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h11
-rw-r--r--src/blockchain_db/blockchain_db.cpp11
-rw-r--r--src/blockchain_db/blockchain_db.h2
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp637
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h78
7 files changed, 538 insertions, 295 deletions
diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt
index 4953d92a6..39459d5c4 100644
--- a/src/blockchain_db/CMakeLists.txt
+++ b/src/blockchain_db/CMakeLists.txt
@@ -65,6 +65,7 @@ target_link_libraries(blockchain_db
crypto
cryptonote_core
${Boost_DATE_TIME_LIBRARY}
+ ${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
${LMDB_LIBRARY}
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index f035bf4c4..1fef9e619 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -135,7 +135,7 @@ const unsigned int DB_BUFFER_LENGTH = 32 * MB;
const unsigned int DB_DEF_CACHESIZE = 256 * MB;
#if defined(BDB_BULK_CAN_THREAD)
-const unsigned int DB_BUFFER_COUNT = std::thread::hardware_concurrency();
+const unsigned int DB_BUFFER_COUNT = boost::thread::hardware_concurrency();
#else
const unsigned int DB_BUFFER_COUNT = 1;
#endif
@@ -1073,8 +1073,10 @@ void BlockchainBDB::sync()
m_spent_keys->sync(0);
- m_hf_starting_heights->sync(0);
- m_hf_versions->sync(0);
+ if (m_hf_starting_heights != nullptr)
+ m_hf_starting_heights->sync(0);
+ if (m_hf_versions != nullptr)
+ m_hf_versions->sync(0);
m_properties->sync(0);
}
@@ -1835,7 +1837,7 @@ void BlockchainBDB::set_batch_transactions(bool batch_transactions)
LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled"));
}
-void BlockchainBDB::block_txn_start()
+void BlockchainBDB::block_txn_start(bool readonly)
{
// TODO
}
@@ -2208,12 +2210,91 @@ uint64_t BlockchainBDB::get_hard_fork_starting_height(uint8_t version) const
void BlockchainBDB::check_hard_fork_info()
{
- /* FIXME: Some other time */
+ LOG_PRINT_L3("BlockchainBDB::" << __func__);
+ check_open();
+
+ if (m_hf_versions == nullptr)
+ {
+ LOG_PRINT_L0("hf versions DB not open, so not checking");
+ return;
+ }
+
+ DB_BTREE_STAT* db_stat1, * db_stat2;
+
+ // DB_FAST_STAT can apparently cause an incorrect number of records
+ // to be returned. The flag should be set to 0 instead if this proves
+ // to be the case.
+
+ // Set txn to NULL and DB_FAST_STAT to zero (0) for reliability.
+ m_blocks->stat(NULL, &db_stat1, 0);
+ m_hf_versions->stat(NULL, &db_stat2, 0);
+ if (db_stat1->bt_nkeys != db_stat2->bt_nkeys)
+ {
+ LOG_PRINT_L0("num blocks " << db_stat1->bt_nkeys << " != " << "num hf_versions " << db_stat2->bt_nkeys << " - will clear the two hard fork DBs");
+
+ bdb_txn_safe txn;
+ bdb_txn_safe* txn_ptr = &txn;
+ if (m_write_txn)
+ txn_ptr = m_write_txn;
+ else
+ {
+ if (m_env->txn_begin(NULL, txn, 0))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+ }
+
+ try
+ {
+ uint32_t count;
+ m_hf_starting_heights->truncate(*txn_ptr, &count, 0);
+ LOG_PRINT_L0("hf_starting_heights count: " << count);
+ m_hf_versions->truncate(*txn_ptr, &count, 0);
+ LOG_PRINT_L0("hf_versions count: " << count);
+
+ if (!m_write_txn)
+ txn.commit();
+ }
+ catch (const std::exception& e)
+ {
+ throw0(DB_ERROR(std::string("Failed to clear two hard fork DBs: ").append(e.what()).c_str()));
+ }
+ }
+ delete db_stat1;
+ delete db_stat2;
}
void BlockchainBDB::drop_hard_fork_info()
{
- /* TODO */
+ LOG_PRINT_L3("BlockchainBDB::" << __func__);
+ check_open();
+
+ bdb_txn_safe txn;
+ bdb_txn_safe* txn_ptr = &txn;
+ if (m_write_txn)
+ txn_ptr = m_write_txn;
+ else
+ {
+ if (m_env->txn_begin(NULL, txn, 0))
+ throw0(DB_ERROR("Failed to create a transaction for the db"));
+ }
+
+ try
+ {
+ m_hf_starting_heights->close(0);
+ m_hf_versions->close(0);
+ m_hf_starting_heights = nullptr;
+ m_hf_versions = nullptr;
+ if (m_env->dbremove(*txn_ptr, BDB_HF_STARTING_HEIGHTS, NULL, 0) != 0)
+ LOG_ERROR("Error removing hf_starting_heights");
+ if (m_env->dbremove(*txn_ptr, BDB_HF_VERSIONS, NULL, 0) != 0)
+ LOG_ERROR("Error removing hf_versions");
+
+ if (!m_write_txn)
+ txn.commit();
+ }
+ catch (const std::exception& e)
+ {
+ throw0(DB_ERROR(std::string("Failed to drop hard fork info: ").append(e.what()).c_str()));
+ }
}
void BlockchainBDB::set_hard_fork_version(uint64_t height, uint8_t version)
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index 42119da93..d7cbd24e7 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -31,6 +31,7 @@
#include "cryptonote_protocol/blobdatatype.h" // for type blobdata
#include <unordered_map>
+#include <condition_variable>
// ND: Enables multi-threaded bulk reads for when getting indices.
// TODO: Disabled for now, as it doesn't seem to provide noticeable improvements (??. Reason: TBD.
@@ -129,7 +130,7 @@ public:
T acquire_buffer()
{
- std::unique_lock<std::mutex> lock(m_lock);
+ boost::unique_lock<boost::mutex> lock(m_lock);
m_cv.wait(lock, [&]{ return m_count > 0; });
--m_count;
@@ -153,7 +154,7 @@ public:
void release_buffer(T buffer)
{
- std::unique_lock<std::mutex> lock(m_lock);
+ boost::unique_lock<boost::mutex> lock(m_lock);
assert(buffer != nullptr);
auto it = m_buffer_map.find(buffer);
@@ -195,10 +196,10 @@ private:
std::vector<T> m_buffers;
std::unordered_map<T, size_t> m_buffer_map;
- std::condition_variable m_cv;
+ boost::condition_variable m_cv;
std::vector<bool> m_open_slot;
size_t m_count;
- std::mutex m_lock;
+ boost::mutex m_lock;
size_t m_buffer_count;
};
@@ -328,7 +329,7 @@ public:
virtual void batch_stop();
virtual void batch_abort();
- virtual void block_txn_start();
+ virtual void block_txn_start(bool readonly);
virtual void block_txn_stop();
virtual void block_txn_abort();
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 68bef3892..a66f4a403 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -99,7 +99,7 @@ uint64_t BlockchainDB::add_block( const block& blk
, const std::vector<transaction>& txs
)
{
- block_txn_start();
+ block_txn_start(false);
TIME_MEASURE_START(time1);
crypto::hash blk_hash = get_block_hash(blk);
@@ -227,6 +227,9 @@ void BlockchainDB::fixup()
static const char * const mainnet_genesis_hex = "418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3";
crypto::hash mainnet_genesis_hash;
epee::string_tools::hex_to_pod(mainnet_genesis_hex, mainnet_genesis_hash );
+ set_batch_transactions(true);
+ batch_start();
+
if (get_block_hash_from_height(0) == mainnet_genesis_hash)
{
// block 202612 (511 key images in 511 transactions)
@@ -762,9 +765,6 @@ void BlockchainDB::fixup()
"633cdedeb3b96ec4f234c670254c6f721e0b368d00b48c6b26759db7d62cf52d",
};
- set_batch_transactions(true);
- batch_start();
-
if (height() > 202612)
{
for (const auto &kis: key_images_202612)
@@ -791,9 +791,8 @@ void BlockchainDB::fixup()
}
}
}
-
- batch_stop();
}
+ batch_stop();
}
} // namespace cryptonote
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 2aa4506e6..3396b8c20 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -381,7 +381,7 @@ public:
virtual void batch_stop() = 0;
virtual void set_batch_transactions(bool) = 0;
- virtual void block_txn_start() = 0;
+ virtual void block_txn_start(bool readonly=false) = 0;
virtual void block_txn_stop() = 0;
virtual void block_txn_abort() = 0;
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 5119be3f5..69673215c 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -38,6 +38,10 @@
#include "crypto/crypto.h"
#include "profile_tools.h"
+#if defined(__i386) || defined(__x86_64)
+#define MISALIGNED_OK 1
+#endif
+
using epee::string_tools::pod_to_hex;
// Increase when the DB changes in a non backward compatible way, and there
@@ -61,45 +65,6 @@ inline void throw1(const T &e)
throw e;
}
-// cursor needs to be closed when it goes out of scope,
-// this helps if the function using it throws
-struct lmdb_cur
-{
- lmdb_cur(MDB_txn* txn, MDB_dbi dbi)
- {
- if (mdb_cursor_open(txn, dbi, &m_cur))
- throw0(cryptonote::DB_ERROR("Error opening db cursor"));
- done = false;
- }
-
- ~lmdb_cur()
- {
- close();
- }
-
- operator MDB_cursor*()
- {
- return m_cur;
- }
- operator MDB_cursor**()
- {
- return &m_cur;
- }
-
- void close()
- {
- if (!done)
- {
- mdb_cursor_close(m_cur);
- done = true;
- }
- }
-
-private:
- MDB_cursor* m_cur;
- bool done;
-};
-
template<typename T>
struct MDB_val_copy: public MDB_val
{
@@ -143,22 +108,26 @@ private:
std::unique_ptr<char[]> data;
};
-auto compare_uint64 = [](const MDB_val *a, const MDB_val *b)
+int compare_uint64(const MDB_val *a, const MDB_val *b)
{
+#ifdef MISALIGNED_OK
const uint64_t va = *(const uint64_t*)a->mv_data;
const uint64_t vb = *(const uint64_t*)b->mv_data;
+#else
+ uint64_t va, vb;
+ memcpy(&va, a->mv_data, sizeof(uint64_t));
+ memcpy(&vb, b->mv_data, sizeof(uint64_t));
+#endif
if (va < vb) return -1;
else if (va == vb) return 0;
else return 1;
};
-auto compare_uint8 = [](const MDB_val *a, const MDB_val *b)
+int compare_uint8(const MDB_val *a, const MDB_val *b)
{
const uint8_t va = *(const uint8_t*)a->mv_data;
const uint8_t vb = *(const uint8_t*)b->mv_data;
- if (va < vb) return -1;
- else if (va == vb) return 0;
- else return 1;
+ return va - vb;
};
int compare_hash32(const MDB_val *a, const MDB_val *b)
@@ -206,11 +175,6 @@ const char* const LMDB_HF_VERSIONS = "hf_versions";
const char* const LMDB_PROPERTIES = "properties";
-inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi, const std::string& error_string)
-{
- if (mdb_dbi_open(txn, name, flags, &dbi))
- throw0(cryptonote::DB_OPEN_FAILURE(error_string.c_str()));
-}
const std::string lmdb_error(const std::string& error_string, int mdb_res)
{
@@ -218,13 +182,32 @@ const std::string lmdb_error(const std::string& error_string, int mdb_res)
return full_string;
}
+inline void lmdb_db_open(MDB_txn* txn, const char* name, int flags, MDB_dbi& dbi, const std::string& error_string)
+{
+ if (auto res = mdb_dbi_open(txn, name, flags, &dbi))
+ throw0(cryptonote::DB_OPEN_FAILURE(lmdb_error(error_string + " : ", res).c_str()));
+}
+
+
} // anonymous namespace
#define CURSOR(name) \
if (!m_cur_ ## name) { \
int result = mdb_cursor_open(*m_write_txn, m_ ## name, &m_cur_ ## name); \
if (result) \
- throw0(DB_ERROR(std::string("Failed to open cursor: ").append(mdb_strerror(result)).c_str())); \
+ throw0(DB_ERROR(lmdb_error("Failed to open cursor: ", result).c_str())); \
+ }
+
+#define RCURSOR(name) \
+ if (!m_cur_ ## name) { \
+ int result = mdb_cursor_open(m_txn, m_ ## name, (MDB_cursor **)&m_cur_ ## name); \
+ if (result) \
+ throw0(DB_ERROR(lmdb_error("Failed to open cursor: ", result).c_str())); \
+ if (!m_write_txn) \
+ m_tinfo->m_ti_rflags.m_rf_ ## name = true; \
+ } else if (!m_write_txn && !m_tinfo->m_ti_rflags.m_rf_ ## name) { \
+ mdb_cursor_renew(m_txn, m_cur_ ## name); \
+ m_tinfo->m_ti_rflags.m_rf_ ## name = true; \
}
namespace cryptonote
@@ -232,6 +215,17 @@ namespace cryptonote
std::atomic<uint64_t> mdb_txn_safe::num_active_txns{0};
std::atomic_flag mdb_txn_safe::creation_gate = ATOMIC_FLAG_INIT;
+mdb_threadinfo::~mdb_threadinfo()
+{
+ MDB_cursor **cur = &m_ti_rcursors.m_txc_blocks;
+ unsigned i;
+ for (i=0; i<sizeof(mdb_txn_cursors)/sizeof(MDB_cursor *); i++)
+ if (cur[i])
+ mdb_cursor_close(cur[i]);
+ if (m_ti_rtxn)
+ mdb_txn_abort(m_ti_rtxn);
+}
+
mdb_txn_safe::mdb_txn_safe() : m_txn(NULL)
{
while (creation_gate.test_and_set());
@@ -273,7 +267,7 @@ void mdb_txn_safe::commit(std::string message)
if (auto result = mdb_txn_commit(m_txn))
{
m_txn = nullptr;
- throw0(DB_ERROR((message + ": ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error(message + ": ", result).c_str()));
}
m_txn = nullptr;
}
@@ -529,6 +523,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
+ mdb_txn_cursors *m_cursors = &m_wcursors;
CURSOR(block_heights)
MDB_val_copy<crypto::hash> val_h(blk_hash);
@@ -564,35 +559,35 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
MDB_val_copy<blobdata> blob(block_to_blob(blk));
result = mdb_cursor_put(m_cur_blocks, &key, &blob, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block blob to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block blob to db transaction: ", result).c_str()));
MDB_val_copy<size_t> sz(block_size);
result = mdb_cursor_put(m_cur_block_sizes, &key, &sz, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block size to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block size to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> ts(blk.timestamp);
result = mdb_cursor_put(m_cur_block_timestamps, &key, &ts, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block timestamp to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block timestamp to db transaction: ", result).c_str()));
MDB_val_copy<difficulty_type> diff(cumulative_difficulty);
result = mdb_cursor_put(m_cur_block_diffs, &key, &diff, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block cumulative difficulty to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block cumulative difficulty to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> coinsgen(coins_generated);
result = mdb_cursor_put(m_cur_block_coins, &key, &coinsgen, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block total generated coins to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block total generated coins to db transaction: ", result).c_str()));
result = mdb_cursor_put(m_cur_block_heights, &val_h, &key, 0);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block height by hash to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str()));
result = mdb_cursor_put(m_cur_block_hashes, &key, &val_h, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add block hash to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add block hash to db transaction: ", result).c_str()));
m_cum_size += block_size;
m_cum_count++;
@@ -637,6 +632,7 @@ void BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const tr
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
+ mdb_txn_cursors *m_cursors = &m_wcursors;
int result = 0;
@@ -652,17 +648,17 @@ void BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const tr
MDB_val_copy<blobdata> blob(tx_to_blob(tx));
result = mdb_cursor_put(m_cur_txs, &val_h, &blob, 0);
if (result)
- throw0(DB_ERROR(std::string("Failed to add tx blob to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add tx blob to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> height(m_height);
result = mdb_cursor_put(m_cur_tx_heights, &val_h, &height, 0);
if (result)
- throw0(DB_ERROR(std::string("Failed to add tx block height to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add tx block height to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> unlock_time(tx.unlock_time);
result = mdb_cursor_put(m_cur_tx_unlocks, &val_h, &unlock_time, 0);
if (result)
- throw0(DB_ERROR(std::string("Failed to add tx unlock time to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add tx unlock time to db transaction: ", result).c_str()));
}
void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx)
@@ -688,13 +684,14 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
if (result == MDB_NOTFOUND)
LOG_PRINT_L1("tx has no outputs to remove: " << tx_hash);
else if (result)
- throw1(DB_ERROR(std::string("Failed to add removal of tx outputs to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw1(DB_ERROR(lmdb_error("Failed to add removal of tx outputs to db transaction: ", result).c_str()));
}
void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
+ mdb_txn_cursors *m_cursors = &m_wcursors;
int result = 0;
@@ -709,20 +706,20 @@ void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_ou
result = mdb_cursor_put(m_cur_output_txs, &k, &v, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add output tx hash to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add output tx hash to db transaction: ", result).c_str()));
result = mdb_cursor_put(m_cur_tx_outputs, &v, &k, 0);
if (result)
- throw0(DB_ERROR(std::string("Failed to add <tx hash, global output index> to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add <tx hash, global output index> to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> val_local_index(local_index);
result = mdb_cursor_put(m_cur_output_indices, &k, &val_local_index, MDB_APPEND);
if (result)
- throw0(DB_ERROR(std::string("Failed to add tx output index to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add tx output index to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> val_amount(tx_output.amount);
result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &k, 0);
if (result)
- throw0(DB_ERROR(std::string("Failed to add output amount to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to add output amount to db transaction: ", result).c_str()));
if (tx_output.target.type() == typeid(txout_to_key))
{
@@ -748,12 +745,12 @@ void BlockchainLMDB::remove_tx_outputs(const crypto::hash& tx_hash, const transa
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
- lmdb_cur cur(*m_write_txn, m_tx_outputs);
-
+ mdb_txn_cursors *m_cursors = &m_wcursors;
MDB_val_copy<crypto::hash> k(tx_hash);
MDB_val v;
+ CURSOR(tx_outputs)
- auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto result = mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
{
LOG_PRINT_L2("tx has no outputs, so no global output indices");
@@ -764,10 +761,10 @@ void BlockchainLMDB::remove_tx_outputs(const crypto::hash& tx_hash, const transa
}
else
{
- size_t num_elems = 0;
- mdb_cursor_count(cur, &num_elems);
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_tx_outputs, &num_elems);
- mdb_cursor_get(cur, &k, &v, MDB_LAST_DUP);
+ mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_LAST_DUP);
for (uint64_t i = num_elems; i > 0; --i)
{
@@ -775,12 +772,10 @@ void BlockchainLMDB::remove_tx_outputs(const crypto::hash& tx_hash, const transa
remove_output(*(const uint64_t*)v.mv_data, tx_output.amount);
if (i > 1)
{
- mdb_cursor_get(cur, &k, &v, MDB_PREV_DUP);
+ mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_PREV_DUP);
}
}
}
-
- cur.close();
}
// TODO: probably remove this function
@@ -804,7 +799,7 @@ void BlockchainLMDB::remove_output(const uint64_t& out_index, const uint64_t amo
}
else if (result)
{
- throw1(DB_ERROR("Error adding removal of output tx index to db transaction"));
+ throw1(DB_ERROR(lmdb_error("Error adding removal of output tx index to db transaction", result).c_str()));
}
result = mdb_del(*m_write_txn, m_output_txs, &k, NULL);
@@ -816,7 +811,7 @@ void BlockchainLMDB::remove_output(const uint64_t& out_index, const uint64_t amo
}
else if (result)
{
- throw1(DB_ERROR("Error adding removal of output tx hash to db transaction"));
+ throw1(DB_ERROR(lmdb_error("Error adding removal of output tx hash to db transaction", result).c_str()));
}
result = mdb_del(*m_write_txn, m_output_keys, &k, NULL);
@@ -825,7 +820,7 @@ void BlockchainLMDB::remove_output(const uint64_t& out_index, const uint64_t amo
LOG_PRINT_L0("Unexpected: global output index not found in m_output_keys");
}
else if (result)
- throw1(DB_ERROR("Error adding removal of output pubkey to db transaction"));
+ throw1(DB_ERROR(lmdb_error("Error adding removal of output pubkey to db transaction", result).c_str()));
remove_amount_output_index(amount, out_index);
@@ -836,29 +831,29 @@ void BlockchainLMDB::remove_amount_output_index(const uint64_t amount, const uin
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
-
- lmdb_cur cur(*m_write_txn, m_output_amounts);
+ mdb_txn_cursors *m_cursors = &m_wcursors;
+ CURSOR(output_amounts);
MDB_val_copy<uint64_t> k(amount);
MDB_val v;
- auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to get an output", result).c_str()));
- size_t num_elems = 0;
- mdb_cursor_count(cur, &num_elems);
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_output_amounts, &num_elems);
- mdb_cursor_get(cur, &k, &v, MDB_LAST_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_LAST_DUP);
uint64_t amount_output_index = 0;
uint64_t goi = 0;
bool found_index = false;
for (uint64_t i = num_elems; i > 0; --i)
{
- mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_CURRENT);
goi = *(const uint64_t *)v.mv_data;
if (goi == global_output_index)
{
@@ -867,29 +862,28 @@ void BlockchainLMDB::remove_amount_output_index(const uint64_t amount, const uin
break;
}
if (i > 1)
- mdb_cursor_get(cur, &k, &v, MDB_PREV_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_PREV_DUP);
}
if (found_index)
{
// found the amount output index
// now delete it
- result = mdb_cursor_del(cur, 0);
+ result = mdb_cursor_del(m_cur_output_amounts, 0);
if (result)
throw0(DB_ERROR(std::string("Error deleting amount output index ").append(boost::lexical_cast<std::string>(amount_output_index)).c_str()));
}
else
{
// not found
- cur.close();
throw1(OUTPUT_DNE("Failed to find amount output index"));
}
- cur.close();
}
void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
+ mdb_txn_cursors *m_cursors = &m_wcursors;
CURSOR(spent_keys)
@@ -902,7 +896,7 @@ void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image)
unused.mv_size = sizeof(char);
unused.mv_data = &anything;
if (auto result = mdb_cursor_put(m_cur_spent_keys, &val_key, &unused, 0))
- throw1(DB_ERROR(std::string("Error adding spent key image to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw1(DB_ERROR(lmdb_error("Error adding spent key image to db transaction: ", result).c_str()));
}
void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image)
@@ -913,7 +907,7 @@ void BlockchainLMDB::remove_spent_key(const crypto::key_image& k_image)
MDB_val_copy<crypto::key_image> k(k_image);
auto result = mdb_del(*m_write_txn, m_spent_keys, &k, NULL);
if (result != 0 && result != MDB_NOTFOUND)
- throw1(DB_ERROR("Error adding removal of key image to db transaction"));
+ throw1(DB_ERROR(lmdb_error("Error adding removal of key image to db transaction", result).c_str()));
}
blobdata BlockchainLMDB::output_to_blob(const tx_out& output) const
@@ -1028,7 +1022,7 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags)
size_t mapsize = DEFAULT_MAPSIZE;
if (auto result = mdb_env_open(m_env, filename.c_str(), mdb_flags, 0644))
- throw0(DB_ERROR(std::string("Failed to open lmdb environment: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to open lmdb environment: ", result).c_str()));
MDB_envinfo mei;
mdb_env_info(m_env, &mei);
@@ -1037,7 +1031,7 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags)
if (cur_mapsize < mapsize)
{
if (auto result = mdb_env_set_mapsize(m_env, mapsize))
- throw0(DB_ERROR(std::string("Failed to set max memory map size: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to set max memory map size: ", result).c_str()));
mdb_env_info(m_env, &mei);
cur_mapsize = (double)mei.me_mapsize;
LOG_PRINT_L1("LMDB memory map size: " << cur_mapsize);
@@ -1201,6 +1195,7 @@ void BlockchainLMDB::close()
batch_abort();
}
this->sync();
+ m_tinfo.reset();
// FIXME: not yet thread safe!!! Use with care.
mdb_env_close(m_env);
@@ -1216,7 +1211,7 @@ void BlockchainLMDB::sync()
// MDB_NOMETASYNC. Force flush to be synchronous.
if (auto result = mdb_env_sync(m_env, true))
{
- throw0(DB_ERROR(std::string("Failed to sync database: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Failed to sync database: ", result).c_str()));
}
}
@@ -1303,7 +1298,11 @@ void BlockchainLMDB::unlock()
throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \
} \
-#define TXN_PREFIX_RDONLY(); TXN_PREFIX(MDB_RDONLY);
+#define TXN_PREFIX_RDONLY() \
+ bool my_rtxn = block_rtxn_start(); \
+ MDB_txn *m_txn = m_write_txn ? m_write_txn->m_txn : m_tinfo->m_ti_rtxn
+#define TXN_POSTFIX_RDONLY() \
+ if (my_rtxn) block_rtxn_stop()
#define TXN_POSTFIX_SUCCESS() \
do { \
@@ -1343,20 +1342,21 @@ bool BlockchainLMDB::block_exists(const crypto::hash& h) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_heights);
MDB_val_copy<crypto::hash> key(h);
- MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_heights, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_heights, &key, NULL, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
LOG_PRINT_L3("Block with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
return false;
}
else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch block index from hash"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch block index from hash", get_result).c_str()));
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return true;
}
@@ -1374,17 +1374,19 @@ uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_heights);
MDB_val_copy<crypto::hash> key(h);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_heights, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_heights, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(BLOCK_DNE("Attempted to retrieve non-existent block height"));
else if (get_result)
throw0(DB_ERROR("Error attempting to retrieve a block height from the db"));
uint64_t ret = *(const uint64_t *)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1403,10 +1405,12 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(blocks);
MDB_val_copy<uint64_t> key(height);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_blocks, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_blocks, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get block from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block not in db").c_str()));
@@ -1421,7 +1425,7 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const
if (!parse_and_validate_block_from_blob(bd, b))
throw0(DB_ERROR("Failed to parse block from blob retrieved from the db"));
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return b;
}
@@ -1432,10 +1436,12 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_timestamps);
MDB_val_copy<uint64_t> key(height);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_timestamps, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_timestamps, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- timestamp not in db").c_str()));
@@ -1444,7 +1450,7 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
throw0(DB_ERROR("Error attempting to retrieve a timestamp from the db"));
uint64_t ret = *(const uint64_t *)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1468,10 +1474,12 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_sizes);
MDB_val_copy<uint64_t> key(height);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_sizes, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_sizes, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get block size from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str()));
@@ -1480,7 +1488,7 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const
throw0(DB_ERROR("Error attempting to retrieve a block size from the db"));
size_t ret = *(const size_t *)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1490,10 +1498,12 @@ difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t&
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_diffs);
MDB_val_copy<uint64_t> key(height);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_diffs, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_diffs, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- difficulty not in db").c_str()));
@@ -1502,7 +1512,7 @@ difficulty_type BlockchainLMDB::get_block_cumulative_difficulty(const uint64_t&
throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db"));
difficulty_type ret = *(const difficulty_type*)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1529,10 +1539,12 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_coins);
MDB_val_copy<uint64_t> key(height);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_coins, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_coins, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str()));
@@ -1541,7 +1553,7 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh
throw0(DB_ERROR("Error attempting to retrieve a total generated coins from the db"));
uint64_t ret = *(const uint64_t*)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1551,20 +1563,21 @@ crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height)
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(block_hashes);
MDB_val_copy<uint64_t> key(height);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_block_hashes, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_block_hashes, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get hash from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- hash not in db").c_str()));
}
else if (get_result)
- throw0(DB_ERROR(std::string("Error attempting to retrieve a block hash from the db: ").
- append(mdb_strerror(get_result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve a block hash from the db: ", get_result).c_str()));
crypto::hash ret = *(const crypto::hash*)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1636,16 +1649,18 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(txs);
MDB_val_copy<crypto::hash> key(h);
MDB_val result;
TIME_MEASURE_START(time1);
- auto get_result = mdb_get(*txn_ptr, m_txs, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_txs, &key, &result, MDB_SET);
TIME_MEASURE_FINISH(time1);
time_tx_exists += time1;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
if (get_result == MDB_NOTFOUND)
{
@@ -1653,7 +1668,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
return false;
}
else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch transaction from hash"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch transaction from hash", get_result).c_str()));
return true;
}
@@ -1664,17 +1679,19 @@ uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(tx_unlocks);
MDB_val_copy<crypto::hash> key(h);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_tx_unlocks, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_tx_unlocks, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(TX_DNE(std::string("tx unlock time with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch tx unlock time from hash"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx unlock time from hash", get_result).c_str()));
uint64_t ret = *(const uint64_t*)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1684,14 +1701,16 @@ transaction BlockchainLMDB::get_tx(const crypto::hash& h) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(txs);
MDB_val_copy<crypto::hash> key(h);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_txs, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_txs, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch tx from hash"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str()));
blobdata bd;
bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
@@ -1700,7 +1719,7 @@ transaction BlockchainLMDB::get_tx(const crypto::hash& h) const
if (!parse_and_validate_tx_from_blob(bd, tx))
throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return tx;
}
@@ -1713,10 +1732,10 @@ uint64_t BlockchainLMDB::get_tx_count() const
TXN_PREFIX_RDONLY();
MDB_stat db_stats;
- if (mdb_stat(*txn_ptr, m_txs, &db_stats))
+ if (mdb_stat(m_txn, m_txs, &db_stats))
throw0(DB_ERROR("Failed to query m_txs"));
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return db_stats.ms_entries;
}
@@ -1741,19 +1760,21 @@ uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(tx_heights);
MDB_val_copy<crypto::hash> key(h);
MDB_val result;
- auto get_result = mdb_get(*txn_ptr, m_tx_heights, &key, &result);
+ auto get_result = mdb_cursor_get(m_cur_tx_heights, &key, &result, MDB_SET);
if (get_result == MDB_NOTFOUND)
{
throw1(TX_DNE(std::string("tx height with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
}
else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch tx height from hash"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx height from hash", get_result).c_str()));
uint64_t ret = *(const uint64_t*)result.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1763,24 +1784,24 @@ uint64_t BlockchainLMDB::get_num_outputs(const uint64_t& amount) const
check_open();
TXN_PREFIX_RDONLY();
-
- lmdb_cur cur(*txn_ptr, m_output_amounts);
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_amounts);
MDB_val_copy<uint64_t> k(amount);
MDB_val v;
- auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
{
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return 0;
}
else if (result)
throw0(DB_ERROR("DB error attempting to get number of outputs of an amount"));
- size_t num_elems = 0;
- mdb_cursor_count(cur, &num_elems);
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_output_amounts, &num_elems);
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return num_elems;
}
@@ -1791,16 +1812,18 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t &global_index) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_keys);
MDB_val_copy<uint64_t> k(global_index);
MDB_val v;
- auto get_result = mdb_get(*txn_ptr, m_output_keys, &k, &v);
+ auto get_result = mdb_cursor_get(m_cur_output_keys, &k, &v, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("Attempting to get output pubkey by global index, but key does not exist"));
else if (get_result)
throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db"));
output_data_t ret = *(const output_data_t *) v.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1819,11 +1842,14 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t&
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_txs);
+ RCURSOR(output_indices);
MDB_val_copy<uint64_t> k(index);
MDB_val v;
- auto get_result = mdb_get(*txn_ptr, m_output_txs, &k, &v);
+ auto get_result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("output with given index not in db"));
else if (get_result)
@@ -1831,14 +1857,14 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t&
crypto::hash tx_hash = *(const crypto::hash*)v.mv_data;
- get_result = mdb_get(*txn_ptr, m_output_indices, &k, &v);
+ get_result = mdb_cursor_get(m_cur_output_indices, &k, &v, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("output with given index not in db"));
else if (get_result)
throw0(DB_ERROR("DB error attempting to fetch output tx index"));
tx_out_index ret = tx_out_index(tx_hash, *(const uint64_t *)v.mv_data);
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1862,31 +1888,30 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_output_indices(const crypto::hash&
std::vector<uint64_t> index_vec;
TXN_PREFIX_RDONLY();
-
- lmdb_cur cur(*txn_ptr, m_tx_outputs);
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(tx_outputs);
MDB_val_copy<crypto::hash> k(h);
MDB_val v;
- auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto result = mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found"));
else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to get an output", result).c_str()));
- size_t num_elems = 0;
- mdb_cursor_count(cur, &num_elems);
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_tx_outputs, &num_elems);
- mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+ mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_FIRST_DUP);
for (uint64_t i = 0; i < num_elems; ++i)
{
- mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_GET_CURRENT);
index_vec.push_back(*(const uint64_t *)v.mv_data);
- mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ mdb_cursor_get(m_cur_tx_outputs, &k, &v, MDB_NEXT_DUP);
}
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return index_vec;
}
@@ -1905,6 +1930,8 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const crypto:
transaction tx = get_tx(h);
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_amounts);
uint64_t i = 0;
uint64_t global_index;
@@ -1914,28 +1941,26 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const crypto:
global_index = index_vec[i];
- lmdb_cur cur(*txn_ptr, m_output_amounts);
-
MDB_val_copy<uint64_t> k(amount);
MDB_val v;
- auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to get an output", result).c_str()));
- size_t num_elems = 0;
- mdb_cursor_count(cur, &num_elems);
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_output_amounts, &num_elems);
- mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_FIRST_DUP);
uint64_t amount_output_index = 0;
uint64_t output_index = 0;
bool found_index = false;
for (uint64_t j = 0; j < num_elems; ++j)
{
- mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_CURRENT);
output_index = *(const uint64_t *)v.mv_data;
if (output_index == global_index)
{
@@ -1943,7 +1968,7 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const crypto:
found_index = true;
break;
}
- mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP);
}
if (found_index)
{
@@ -1952,16 +1977,14 @@ std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const crypto:
else
{
// not found
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
throw1(OUTPUT_DNE("specified output not found in db"));
}
- cur.close();
++i;
}
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return index_vec2;
}
@@ -1974,16 +1997,17 @@ bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(spent_keys);
MDB_val_copy<crypto::key_image> val_key(img);
- MDB_val unused;
- if (mdb_get(*txn_ptr, m_spent_keys, &val_key, &unused) == 0)
+ if (mdb_cursor_get(m_cur_spent_keys, &val_key, NULL, MDB_SET) == 0)
{
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return true;
}
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return false;
}
@@ -1993,16 +2017,17 @@ bool BlockchainLMDB::for_all_key_images(std::function<bool(const crypto::key_ima
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(spent_keys);
MDB_val k;
MDB_val v;
bool ret = true;
- lmdb_cur cur(*txn_ptr, m_spent_keys);
MDB_cursor_op op = MDB_FIRST;
while (1)
{
- int ret = mdb_cursor_get(cur, &k, &v, op);
+ int ret = mdb_cursor_get(m_cur_spent_keys, &k, &v, op);
op = MDB_NEXT;
if (ret == MDB_NOTFOUND)
break;
@@ -2015,8 +2040,7 @@ bool BlockchainLMDB::for_all_key_images(std::function<bool(const crypto::key_ima
}
}
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2027,16 +2051,17 @@ bool BlockchainLMDB::for_all_blocks(std::function<bool(uint64_t, const crypto::h
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(blocks);
MDB_val k;
MDB_val v;
bool ret = true;
- lmdb_cur cur(*txn_ptr, m_blocks);
MDB_cursor_op op = MDB_FIRST;
while (1)
{
- int ret = mdb_cursor_get(cur, &k, &v, op);
+ int ret = mdb_cursor_get(m_cur_blocks, &k, &v, op);
op = MDB_NEXT;
if (ret == MDB_NOTFOUND)
break;
@@ -2057,8 +2082,7 @@ bool BlockchainLMDB::for_all_blocks(std::function<bool(uint64_t, const crypto::h
}
}
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2069,16 +2093,17 @@ bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(txs);
MDB_val k;
MDB_val v;
bool ret = true;
- lmdb_cur cur(*txn_ptr, m_txs);
MDB_cursor_op op = MDB_FIRST;
while (1)
{
- int ret = mdb_cursor_get(cur, &k, &v, op);
+ int ret = mdb_cursor_get(m_cur_txs, &k, &v, op);
op = MDB_NEXT;
if (ret == MDB_NOTFOUND)
break;
@@ -2096,8 +2121,7 @@ bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&
}
}
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2108,16 +2132,17 @@ bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const c
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_amounts);
MDB_val k;
MDB_val v;
bool ret = true;
- lmdb_cur cur(*txn_ptr, m_output_amounts);
MDB_cursor_op op = MDB_FIRST;
while (1)
{
- int ret = mdb_cursor_get(cur, &k, &v, op);
+ int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op);
op = MDB_NEXT;
if (ret == MDB_NOTFOUND)
break;
@@ -2132,8 +2157,7 @@ bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const c
}
}
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2167,8 +2191,9 @@ void BlockchainLMDB::batch_start(uint64_t batch_num_blocks)
// active
m_write_batch_txn->m_batch_txn = true;
m_write_txn = m_write_batch_txn;
+
m_batch_active = true;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
LOG_PRINT_L3("batch transaction: begin");
}
@@ -2193,7 +2218,7 @@ void BlockchainLMDB::batch_commit()
m_write_txn = nullptr;
delete m_write_batch_txn;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
}
void BlockchainLMDB::batch_stop()
@@ -2216,7 +2241,7 @@ void BlockchainLMDB::batch_stop()
delete m_write_batch_txn;
m_write_batch_txn = nullptr;
m_batch_active = false;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
LOG_PRINT_L3("batch transaction: end");
}
@@ -2234,7 +2259,7 @@ void BlockchainLMDB::batch_abort()
m_write_batch_txn->abort();
m_batch_active = false;
m_write_batch_txn = nullptr;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
LOG_PRINT_L3("batch transaction: aborted");
}
@@ -2245,9 +2270,68 @@ void BlockchainLMDB::set_batch_transactions(bool batch_transactions)
LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled"));
}
-void BlockchainLMDB::block_txn_start()
+// return true if we started the txn, false if already started
+bool BlockchainLMDB::block_rtxn_start() const
+{
+ if (m_write_txn)
+ return false;
+ if (!m_tinfo.get())
+ {
+ m_tinfo.reset(new mdb_threadinfo);
+ memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors));
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
+ throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str()));
+ } else if (!m_tinfo->m_ti_rflags.m_rf_txn)
+ {
+ if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn))
+ throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str()));
+ } else
+ {
+ return false;
+ }
+ m_tinfo->m_ti_rflags.m_rf_txn = true;
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ return true;
+}
+
+void BlockchainLMDB::block_rtxn_stop() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+}
+
+void BlockchainLMDB::block_txn_start(bool readonly)
+{
+ if (readonly)
+ {
+ bool didit = false;
+ if (m_write_txn)
+ return;
+ if (!m_tinfo.get())
+ {
+ m_tinfo.reset(new mdb_threadinfo);
+ memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors));
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ if (auto mdb_res = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
+ throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str()));
+ didit = true;
+ } else if (!m_tinfo->m_ti_rflags.m_rf_txn)
+ {
+ if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn))
+ throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str()));
+ didit = true;
+ }
+ if (didit)
+ {
+ m_tinfo->m_ti_rflags.m_rf_txn = true;
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " RO");
+ }
+ return;
+ }
+
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
// Distinguish the exceptions here from exceptions that would be thrown while
// using the txn and committing it.
//
@@ -2266,7 +2350,7 @@ void BlockchainLMDB::block_txn_start()
m_write_txn = nullptr;
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str()));
}
- memset(&m_cursors, 0, sizeof(m_cursors));
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
}
}
@@ -2275,14 +2359,22 @@ void BlockchainLMDB::block_txn_stop()
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
if (! m_batch_active)
{
- TIME_MEASURE_START(time1);
- m_write_txn->commit();
- TIME_MEASURE_FINISH(time1);
- time_commit1 += time1;
+ if (m_write_txn)
+ {
+ TIME_MEASURE_START(time1);
+ m_write_txn->commit();
+ TIME_MEASURE_FINISH(time1);
+ time_commit1 += time1;
- delete m_write_txn;
- m_write_txn = nullptr;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ delete m_write_txn;
+ m_write_txn = nullptr;
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
+ }
+ else if (m_tinfo->m_ti_rtxn)
+ {
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ }
}
}
@@ -2295,8 +2387,13 @@ void BlockchainLMDB::block_txn_abort()
{
delete m_write_txn;
m_write_txn = nullptr;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
}
+ else if (m_tinfo->m_ti_rtxn)
+ {
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ }
else
{
// This would probably mean an earlier exception was caught, but then we
@@ -2348,31 +2445,18 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs)
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
- mdb_txn_safe txn;
- if (! m_batch_active)
- {
- if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, txn))
- throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str()));
- m_write_txn = &txn;
- memset(&m_cursors, 0, sizeof(m_cursors));
- }
+ block_txn_start(false);
uint64_t num_outputs = m_num_outputs;
try
{
BlockchainDB::pop_block(blk, txs);
- if (! m_batch_active)
- {
- m_write_txn = nullptr;
- memset(&m_cursors, 0, sizeof(m_cursors));
- txn.commit();
- }
+ block_txn_stop();
}
catch (...)
{
m_num_outputs = num_outputs;
- m_write_txn = nullptr;
- memset(&m_cursors, 0, sizeof(m_cursors));
+ block_txn_abort();
throw;
}
@@ -2387,13 +2471,16 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6
tx_out_indices.clear();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_txs);
+ RCURSOR(output_indices);
for (const uint64_t &index : global_indices)
{
MDB_val_copy<uint64_t> k(index);
MDB_val v;
- auto get_result = mdb_get(*txn_ptr, m_output_txs, &k, &v);
+ auto get_result = mdb_cursor_get(m_cur_output_txs, &k, &v, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("output with given index not in db"));
else if (get_result)
@@ -2401,7 +2488,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6
crypto::hash tx_hash = *(const crypto::hash*) v.mv_data;
- get_result = mdb_get(*txn_ptr, m_output_indices, &k, &v);
+ get_result = mdb_cursor_get(m_cur_output_indices, &k, &v, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("output with given index not in db"));
else if (get_result)
@@ -2411,7 +2498,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6
tx_out_indices.push_back(result);
}
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
}
void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std::vector<uint64_t> &offsets,
@@ -2430,19 +2517,19 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std
}
TXN_PREFIX_RDONLY();
-
- lmdb_cur cur(*txn_ptr, m_output_amounts);
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(output_amounts);
MDB_val_copy<uint64_t> k(amount);
MDB_val v;
- auto result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
+ throw0(DB_ERROR(lmdb_error("DB error attempting to get an output", result).c_str()));
- size_t num_elems = 0;
- mdb_cursor_count(cur, &num_elems);
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_output_amounts, &num_elems);
if (max <= 1 && num_elems <= max)
throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found"));
@@ -2452,13 +2539,13 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std
{
for (const uint64_t& index : offsets)
{
- mdb_cursor_get(cur, &k, &v, MDB_FIRST_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_FIRST_DUP);
for (uint64_t i = 0; i < index; ++i)
{
- mdb_cursor_get(cur, &k, &v, MDB_NEXT_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT_DUP);
}
- mdb_cursor_get(cur, &k, &v, MDB_GET_CURRENT);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_CURRENT);
uint64_t glob_index = *(const uint64_t*) v.mv_data;
LOG_PRINT_L3("Amount: " << amount << " M0->v: " << glob_index);
global_indices.push_back(glob_index);
@@ -2475,22 +2562,51 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std
LOG_PRINT_L1("Index: " << index << " Elems: " << num_elems << " partial results found for get_output_tx_and_index");
break;
}
- while (index >= curcount)
+ if (!curcount && index > num_elems/2)
{
- TIME_MEASURE_START(db1);
- if (mdb_cursor_get(cur, &k, &v, curcount == 0 ? MDB_GET_MULTIPLE : MDB_NEXT_MULTIPLE) != 0)
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_LAST_DUP);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_PREV); /* kludge to unset C_EOF */
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_NEXT);
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_MULTIPLE);
+
+ curcount = num_elems;
+ while(1)
{
- // allow partial results
- result = false;
- break;
+ TIME_MEASURE_START(db1);
+ int count = v.mv_size / sizeof(uint64_t);
+ curcount -= count;
+ if (curcount > index)
+ {
+ mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_PREV_MULTIPLE);
+ } else
+ {
+ blockstart = curcount;
+ curcount += count;
+ break;
+ }
+ TIME_MEASURE_FINISH(db1);
+ t_dbmul += db1;
}
- int count = v.mv_size / sizeof(uint64_t);
-
- blockstart = curcount;
- curcount += count;
- TIME_MEASURE_FINISH(db1);
- t_dbmul += db1;
+ } else
+ {
+ while (index >= curcount)
+ {
+ TIME_MEASURE_START(db1);
+ if (mdb_cursor_get(m_cur_output_amounts, &k, &v, curcount == 0 ? MDB_GET_MULTIPLE : MDB_NEXT_MULTIPLE) != 0)
+ {
+ // allow partial results
+ result = false;
+ break;
+ }
+
+ int count = v.mv_size / sizeof(uint64_t);
+
+ blockstart = curcount;
+ curcount += count;
+ TIME_MEASURE_FINISH(db1);
+ t_dbmul += db1;
+ }
}
LOG_PRINT_L3("Records returned: " << curcount << " Index: " << index);
@@ -2507,8 +2623,7 @@ void BlockchainLMDB::get_output_global_indices(const uint64_t& amount, const std
}
}
- cur.close();
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
TIME_MEASURE_FINISH(txx);
LOG_PRINT_L3("txx: " << txx << " db1: " << t_dbmul << " db2: " << t_dbscan);
@@ -2521,31 +2636,32 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui
check_open();
outputs.clear();
+ TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
std::vector <uint64_t> global_indices;
get_output_global_indices(amount, offsets, global_indices);
if (global_indices.size() > 0)
{
- TXN_PREFIX_RDONLY();
- lmdb_cur cur(*txn_ptr, m_output_keys);
+ RCURSOR(output_keys);
for (const uint64_t &index : global_indices)
{
MDB_val_copy<uint64_t> k(index);
MDB_val v;
- auto get_result = mdb_cursor_get(cur, &k, &v, MDB_SET);
+ auto get_result = mdb_cursor_get(m_cur_output_keys, &k, &v, MDB_SET);
if (get_result == MDB_NOTFOUND)
throw1(OUTPUT_DNE("Attempting to get output pubkey by global index, but key does not exist"));
else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db"));
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str()));
output_data_t data = *(const output_data_t *) v.mv_data;
outputs.push_back(data);
}
- TXN_POSTFIX_SUCCESS();
}
+ TXN_POSTFIX_RDONLY();
TIME_MEASURE_FINISH(db3);
LOG_PRINT_L3("db3: " << db3);
@@ -2619,7 +2735,7 @@ void BlockchainLMDB::set_hard_fork_starting_height(uint8_t version, uint64_t hei
MDB_val_copy<uint8_t> val_key(version);
MDB_val_copy<uint64_t> val_value(height);
if (auto result = mdb_put(*txn_ptr, m_hf_starting_heights, &val_key, &val_value, MDB_APPEND))
- throw1(DB_ERROR(std::string("Error adding hard fork starting height to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw1(DB_ERROR(lmdb_error("Error adding hard fork starting height to db transaction: ", result).c_str()));
TXN_BLOCK_POSTFIX_SUCCESS();
}
@@ -2633,14 +2749,19 @@ uint64_t BlockchainLMDB::get_hard_fork_starting_height(uint8_t version) const
MDB_val_copy<uint8_t> val_key(version);
MDB_val val_ret;
- auto result = mdb_get(*txn_ptr, m_hf_starting_heights, &val_key, &val_ret);
+ auto result = mdb_get(m_txn, m_hf_starting_heights, &val_key, &val_ret);
if (result == MDB_NOTFOUND)
return std::numeric_limits<uint64_t>::max();
if (result)
throw0(DB_ERROR("Error attempting to retrieve a hard fork starting height from the db"));
- uint64_t ret = *(const uint64_t*)val_ret.mv_data;
- TXN_POSTFIX_SUCCESS();
+ uint64_t ret;
+#ifdef MISALIGNED_OK
+ ret = *(const uint64_t*)val_ret.mv_data;
+#else
+ memcpy(&ret, val_ret.mv_data, sizeof(uint64_t));
+#endif
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2658,7 +2779,7 @@ void BlockchainLMDB::set_hard_fork_version(uint64_t height, uint8_t version)
if (result == MDB_KEYEXIST)
result = mdb_put(*txn_ptr, m_hf_versions, &val_key, &val_value, 0);
if (result)
- throw1(DB_ERROR(std::string("Error adding hard fork version to db transaction: ").append(mdb_strerror(result)).c_str()));
+ throw1(DB_ERROR(lmdb_error("Error adding hard fork version to db transaction: ", result).c_str()));
TXN_BLOCK_POSTFIX_SUCCESS();
}
@@ -2669,15 +2790,17 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const
check_open();
TXN_PREFIX_RDONLY();
+ const mdb_txn_cursors *m_cursors = m_write_txn ? &m_wcursors : &m_tinfo->m_ti_rcursors;
+ RCURSOR(hf_versions);
MDB_val_copy<uint64_t> val_key(height);
MDB_val val_ret;
- auto result = mdb_get(*txn_ptr, m_hf_versions, &val_key, &val_ret);
+ auto result = mdb_cursor_get(m_cur_hf_versions, &val_key, &val_ret, MDB_SET);
if (result == MDB_NOTFOUND || result)
- throw0(DB_ERROR("Error attempting to retrieve a hard fork version from the db"));
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve a hard fork version at height " + boost::lexical_cast<std::string>(height) + " from the db: ", result).c_str()));
uint8_t ret = *(const uint8_t*)val_ret.mv_data;
- TXN_POSTFIX_SUCCESS();
+ TXN_POSTFIX_RDONLY();
return ret;
}
@@ -2686,7 +2809,7 @@ bool BlockchainLMDB::is_read_only() const
unsigned int flags;
auto result = mdb_env_get_flags(m_env, &flags);
if (result)
- throw0(DB_ERROR(std::string("Error getting database environment info: ").append(mdb_strerror(result)).c_str()));
+ throw0(DB_ERROR(lmdb_error("Error getting database environment info: ", result).c_str()));
if (flags & MDB_RDONLY)
return true;
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 150f59475..e58643efa 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -30,6 +30,7 @@
#include "blockchain_db/blockchain_db.h"
#include "cryptonote_protocol/blobdatatype.h" // for type blobdata
+#include <boost/thread/tss.hpp>
#include <lmdb.h>
@@ -38,7 +39,7 @@
namespace cryptonote
{
-struct mdb_txn_cursors
+typedef struct mdb_txn_cursors
{
MDB_cursor *m_txc_blocks;
MDB_cursor *m_txc_block_heights;
@@ -59,24 +60,58 @@ struct mdb_txn_cursors
MDB_cursor *m_txc_tx_outputs;
MDB_cursor *m_txc_spent_keys;
-};
-#define m_cur_blocks m_cursors.m_txc_blocks
-#define m_cur_block_heights m_cursors.m_txc_block_heights
-#define m_cur_block_hashes m_cursors.m_txc_block_hashes
-#define m_cur_block_timestamps m_cursors.m_txc_block_timestamps
-#define m_cur_block_sizes m_cursors.m_txc_block_sizes
-#define m_cur_block_diffs m_cursors.m_txc_block_diffs
-#define m_cur_block_coins m_cursors.m_txc_block_coins
-#define m_cur_output_txs m_cursors.m_txc_output_txs
-#define m_cur_output_indices m_cursors.m_txc_output_indices
-#define m_cur_output_amounts m_cursors.m_txc_output_amounts
-#define m_cur_output_keys m_cursors.m_txc_output_keys
-#define m_cur_txs m_cursors.m_txc_txs
-#define m_cur_tx_heights m_cursors.m_txc_tx_heights
-#define m_cur_tx_unlocks m_cursors.m_txc_tx_unlocks
-#define m_cur_tx_outputs m_cursors.m_txc_tx_outputs
-#define m_cur_spent_keys m_cursors.m_txc_spent_keys
+ MDB_cursor *m_txc_hf_versions;
+} mdb_txn_cursors;
+
+#define m_cur_blocks m_cursors->m_txc_blocks
+#define m_cur_block_heights m_cursors->m_txc_block_heights
+#define m_cur_block_hashes m_cursors->m_txc_block_hashes
+#define m_cur_block_timestamps m_cursors->m_txc_block_timestamps
+#define m_cur_block_sizes m_cursors->m_txc_block_sizes
+#define m_cur_block_diffs m_cursors->m_txc_block_diffs
+#define m_cur_block_coins m_cursors->m_txc_block_coins
+#define m_cur_output_txs m_cursors->m_txc_output_txs
+#define m_cur_output_indices m_cursors->m_txc_output_indices
+#define m_cur_output_amounts m_cursors->m_txc_output_amounts
+#define m_cur_output_keys m_cursors->m_txc_output_keys
+#define m_cur_txs m_cursors->m_txc_txs
+#define m_cur_tx_heights m_cursors->m_txc_tx_heights
+#define m_cur_tx_unlocks m_cursors->m_txc_tx_unlocks
+#define m_cur_tx_outputs m_cursors->m_txc_tx_outputs
+#define m_cur_spent_keys m_cursors->m_txc_spent_keys
+#define m_cur_hf_versions m_cursors->m_txc_hf_versions
+
+typedef struct mdb_rflags
+{
+ bool m_rf_txn;
+ bool m_rf_blocks;
+ bool m_rf_block_heights;
+ bool m_rf_block_hashes;
+ bool m_rf_block_timestamps;
+ bool m_rf_block_sizes;
+ bool m_rf_block_diffs;
+ bool m_rf_block_coins;
+ bool m_rf_output_txs;
+ bool m_rf_output_indices;
+ bool m_rf_output_amounts;
+ bool m_rf_output_keys;
+ bool m_rf_txs;
+ bool m_rf_tx_heights;
+ bool m_rf_tx_unlocks;
+ bool m_rf_tx_outputs;
+ bool m_rf_spent_keys;
+ bool m_rf_hf_versions;
+} mdb_rflags;
+
+typedef struct mdb_threadinfo
+{
+ MDB_txn *m_ti_rtxn; // per-thread read txn
+ mdb_txn_cursors m_ti_rcursors; // per-thread read cursors
+ mdb_rflags m_ti_rflags; // per-thread read state
+
+ ~mdb_threadinfo();
+} mdb_threadinfo;
struct mdb_txn_safe
{
@@ -234,9 +269,11 @@ public:
virtual void batch_stop();
virtual void batch_abort();
- virtual void block_txn_start();
+ virtual void block_txn_start(bool readonly);
virtual void block_txn_stop();
virtual void block_txn_abort();
+ virtual bool block_rtxn_start() const;
+ virtual void block_rtxn_stop() const;
virtual void pop_block(block& blk, std::vector<transaction>& txs);
@@ -355,7 +392,8 @@ private:
bool m_batch_transactions; // support for batch transactions
bool m_batch_active; // whether batch transaction is in progress
- struct mdb_txn_cursors m_cursors;
+ mdb_txn_cursors m_wcursors;
+ mutable boost::thread_specific_ptr<mdb_threadinfo> m_tinfo;
#if defined(__arm__)
// force a value so it can compile with 32-bit ARM