From d57a87cdaa4c66dcf22944ae8381f2b9a15764d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= Date: Thu, 17 Oct 2024 13:52:27 +0200 Subject: [PATCH] Optimize getting counts of transaction items 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. --- .../transaction/transaction_history.hpp | 8 ++++ libdnf5-cli/output/transactionlist.cpp | 14 +++--- libdnf5/transaction/db/trans.cpp | 44 +++++++++++++++++++ libdnf5/transaction/db/trans.hpp | 4 ++ libdnf5/transaction/transaction_history.cpp | 5 +++ 5 files changed, 70 insertions(+), 5 deletions(-) diff --git a/include/libdnf5/transaction/transaction_history.hpp b/include/libdnf5/transaction/transaction_history.hpp index 306a07340..55923426b 100644 --- a/include/libdnf5/transaction/transaction_history.hpp +++ b/include/libdnf5/transaction/transaction_history.hpp @@ -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 get_transaction_item_counts(const std::vector & transactions); + private: /// Create a new Transaction object. LIBDNF_LOCAL libdnf5::transaction::Transaction new_transaction(); diff --git a/libdnf5-cli/output/transactionlist.cpp b/libdnf5-cli/output/transactionlist.cpp index c3add2548..5d22a829a 100644 --- a/libdnf5-cli/output/transactionlist.cpp +++ b/libdnf5-cli/output/transactionlist.cpp @@ -24,12 +24,20 @@ along with libdnf. If not, see . #include "libdnf5-cli/tty.hpp" +#include "libdnf5/transaction/transaction_history.hpp" + #include namespace libdnf5::cli::output { void print_transaction_list(std::vector & ts_list) { + std::unordered_map 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 table(scols_new_table(), &scols_unref_table); scols_table_new_column(table.get(), "ID", 0, SCOLS_FL_RIGHT); @@ -49,11 +57,7 @@ void print_transaction_list(std::vector & 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()); diff --git a/libdnf5/transaction/db/trans.cpp b/libdnf5/transaction/db/trans.cpp index f1b0026b6..9edf9b8c6 100644 --- a/libdnf5/transaction/db/trans.cpp +++ b/libdnf5/transaction/db/trans.cpp @@ -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 TransactionDbUtils::transactions_item_counts( + const BaseWeakPtr & base, const std::vector & 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(i + 1), transactions[i].get_id()); + } + + std::unordered_map id_to_count; + + while (query.step() == libdnf5::utils::SQLite3::Statement::StepResult::ROW) { + id_to_count.emplace(query.get("id"), query.get("item_count")); + } + + return id_to_count; +} + } // namespace libdnf5::transaction diff --git a/libdnf5/transaction/db/trans.hpp b/libdnf5/transaction/db/trans.hpp index b21f089f3..b49a7501d 100644 --- a/libdnf5/transaction/db/trans.hpp +++ b/libdnf5/transaction/db/trans.hpp @@ -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 transactions_item_counts( + const BaseWeakPtr & base, const std::vector & transactions); }; } // namespace libdnf5::transaction diff --git a/libdnf5/transaction/transaction_history.cpp b/libdnf5/transaction/transaction_history.cpp index a4e6b7fee..4d53ae888 100644 --- a/libdnf5/transaction/transaction_history.cpp +++ b/libdnf5/transaction/transaction_history.cpp @@ -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 TransactionHistory::get_transaction_item_counts( + const std::vector & transactions) { + return TransactionDbUtils::transactions_item_counts(p_impl->base, transactions); +} + } // namespace libdnf5::transaction