aboutsummaryrefslogtreecommitdiff
path: root/src/blockchain_db/lmdb/db_lmdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/blockchain_db/lmdb/db_lmdb.cpp')
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp188
1 files changed, 86 insertions, 102 deletions
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index b9e07427b..a9645a95e 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -145,9 +145,34 @@ int compare_string(const MDB_val *a, const MDB_val *b)
return strcmp(va, vb);
}
+/* DB schema:
+ *
+ * Table Key Data
+ * ----- --- ----
+ * blocks block ID block blob
+ * block_heights block hash block height
+ * block_info block ID {block metadata}
+ *
+ * txs txn ID txn blob
+ * tx_indices txn hash {txn ID, metadata}
+ * tx_outputs txn ID [txn amount output indices]
+ *
+ * output_txs output ID {txn hash, local index}
+ * output_amounts amount [{amount output index, metadata}...]
+ *
+ * spent_keys output hash -
+ *
+ * Note: where the data items are of uniform size, DUPFIXED tables have
+ * been used to save space. In most of these cases, a dummy "zerokval"
+ * key is used when accessing the table; the Key listed above will be
+ * attached as a prefix on the Data to serve as the DUPSORT key.
+ * (DUPFIXED saves 8 bytes per record.)
+ *
+ * The output_amounts table doesn't use a dummy key, but uses DUPSORT.
+ */
const char* const LMDB_BLOCKS = "blocks";
-const char* const LMDB_BLOCK_INFO = "block_info";
const char* const LMDB_BLOCK_HEIGHTS = "block_heights";
+const char* const LMDB_BLOCK_INFO = "block_info";
const char* const LMDB_TXS = "txs";
const char* const LMDB_TX_INDICES = "tx_indices";
@@ -215,8 +240,8 @@ typedef struct mdb_block_info
} mdb_block_info;
typedef struct blk_height {
- crypto::hash key;
- uint64_t height;
+ crypto::hash bh_hash;
+ uint64_t bh_height;
} blk_height;
typedef struct txindex {
@@ -226,12 +251,12 @@ typedef struct txindex {
typedef struct outkey {
uint64_t amount_index;
- uint64_t tx_index;
+ uint64_t output_id;
output_data_t data;
} outkey;
typedef struct outtx {
- uint64_t txnum;
+ uint64_t output_id;
crypto::hash tx_hash;
uint64_t local_index;
} outtx;
@@ -560,14 +585,13 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
CURSOR(block_heights)
blk_height bh = {blk_hash, m_height};
- MDB_val val_h = {sizeof(bh), (void *)&bh};
+ MDB_val_set(val_h, bh);
if (mdb_cursor_get(m_cur_block_heights, (MDB_val *)&zerokval, &val_h, MDB_GET_BOTH) == 0)
throw1(BLOCK_EXISTS("Attempting to add block that's already in the db"));
if (m_height > 0)
{
- blk_height ph = {blk.prev_id, 0};
- MDB_val parent_key = {sizeof(ph), (void *)&ph};
+ MDB_val_set(parent_key, blk.prev_id);
int result = mdb_cursor_get(m_cur_block_heights, (MDB_val *)&zerokval, &parent_key, MDB_GET_BOTH);
if (result)
{
@@ -576,13 +600,13 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
throw0(DB_ERROR(lmdb_error("Failed to get top block hash to check for new block's parent: ", result).c_str()));
}
blk_height *prev = (blk_height *)parent_key.mv_data;
- if (prev->height != m_height - 1)
+ if (prev->bh_height != m_height - 1)
throw0(BLOCK_PARENT_DNE("Top block is not new block's parent"));
}
int result = 0;
- MDB_val_copy<uint64_t> key(m_height);
+ MDB_val_set(key, m_height);
CURSOR(blocks)
CURSOR(block_info)
@@ -600,7 +624,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
bi.bi_diff = cumulative_difficulty;
bi.bi_hash = blk_hash;
- MDB_val val = {sizeof(bi), (void *)&bi};
+ MDB_val_set(val, bi);
result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add block info to db transaction: ", result).c_str()));
@@ -657,26 +681,27 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
check_open();
mdb_txn_cursors *m_cursors = &m_wcursors;
- int result = 0;
- uint64_t tx_index = m_num_txs;
+ int result;
+ uint64_t tx_id = m_num_txs;
CURSOR(txs)
CURSOR(tx_indices)
- txindex ti = {tx_hash};
- MDB_val_copy<uint64_t> val_tx_index(tx_index);
- MDB_val val_h = {sizeof(ti), (void *)&ti};
+ MDB_val_set(val_tx_id, tx_id);
+ MDB_val_set(val_h, tx_hash);
result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &val_h, MDB_GET_BOTH);
if (result == 0) {
txindex *tip = (txindex *)val_h.mv_data;
- throw1(TX_EXISTS(std::string("Attempting to add transaction that's already in the db (tx index ").append(boost::lexical_cast<std::string>(tip->data.tx_index)).append(")").c_str()));
+ throw1(TX_EXISTS(std::string("Attempting to add transaction that's already in the db (tx id ").append(boost::lexical_cast<std::string>(tip->data.tx_id)).append(")").c_str()));
} else if (result != MDB_NOTFOUND) {
throw1(DB_ERROR(lmdb_error(std::string("Error checking if tx index exists for tx hash ") + epee::string_tools::pod_to_hex(tx_hash) + ": ", result).c_str()));
}
- ti.data.tx_index = tx_index;
+ txindex ti;
+ ti.key = tx_hash;
+ ti.data.tx_id = tx_id;
ti.data.unlock_time = tx.unlock_time;
- ti.data.height = m_height;
+ ti.data.block_id = m_height; // we don't need blk_hash since we know m_height
val_h.mv_size = sizeof(ti);
val_h.mv_data = (void *)&ti;
@@ -686,12 +711,12 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
throw0(DB_ERROR(lmdb_error("Failed to add tx data to db transaction: ", result).c_str()));
MDB_val_copy<blobdata> blob(tx_to_blob(tx));
- result = mdb_cursor_put(m_cur_txs, &val_tx_index, &blob, MDB_APPEND);
+ result = mdb_cursor_put(m_cur_txs, &val_tx_id, &blob, MDB_APPEND);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx blob to db transaction: ", result).c_str()));
m_num_txs++;
- return tx_index;
+ return tx_id;
}
// TODO: compare pros and cons of looking up the tx hash's tx index once and
@@ -708,24 +733,22 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
CURSOR(txs)
CURSOR(tx_outputs)
- txindex ti = {tx_hash};
- MDB_val val_h = {sizeof(ti), (void *)&ti};
+ MDB_val_set(val_h, tx_hash);
if (mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &val_h, MDB_GET_BOTH))
throw1(TX_DNE("Attempting to remove transaction that isn't in the db"));
txindex *tip = (txindex *)val_h.mv_data;
- uint64_t tx_index = tip->data.tx_index;
- MDB_val_copy<uint64_t> val_tx_index(tx_index);
+ MDB_val_set(val_tx_id, tip->data.tx_id);
- if ((result = mdb_cursor_get(m_cur_txs, &val_tx_index, NULL, MDB_SET)))
+ if ((result = mdb_cursor_get(m_cur_txs, &val_tx_id, NULL, MDB_SET)))
throw1(DB_ERROR(lmdb_error("Failed to locate tx for removal: ", result).c_str()));
result = mdb_cursor_del(m_cur_txs, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Failed to add removal of tx to db transaction: ", result).c_str()));
- remove_tx_outputs(tx_index, tx);
+ remove_tx_outputs(tip->data.tx_id, tx);
- result = mdb_cursor_get(m_cur_tx_outputs, &val_tx_index, NULL, MDB_SET);
+ result = mdb_cursor_get(m_cur_tx_outputs, &val_tx_id, NULL, MDB_SET);
if (result == MDB_NOTFOUND)
LOG_PRINT_L1("tx has no outputs to remove: " << tx_hash);
else if (result)
@@ -737,24 +760,17 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
throw1(DB_ERROR(lmdb_error("Failed to add removal of tx outputs to db transaction: ", result).c_str()));
}
- // Though other things could change, so long as earlier functions (like
- // remove_tx_outputs) need to do the lookup of tx hash -> tx index, don't
- // delete the tx_indices entry until the end.
- if (mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &val_h, MDB_GET_BOTH))
- throw1(TX_DNE("Attempting to remove transaction that isn't in the db"));
+ // Don't delete the tx_indices entry until the end, after we're done with val_tx_id
if (mdb_cursor_del(m_cur_tx_indices, 0))
throw1(DB_ERROR("Failed to add removal of tx index to db transaction"));
m_num_txs--;
}
-// global_output_index is no longer used for locating outputs
-void BlockchainLMDB::add_output(const crypto::hash& tx_hash,
+uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
const tx_out& tx_output,
const uint64_t& local_index,
- const uint64_t unlock_time,
- uint64_t& amount_output_index,
- uint64_t& global_output_index)
+ const uint64_t unlock_time)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -791,7 +807,7 @@ void BlockchainLMDB::add_output(const crypto::hash& tx_hash,
throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str()));
else
ok.amount_index = 0;
- ok.tx_index = m_num_outputs;
+ ok.output_id = m_num_outputs;
ok.data.pubkey = boost::get < txout_to_key > (tx_output.target).key;
ok.data.unlock_time = unlock_time;
ok.data.height = m_height;
@@ -801,16 +817,12 @@ void BlockchainLMDB::add_output(const crypto::hash& tx_hash,
if ((result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &data, MDB_APPENDDUP)))
throw0(DB_ERROR(lmdb_error("Failed to add output pubkey to db transaction: ", result).c_str()));
- amount_output_index = ok.amount_index;
- global_output_index = m_num_outputs;
-
m_num_outputs++;
+ return ok.amount_index;
}
-// global_output_indices is now ignored
-void BlockchainLMDB::add_amount_and_global_output_indices(const uint64_t tx_index,
- const std::vector<uint64_t>& amount_output_indices,
- const std::vector<uint64_t>& global_output_indices)
+void BlockchainLMDB::add_tx_amount_output_indices(const uint64_t tx_id,
+ const std::vector<uint64_t>& amount_output_indices)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -821,24 +833,22 @@ void BlockchainLMDB::add_amount_and_global_output_indices(const uint64_t tx_inde
int num_outputs = amount_output_indices.size();
- MDB_val_copy<uint64_t> k_tx_index(tx_index);
+ MDB_val_set(k_tx_id, tx_id);
MDB_val v;
v.mv_data = (void *)amount_output_indices.data();
v.mv_size = sizeof(uint64_t) * num_outputs;
// LOG_PRINT_L1("tx_outputs[tx_hash] size: " << v.mv_size);
- result = mdb_cursor_put(m_cur_tx_outputs, &k_tx_index, &v, MDB_APPEND);
+ result = mdb_cursor_put(m_cur_tx_outputs, &k_tx_id, &v, MDB_APPEND);
if (result)
throw0(DB_ERROR(std::string("Failed to add <tx hash, amount output index array> to db transaction: ").append(mdb_strerror(result)).c_str()));
}
-void BlockchainLMDB::remove_tx_outputs(const uint64_t tx_index, const transaction& tx)
+void BlockchainLMDB::remove_tx_outputs(const uint64_t tx_id, const transaction& tx)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
- // only need amount_output_indices
- std::vector<uint64_t> amount_output_indices, global_output_indices;
- get_amount_and_global_output_indices(tx_index, amount_output_indices, global_output_indices);
+ std::vector<uint64_t> amount_output_indices = get_tx_amount_output_indices(tx_id);
if (amount_output_indices.empty())
{
@@ -880,7 +890,7 @@ void BlockchainLMDB::remove_output(const uint64_t& out_index, const uint64_t amo
throw0(DB_ERROR(lmdb_error("DB error attempting to get an output", result).c_str()));
outkey *ok = (outkey *)v.mv_data;
- MDB_val_set(otxk, ok->tx_index);
+ MDB_val_set(otxk, ok->output_id);
result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &otxk, MDB_GET_BOTH);
if (result == MDB_NOTFOUND)
{
@@ -1333,8 +1343,7 @@ bool BlockchainLMDB::block_exists(const crypto::hash& h) const
RCURSOR(block_heights);
bool ret = false;
- blk_height bh = {h, 0};
- MDB_val key = {sizeof(bh), (void *)&bh};
+ MDB_val_set(key, h);
auto get_result = mdb_cursor_get(m_cur_block_heights, (MDB_val *)&zerokval, &key, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
{
@@ -1365,8 +1374,7 @@ uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const
TXN_PREFIX_RDONLY();
RCURSOR(block_heights);
- blk_height bh = {h, 0};
- MDB_val key = {sizeof(bh), (void *)&bh};
+ MDB_val_set(key, h);
auto get_result = mdb_cursor_get(m_cur_block_heights, (MDB_val *)&zerokval, &key, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
throw1(BLOCK_DNE("Attempted to retrieve non-existent block height"));
@@ -1374,7 +1382,7 @@ uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const
throw0(DB_ERROR("Error attempting to retrieve a block height from the db"));
blk_height *bhp = (blk_height *)key.mv_data;
- uint64_t ret = bhp->height;
+ uint64_t ret = bhp->bh_height;
TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1635,7 +1643,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
RCURSOR(tx_indices);
RCURSOR(txs);
- MDB_val_copy<crypto::hash> key(h);
+ MDB_val_set(key, h);
bool tx_found = false;
TIME_MEASURE_START(time1);
@@ -1666,7 +1674,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
return true;
}
-bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_index) const
+bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_id) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1674,8 +1682,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_index) const
TXN_PREFIX_RDONLY();
RCURSOR(tx_indices);
- txindex ti = {h};
- MDB_val v = {sizeof(ti), (void *)&ti};
+ MDB_val_set(v, h);
TIME_MEASURE_START(time1);
auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
@@ -1683,7 +1690,7 @@ bool BlockchainLMDB::tx_exists(const crypto::hash& h, uint64_t& tx_index) const
time_tx_exists += time1;
if (!get_result) {
txindex *tip = (txindex *)v.mv_data;
- tx_index = tip->data.tx_index;
+ tx_id = tip->data.tx_id;
}
TXN_POSTFIX_RDONLY();
@@ -1709,8 +1716,7 @@ uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const
TXN_PREFIX_RDONLY();
RCURSOR(tx_indices);
- txindex ti = {h};
- MDB_val v = {sizeof(ti), (void *)&ti};
+ MDB_val_set(v, h);
auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
throw1(TX_DNE(lmdb_error(std::string("tx data with hash ") + epee::string_tools::pod_to_hex(h) + " not found in db: ", get_result).c_str()));
@@ -1732,16 +1738,14 @@ transaction BlockchainLMDB::get_tx(const crypto::hash& h) const
RCURSOR(tx_indices);
RCURSOR(txs);
- txindex ti = {h};
- MDB_val v = {sizeof(ti), (void *)&ti};
+ MDB_val_set(v, h);
MDB_val result;
auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
if (get_result == 0)
{
txindex *tip = (txindex *)v.mv_data;
- uint64_t tx_index = tip->data.tx_index;
- MDB_val_copy<uint64_t> val_tx_index(tx_index);
- get_result = mdb_cursor_get(m_cur_txs, &val_tx_index, &result, MDB_SET);
+ MDB_val_set(val_tx_id, tip->data.tx_id);
+ get_result = mdb_cursor_get(m_cur_txs, &val_tx_id, &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()));
@@ -1798,8 +1802,7 @@ uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const
TXN_PREFIX_RDONLY();
RCURSOR(tx_indices);
- txindex ti = {h};
- MDB_val v = {sizeof(ti), (void *)&ti};
+ MDB_val_set(v, h);
auto get_result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
{
@@ -1809,7 +1812,7 @@ uint64_t BlockchainLMDB::get_tx_block_height(const crypto::hash& h) const
throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx height from hash", get_result).c_str()));
txindex *tip = (txindex *)v.mv_data;
- uint64_t ret = tip->data.height;
+ uint64_t ret = tip->data.block_id;
TXN_POSTFIX_RDONLY();
return ret;
}
@@ -1867,7 +1870,7 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6
return ret;
}
-tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& index) const
+tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t& output_id) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1875,7 +1878,7 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index_from_global(const uint64_t&
TXN_PREFIX_RDONLY();
RCURSOR(output_txs);
- MDB_val_set(v, index);
+ MDB_val_set(v, output_id);
auto get_result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
@@ -1903,29 +1906,21 @@ tx_out_index BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, con
return indices[0];
}
-// we don't have global_output_indices
-void BlockchainLMDB::get_amount_and_global_output_indices(const uint64_t tx_index,
- std::vector<uint64_t>& amount_output_indices,
- std::vector<uint64_t>& global_output_indices) const
+std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const uint64_t tx_id) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
- // If a new txn is created, it only needs to read.
- //
- // This must existence of m_write_txn too (not only m_batch_active), as
- // that's what remove_tx_outputs() expected to use instead of creating a new
- // txn, regardless of batch mode. Otherwise, remove_tx_outputs() would now
- // create a new read-only txn here, which is incorrect.
TXN_PREFIX_RDONLY();
RCURSOR(tx_outputs);
int result = 0;
- MDB_val_copy<uint64_t> k_tx_index(tx_index);
+ MDB_val_set(k_tx_id, tx_id);
MDB_val v;
+ std::vector<uint64_t> amount_output_indices;
- result = mdb_cursor_get(m_cur_tx_outputs, &k_tx_index, &v, MDB_SET);
+ result = mdb_cursor_get(m_cur_tx_outputs, &k_tx_id, &v, MDB_SET);
if (result == MDB_NOTFOUND)
LOG_PRINT_L0("WARNING: Unexpected: tx has no amount indices stored in "
"tx_outputs, but it should have an empty entry even if it's a tx without "
@@ -1944,21 +1939,10 @@ void BlockchainLMDB::get_amount_and_global_output_indices(const uint64_t tx_inde
indices = nullptr;
TXN_POSTFIX_RDONLY();
-}
-
-std::vector<uint64_t> BlockchainLMDB::get_tx_amount_output_indices(const uint64_t tx_index) const
-{
- LOG_PRINT_L3("BlockchainLMDB::" << __func__);
-
- std::vector<uint64_t> amount_output_indices, global_output_indices;
- // only need amount_output_indices
- get_amount_and_global_output_indices(tx_index, amount_output_indices, global_output_indices);
-
return amount_output_indices;
}
-
bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -2110,7 +2094,7 @@ bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const c
throw0(DB_ERROR("Failed to enumerate outputs"));
uint64_t amount = *(const uint64_t*)k.mv_data;
outkey *ok = (outkey *)v.mv_data;
- tx_out_index toi = get_output_tx_and_index_from_global(ok->tx_index);
+ tx_out_index toi = get_output_tx_and_index_from_global(ok->output_id);
if (!f(amount, toi.first, toi.second)) {
ret = false;
break;
@@ -2453,9 +2437,9 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6
TXN_PREFIX_RDONLY();
RCURSOR(output_txs);
- for (const uint64_t &index : global_indices)
+ for (const uint64_t &output_id : global_indices)
{
- MDB_val_set(v, index);
+ MDB_val_set(v, output_id);
auto get_result = mdb_cursor_get(m_cur_output_txs, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
@@ -2527,7 +2511,7 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std::
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output from the db", get_result).c_str()));
outkey *okp = (outkey *)v.mv_data;
- tx_indices.push_back(okp->tx_index);
+ tx_indices.push_back(okp->output_id);
}
TIME_MEASURE_START(db3);