Skip to content

Commit

Permalink
Optimize getting counts of transaction items
Browse files Browse the repository at this point in the history
Previously during `history list` we separately fetched all transaction
packages, groups and environments only to count them. This could be
quite slow because it resulted in a lot of separate DB connections.

Now we fetch only the counts and use only one DB connection.
  • Loading branch information
kontura authored and jan-kolarik committed Oct 24, 2024
1 parent 956c46e commit f1e8d1b
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 5 deletions.
8 changes: 8 additions & 0 deletions include/libdnf5/transaction/transaction_history.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ class LIBDNF_API TransactionHistory {
TransactionItemReason transaction_item_reason_at(
const std::string & name, const std::string & arch, int64_t transaction_id_point);

/// Get counts of transaction items for specified transactions.
/// It gets the counts in a single db query.
///
/// @param transactions Get counts for these transactions.
///
/// @return Mapped transaction id -> count.
std::unordered_map<int64_t, int64_t> get_transaction_item_counts(const std::vector<Transaction> & transactions);

private:
/// Create a new Transaction object.
LIBDNF_LOCAL libdnf5::transaction::Transaction new_transaction();
Expand Down
14 changes: 9 additions & 5 deletions libdnf5-cli/output/transactionlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.

#include "libdnf5-cli/tty.hpp"

#include "libdnf5/transaction/transaction_history.hpp"

#include <libsmartcols/libsmartcols.h>


namespace libdnf5::cli::output {

void print_transaction_list(std::vector<libdnf5::transaction::Transaction> & ts_list) {
std::unordered_map<int64_t, int64_t> id_to_item_count;
if (!ts_list.empty()) {
libdnf5::transaction::TransactionHistory history(ts_list[0].get_base());
id_to_item_count = history.get_transaction_item_counts(ts_list);
}

std::unique_ptr<libscols_table, decltype(&scols_unref_table)> table(scols_new_table(), &scols_unref_table);

scols_table_new_column(table.get(), "ID", 0, SCOLS_FL_RIGHT);
Expand All @@ -49,11 +57,7 @@ void print_transaction_list(std::vector<libdnf5::transaction::Transaction> & ts_
scols_line_set_data(ln, 2, libdnf5::utils::string::format_epoch(ts.get_dt_start()).c_str());
// TODO(lukash) fill the Actions(s), if we even want them?
scols_line_set_data(ln, 3, "");
scols_line_set_data(
ln,
4,
std::to_string(ts.get_packages().size() + ts.get_comps_groups().size() + ts.get_comps_environments().size())
.c_str());
scols_line_set_data(ln, 4, std::to_string(id_to_item_count.at(ts.get_id())).c_str());
}

scols_print_table(table.get());
Expand Down
44 changes: 44 additions & 0 deletions libdnf5/transaction/db/trans.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,48 @@ TransactionItemReason TransactionDbUtils::transaction_item_reason_at(
return TransactionItemReason::NONE;
}

static constexpr const char * ITEM_COUNT_SQL = R"**(
SELECT
"trans"."id",
COUNT("trans_item"."trans_id") AS "item_count"
FROM "trans"
LEFT JOIN "trans_item" ON "trans"."id" = "trans_item"."trans_id"
)**";

std::unordered_map<int64_t, int64_t> TransactionDbUtils::transactions_item_counts(
const BaseWeakPtr & base, const std::vector<Transaction> & transactions) {
auto conn = transaction_db_connect(*base);

std::string sql = ITEM_COUNT_SQL;

if (!transactions.empty()) {
sql += " WHERE \"trans\".\"id\" IN (";
for (size_t i = 0; i < transactions.size(); ++i) {
if (i == 0) {
sql += "?";
} else {
sql += ", ?";
}
}
sql += ")";
}

// GROUP BY has to be after WHERE clause
sql += "GROUP BY \"trans\".\"id\"";

auto query = libdnf5::utils::SQLite3::Query(*conn, sql);

for (size_t i = 0; i < transactions.size(); ++i) {
query.bind(static_cast<int>(i + 1), transactions[i].get_id());
}

std::unordered_map<int64_t, int64_t> id_to_count;

while (query.step() == libdnf5::utils::SQLite3::Statement::StepResult::ROW) {
id_to_count.emplace(query.get<int>("id"), query.get<int64_t>("item_count"));
}

return id_to_count;
}

} // namespace libdnf5::transaction
4 changes: 4 additions & 0 deletions libdnf5/transaction/db/trans.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class TransactionDbUtils {
/// Get reason for name-arch at a point in history specified by transaction id.
static TransactionItemReason transaction_item_reason_at(
const BaseWeakPtr & base, const std::string & name, const std::string & arch, int64_t transaction_id_point);

/// Get transaction item count for history transactions specified by transaction ids.
static std::unordered_map<int64_t, int64_t> transactions_item_counts(
const BaseWeakPtr & base, const std::vector<Transaction> & transactions);
};

} // namespace libdnf5::transaction
Expand Down
5 changes: 5 additions & 0 deletions libdnf5/transaction/transaction_history.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,9 @@ TransactionItemReason TransactionHistory::transaction_item_reason_at(
return TransactionDbUtils::transaction_item_reason_at(p_impl->base, name, arch, transaction_id_point);
}

std::unordered_map<int64_t, int64_t> TransactionHistory::get_transaction_item_counts(
const std::vector<Transaction> & transactions) {
return TransactionDbUtils::transactions_item_counts(p_impl->base, transactions);
}

} // namespace libdnf5::transaction

0 comments on commit f1e8d1b

Please sign in to comment.