diff options
Diffstat (limited to 'src/blockchain_db')
-rw-r--r-- | src/blockchain_db/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/blockchain_db/berkeleydb/db_bdb.cpp | 93 | ||||
-rw-r--r-- | src/blockchain_db/berkeleydb/db_bdb.h | 11 | ||||
-rw-r--r-- | src/blockchain_db/blockchain_db.cpp | 11 | ||||
-rw-r--r-- | src/blockchain_db/blockchain_db.h | 2 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.cpp | 637 | ||||
-rw-r--r-- | src/blockchain_db/lmdb/db_lmdb.h | 78 |
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 |