From 090b548c3b5a5cd59b67fa7a7bb4f0cf50b193e1 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 7 Jan 2016 06:33:22 +0000 Subject: [PATCH] Use cursors in write txns Saves a bit of seek overhead. LMDB frees them automatically in txn_(commit|abort) so they need no cleanup. --- src/blockchain_db/lmdb/db_lmdb.cpp | 80 ++++++++++++++++++++++-------- src/blockchain_db/lmdb/db_lmdb.h | 42 ++++++++++++++++ 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index a93079aa7..2017ccb43 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -220,6 +220,13 @@ const std::string lmdb_error(const std::string& error_string, int mdb_res) } // 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())); \ + } + namespace cryptonote { std::atomic mdb_txn_safe::num_active_txns{0}; @@ -523,16 +530,16 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); + CURSOR(block_heights) MDB_val_copy val_h(blk_hash); - MDB_val unused; - if (mdb_get(*m_write_txn, m_block_heights, &val_h, &unused) == 0) + if (mdb_cursor_get(m_cur_block_heights, &val_h, NULL, MDB_SET) == 0) throw1(BLOCK_EXISTS("Attempting to add block that's already in the db")); if (m_height > 0) { MDB_val_copy parent_key(blk.prev_id); MDB_val parent_h; - if (mdb_get(*m_write_txn, m_block_heights, &parent_key, &parent_h)) + if (mdb_cursor_get(m_cur_block_heights, &parent_key, &parent_h, MDB_SET)) { LOG_PRINT_L3("m_height: " << m_height); LOG_PRINT_L3("parent_key: " << blk.prev_id); @@ -547,36 +554,43 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const MDB_val_copy key(m_height); + CURSOR(blocks) + CURSOR(block_sizes) + CURSOR(block_timestamps) + CURSOR(block_diffs) + CURSOR(block_coins) + CURSOR(block_hashes) + MDB_val_copy blob(block_to_blob(blk)); - result = mdb_put(*m_write_txn, m_blocks, &key, &blob, 0); + 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())); MDB_val_copy sz(block_size); - result = mdb_put(*m_write_txn, m_block_sizes, &key, &sz, 0); + 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())); MDB_val_copy ts(blk.timestamp); - result = mdb_put(*m_write_txn, m_block_timestamps, &key, &ts, 0); + 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())); MDB_val_copy diff(cumulative_difficulty); - result = mdb_put(*m_write_txn, m_block_diffs, &key, &diff, 0); + 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())); MDB_val_copy coinsgen(coins_generated); - result = mdb_put(*m_write_txn, m_block_coins, &key, &coinsgen, 0); + 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())); - result = mdb_put(*m_write_txn, m_block_heights, &val_h, &key, 0); + 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())); - result = mdb_put(*m_write_txn, m_block_hashes, &key, &val_h, 0); + 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())); @@ -626,23 +640,27 @@ void BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const tr int result = 0; + CURSOR(txs) + CURSOR(tx_heights) + CURSOR(tx_unlocks) + MDB_val_copy val_h(tx_hash); MDB_val unused; - if (mdb_get(*m_write_txn, m_txs, &val_h, &unused) == 0) + if (mdb_cursor_get(m_cur_txs, &val_h, &unused, MDB_SET) == 0) throw1(TX_EXISTS("Attempting to add transaction that's already in the db")); MDB_val_copy blob(tx_to_blob(tx)); - result = mdb_put(*m_write_txn, m_txs, &val_h, &blob, 0); + 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())); MDB_val_copy height(m_height); - result = mdb_put(*m_write_txn, m_tx_heights, &val_h, &height, 0); + 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())); MDB_val_copy unlock_time(tx.unlock_time); - result = mdb_put(*m_write_txn, m_tx_unlocks, &val_h, &unlock_time, 0); + 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())); } @@ -680,23 +698,29 @@ void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_ou int result = 0; + CURSOR(output_txs) + CURSOR(tx_outputs) + CURSOR(output_indices) + CURSOR(output_amounts) + CURSOR(output_keys) + MDB_val_copy k(m_num_outputs); MDB_val_copy v(tx_hash); - result = mdb_put(*m_write_txn, m_output_txs, &k, &v, 0); + result = mdb_cursor_put(m_cur_output_txs, &k, &v, 0); if (result) throw0(DB_ERROR(std::string("Failed to add output tx hash to db transaction: ").append(mdb_strerror(result)).c_str())); - result = mdb_put(*m_write_txn, m_tx_outputs, &v, &k, 0); + result = mdb_cursor_put(m_cur_tx_outputs, &v, &k, 0); if (result) throw0(DB_ERROR(std::string("Failed to add to db transaction: ").append(mdb_strerror(result)).c_str())); MDB_val_copy val_local_index(local_index); - result = mdb_put(*m_write_txn, m_output_indices, &k, &val_local_index, 0); + result = mdb_cursor_put(m_cur_output_indices, &k, &val_local_index, 0); if (result) throw0(DB_ERROR(std::string("Failed to add tx output index to db transaction: ").append(mdb_strerror(result)).c_str())); MDB_val_copy val_amount(tx_output.amount); - result = mdb_put(*m_write_txn, m_output_amounts, &val_amount, &k, 0); + 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())); @@ -709,7 +733,7 @@ void BlockchainLMDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_ou MDB_val_copy data(od); //MDB_val_copy val_pubkey(boost::get(tx_output.target).key); - if (mdb_put(*m_write_txn, m_output_keys, &k, &data, 0)) + if (mdb_cursor_put(m_cur_output_keys, &k, &data, 0)) throw0(DB_ERROR("Failed to add output pubkey to db transaction")); } else @@ -867,15 +891,17 @@ void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image) LOG_PRINT_L3("BlockchainLMDB::" << __func__); check_open(); + CURSOR(spent_keys) + MDB_val_copy val_key(k_image); MDB_val unused; - if (mdb_get(*m_write_txn, m_spent_keys, &val_key, &unused) == 0) + if (mdb_cursor_get(m_cur_spent_keys, &val_key, &unused, MDB_SET) == 0) throw1(KEY_IMAGE_EXISTS("Attempting to add spent key image that's already in the db")); char anything = '\0'; unused.mv_size = sizeof(char); unused.mv_data = &anything; - if (auto result = mdb_put(*m_write_txn, m_spent_keys, &val_key, &unused, 0)) + 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())); } @@ -2140,6 +2166,8 @@ void BlockchainLMDB::batch_start(uint64_t batch_num_blocks) 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)); + LOG_PRINT_L3("batch transaction: begin"); } @@ -2163,6 +2191,7 @@ void BlockchainLMDB::batch_commit() m_write_txn = nullptr; delete m_write_batch_txn; + memset(&m_cursors, 0, sizeof(m_cursors)); } void BlockchainLMDB::batch_stop() @@ -2185,6 +2214,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)); LOG_PRINT_L3("batch transaction: end"); } @@ -2202,6 +2232,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)); LOG_PRINT_L3("batch transaction: aborted"); } @@ -2233,6 +2264,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)); } } @@ -2248,6 +2280,7 @@ void BlockchainLMDB::block_txn_stop() delete m_write_txn; m_write_txn = nullptr; + memset(&m_cursors, 0, sizeof(m_cursors)); } } @@ -2260,6 +2293,7 @@ void BlockchainLMDB::block_txn_abort() { delete m_write_txn; m_write_txn = nullptr; + memset(&m_cursors, 0, sizeof(m_cursors)); } else { @@ -2318,6 +2352,7 @@ void BlockchainLMDB::pop_block(block& blk, std::vector& txs) 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)); } uint64_t num_outputs = m_num_outputs; @@ -2327,7 +2362,7 @@ void BlockchainLMDB::pop_block(block& blk, std::vector& txs) if (! m_batch_active) { m_write_txn = nullptr; - + memset(&m_cursors, 0, sizeof(m_cursors)); txn.commit(); } } @@ -2335,6 +2370,7 @@ void BlockchainLMDB::pop_block(block& blk, std::vector& txs) { m_num_outputs = num_outputs; m_write_txn = nullptr; + memset(&m_cursors, 0, sizeof(m_cursors)); throw; } diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h index 1964bfe45..150f59475 100644 --- a/src/blockchain_db/lmdb/db_lmdb.h +++ b/src/blockchain_db/lmdb/db_lmdb.h @@ -38,6 +38,46 @@ namespace cryptonote { +struct mdb_txn_cursors +{ + MDB_cursor *m_txc_blocks; + MDB_cursor *m_txc_block_heights; + MDB_cursor *m_txc_block_hashes; + MDB_cursor *m_txc_block_timestamps; + MDB_cursor *m_txc_block_sizes; + MDB_cursor *m_txc_block_diffs; + MDB_cursor *m_txc_block_coins; + + MDB_cursor *m_txc_output_txs; + MDB_cursor *m_txc_output_indices; + MDB_cursor *m_txc_output_amounts; + MDB_cursor *m_txc_output_keys; + + MDB_cursor *m_txc_txs; + MDB_cursor *m_txc_tx_heights; + MDB_cursor *m_txc_tx_unlocks; + 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 + struct mdb_txn_safe { mdb_txn_safe(); @@ -315,6 +355,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; + #if defined(__arm__) // force a value so it can compile with 32-bit ARM constexpr static uint64_t DEFAULT_MAPSIZE = 1LL << 31;