fix postgres account_tx performance issue

This commit is contained in:
CJ Cobb
2021-04-13 20:00:47 +00:00
parent d5f21ab295
commit 5364830224

View File

@@ -413,17 +413,19 @@ PostgresBackend::fetchTransactions(
PgQuery pgQuery(pgPool_); PgQuery pgQuery(pgPool_);
pgQuery("SET statement_timeout TO 10000"); pgQuery("SET statement_timeout TO 10000");
std::stringstream sql; std::stringstream sql;
sql << "SELECT transaction,metadata,ledger_seq FROM transactions "
"WHERE ";
bool first = true; bool first = true;
for (auto const& hash : hashes) for (auto const& hash : hashes)
{ {
if (!first) if (!first)
sql << " OR "; sql << " UNION ALL ";
sql << "SELECT transaction,metadata,ledger_seq FROM transactions "
"WHERE ";
sql << "HASH = \'\\x" << ripple::strHex(hash) << "\'"; sql << "HASH = \'\\x" << ripple::strHex(hash) << "\'";
first = false; first = false;
} }
BOOST_LOG_TRIVIAL(debug) << __func__ << " : fetching. sql = " << sql.str();
auto res = pgQuery(sql.str().data()); auto res = pgQuery(sql.str().data());
BOOST_LOG_TRIVIAL(debug) << __func__ << " : fetched";
if (size_t numRows = checkResult(res, 3)) if (size_t numRows = checkResult(res, 3))
{ {
std::vector<TransactionAndMetadata> results; std::vector<TransactionAndMetadata> results;
@@ -498,32 +500,52 @@ PostgresBackend::fetchAccountTransactions(
"account_transactions WHERE account = " "account_transactions WHERE account = "
<< "\'\\x" << ripple::strHex(account) << "\'"; << "\'\\x" << ripple::strHex(account) << "\'";
if (cursor) if (cursor)
sql << " AND (ledger_seq < " << cursor->ledgerSequence sql << " AND ledger_seq = " << cursor->ledgerSequence
<< " OR (ledger_seq = " << cursor->ledgerSequence << " AND transaction_index < " << cursor->transactionIndex;
<< " AND transaction_index < " << cursor->transactionIndex << "))";
sql << " ORDER BY ledger_seq DESC, transaction_index DESC"; sql << " ORDER BY ledger_seq DESC, transaction_index DESC";
sql << " LIMIT " << std::to_string(limit); sql << " LIMIT " << std::to_string(limit);
BOOST_LOG_TRIVIAL(debug) << __func__ << " : " << sql.str(); BOOST_LOG_TRIVIAL(debug) << __func__ << " : fetching " << sql.str();
auto res = pgQuery(sql.str().data()); auto res = pgQuery(sql.str().data());
if (size_t numRows = checkResult(res, 3)) BOOST_LOG_TRIVIAL(debug) << __func__ << " : fetched " << sql.str();
{ size_t numRows = checkResult(res, 3);
std::vector<ripple::uint256> hashes;
for (size_t i = 0; i < numRows; ++i)
{
hashes.push_back(res.asUInt256(i, 0));
}
if (numRows == limit) std::vector<ripple::uint256> hashes;
for (size_t i = 0; i < numRows; ++i)
{
hashes.push_back(res.asUInt256(i, 0));
}
uint32_t newLimit = limit - numRows;
if (newLimit > 0)
{
std::stringstream sql2;
sql2 << "SELECT hash, ledger_seq, transaction_index FROM "
"account_transactions WHERE account = "
<< "\'\\x" << ripple::strHex(account) << "\'";
if (cursor)
sql2 << " AND ledger_seq < " << cursor->ledgerSequence
<< " ORDER BY ledger_seq DESC, transaction_index DESC";
sql2 << " LIMIT " << std::to_string(newLimit);
res = pgQuery(sql2.str().data());
if (numRows = checkResult(res, 3))
{ {
AccountTransactionsCursor retCursor{ for (size_t i = 0; i < numRows; ++i)
res.asBigInt(numRows - 1, 1), res.asBigInt(numRows - 1, 2)}; {
return {fetchTransactions(hashes), {retCursor}}; hashes.push_back(res.asUInt256(i, 0));
} }
else
{
return {fetchTransactions(hashes), {}};
} }
} }
if (hashes.size() == limit)
{
AccountTransactionsCursor retCursor{
res.asBigInt(numRows - 1, 1), res.asBigInt(numRows - 1, 2)};
return {fetchTransactions(hashes), {retCursor}};
}
else
{
return {fetchTransactions(hashes), {}};
}
return {}; return {};
} }
@@ -641,12 +663,13 @@ PostgresBackend::doOnlineDelete(uint32_t minLedgerToKeep) const
} }
else else
{ {
// This is rather unelegant. For a deleted object, we don't // This is rather unelegant. For a deleted object, we
// know its type just from the key (or do we?). So, we just // don't know its type just from the key (or do we?).
// assume it is an offer and try to delete it. The // So, we just assume it is an offer and try to delete
// alternative is to read the actual object out of the db // it. The alternative is to read the actual object out
// from before it was deleted. This could result in a lot of // of the db from before it was deleted. This could
// individual reads though, so we chose to just delete // result in a lot of individual reads though, so we
// chose to just delete
deleteOffer = true; deleteOffer = true;
} }
if (deleteOffer) if (deleteOffer)