From: Thomas Markwalder Date: Wed, 24 Jan 2018 15:57:59 +0000 (-0500) Subject: [5487] Cleaned up, brute force method X-Git-Tag: trac5502_base~5^2~1^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ce6369344d589821091d94e74a7e0b972a560aff;p=thirdparty%2Fkea.git [5487] Cleaned up, brute force method src/lib/dhcpsrv/cql_lease_mgr.cc CqlLeaseStatsQuery - remove derivation from CqlExchange start() - moved aggregation logic into executeSelect executeSelect() - implements explicitly so the raw CQL rows are aggregated directly into the final result set map --- diff --git a/src/lib/dhcpsrv/cql_lease_mgr.cc b/src/lib/dhcpsrv/cql_lease_mgr.cc index ca87638aa8..e40638e514 100644 --- a/src/lib/dhcpsrv/cql_lease_mgr.cc +++ b/src/lib/dhcpsrv/cql_lease_mgr.cc @@ -1443,19 +1443,19 @@ CqlLease6Exchange::getExpiredLeases(const size_t &max_leases, /// This class provides the functionality such as results storage and row /// fetching common to fulfilling the statistical lease data query. /// -class CqlLeaseStatsQuery : public LeaseStatsQuery, CqlExchange { +class CqlLeaseStatsQuery : public LeaseStatsQuery { public: /// @brief Constructor /// /// @param conn A open connection to the database housing the lease data /// @param statement The lease data SQL prepared statement tag to execute /// @param fetch_type Indicates whether or not lease_type should be - /// fetched from the result set + /// fetched from the result set (should be true for v6) CqlLeaseStatsQuery(CqlConnection& conn, StatementTag& statement, const bool fetch_type) : conn_(conn), statement_(statement), fetch_type_(fetch_type), cummulative_rows_(), next_row_(cummulative_rows_.begin()), - subnet_id_(0), type_(0), state_(0) { + subnet_id_(0), lease_type_(0), lease_state_(0) { } /// @brief Destructor @@ -1465,27 +1465,38 @@ public: /// /// The result set is populated by executing a prepared SQL query /// against the database which sums the leases per lease state per - /// subnet id. - void start() { - AnyArray data; // there are no where clause parameters - - // This gets a collection of data for ALL leases, ugh - AnyArray collection = executeSelect(conn_, data, statement_); - - // Now we need to coaelesce the raw rows - for (boost::any &element : collection) { - LeaseStatsRowPtr raw_row = boost::any_cast(element); - auto cum_row = cummulative_rows_.find(*raw_row); - if (cum_row != cummulative_rows_.end()) { - cummulative_rows_[*raw_row] = cum_row->second + 1; - } else { - cummulative_rows_.emplace(std::make_pair(*raw_row, 1)); - } - } - - next_row_ = cummulative_rows_.begin(); - } + /// subnet id. Positions internal row tracking to point to the + /// first row of the aggregate results. + void start(); + /// @brief Executes protocol specific lease query SELECT statement + /// + /// Currently we do not have a good way for Cassandra to roll up the + /// lease counts per subnet, type, and state as we do the other back + /// ends. This method executes the select statement which returns + /// a result set containing a row of data for every lease: + /// -v4 - subnet-id, lease-state + /// -v6 - subnet-id, lease-type, lease-state + /// + /// It then iterates over this result set, aggregating the data into a + /// a map of LeaseStatRows. + /// + /// If we didn't have to roll up the raw lease data first, we could + /// have derived this class from CqlExchange and used it's executeSelect + /// (from which this method borrows heavily). However, that would mean + /// copying all the raw lease data into a collection returned by + /// executeSelect and then aggregating that into cummulative rows. + /// The way we are now we go turn the raw lease data directly into the + /// cummulative row map. + /// + /// @param connection connection used to communicate with the Cassandra + /// database + /// @param where_values array of bound objects used to filter the results + /// @param statement_tag prepared statement being executed + /// + /// @throw DbOperationError + void executeSelect(const CqlConnection& connection, const AnyArray& data, + StatementTag statement_tag); /// @brief Fetches the next row in the result set /// @@ -1498,20 +1509,7 @@ public: /// /// @return True if the fetch succeeded, false if there are no more /// rows to fetch. - bool getNextRow(LeaseStatsRow& row) { - // If we're past the end, punt. - if (next_row_ == cummulative_rows_.end()) { - return (false); - } - - row = next_row_->first; - row.state_count_ = next_row_->second; - - - // Point to the next row. - ++next_row_; - return (true); - } + bool getNextRow(LeaseStatsRow& row); /// @brief Create BIND array to receive C++ data. /// @@ -1521,16 +1519,8 @@ public: /// @param statement_tag prepared statement being executed; defaults to an /// invalid index virtual void - createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL) override; + createBindForSelect(AnyArray& data, StatementTag statement_tag = NULL); - /// @brief Copy received data into the pair. - /// - /// Copies information about the version to be retrieved into a pair. Called - /// in executeSelect(). - /// - /// @return a pointer to the object retrieved. - virtual boost::any retrieve() override; - /// @brief Statement tags definitions /// @{ // Return recalculated lease4 lease statistics @@ -1538,7 +1528,7 @@ public: // Return recalculated lease6 lease statistics static constexpr StatementTag RECOUNT_LEASE6_STATS = "RECOUNT_LEASE6_STATS"; /// @} - + /// @brief Cassandra statements static StatementMap tagged_statements_; @@ -1552,67 +1542,187 @@ private: /// @brief Indicates if query supplies lease type bool fetch_type_; + + /// @brief map containing the aggregated lease counts std::map cummulative_rows_; + /// @brief cursor pointing to the next row to read in aggregate map std::map::iterator next_row_; + /// @brief bind variable for retrieving subnet-id from a result set row int subnet_id_; - int type_; - int state_; + /// @brief bind variable for retrieving lease-type from a result set row + int lease_type_; + /// @brief bind variable for retrieving lease-state from a result set row + int lease_state_; }; constexpr StatementTag CqlLeaseStatsQuery::RECOUNT_LEASE4_STATS; constexpr StatementTag CqlLeaseStatsQuery::RECOUNT_LEASE6_STATS; StatementMap CqlLeaseStatsQuery::tagged_statements_{ - // Return recalculated lease4 lease statistics - {RECOUNT_LEASE4_STATS, - {RECOUNT_LEASE4_STATS, + // Return subnet_id and state of each v4 lease + {RECOUNT_LEASE4_STATS, + {RECOUNT_LEASE4_STATS, "SELECT " "subnet_id, state " "FROM lease4 " -#if 0 - "GROUP BY subnet_id, state " - "ORDER BY subnet_id" -#endif }}, - // Return recalculated lease6 lease statistics - {RECOUNT_LEASE6_STATS, - {RECOUNT_LEASE6_STATS, + // Return subnet_id, lease_type, and state of each v6 lease + {RECOUNT_LEASE6_STATS, + {RECOUNT_LEASE6_STATS, "SELECT " "subnet_id, lease_type, state " "FROM lease6 " -#if 0 - "GROUP BY subnet_id, lease_type, state " - "ORDER BY subnet_id" -#endif }}, }; +void +CqlLeaseStatsQuery::start() { + AnyArray data; // there are no where clause parameters + + // This gets a collection of data for ALL leases, and + // then rolls them up into cummulative_rows_ + executeSelect(conn_, data, statement_); + + // Set our row iterator to the beginning + next_row_ = cummulative_rows_.begin(); +} + +bool +CqlLeaseStatsQuery::getNextRow(LeaseStatsRow& row) { + // If we're past the end, punt. + if (next_row_ == cummulative_rows_.end()) { + return (false); + } + + // Start by copying from the map row key + row.subnet_id_ = next_row_->first.subnet_id_; + row.lease_type_ = next_row_->first.lease_type_; + row.lease_state_ = next_row_->first.lease_state_; + + // Grab the count from the map value + row.state_count_ = next_row_->second; + + // Point to the next row. + ++next_row_; + return (true); +} + void CqlLeaseStatsQuery::createBindForSelect(AnyArray& data, StatementTag) { data.clear(); data.add(&subnet_id_); if (fetch_type_) { - data.add(&type_); + data.add(&lease_type_); } - data.add(&state_); + data.add(&lease_state_); } -boost::any -CqlLeaseStatsQuery::retrieve() { - LeaseStatsRowPtr raw_row(new LeaseStatsRow()); - raw_row->subnet_id_ = subnet_id_; - if (fetch_type_) { - raw_row->lease_type_ = static_cast(type_); +void +CqlLeaseStatsQuery::executeSelect(const CqlConnection& connection, const AnyArray& data, + StatementTag statement_tag) { + CassError rc; + CassStatement* statement = NULL; + CassFuture* future = NULL; + AnyArray local_data = data; + + // Find the query statement first. + StatementMap::const_iterator it = connection.statements_.find(statement_tag); + if (it == connection.statements_.end()) { + isc_throw(DbOperationError, + "CqlLeastStatsQuery::executeSelect(): Statement " + << statement_tag << "has not been prepared."); + } + + // Bind the data before the query is executed. + CqlTaggedStatement tagged_statement = it->second; + if (tagged_statement.is_raw_) { + // The entire query is the first element in data. + std::string* query = boost::any_cast(local_data.back()); + local_data.pop_back(); + statement = cass_statement_new(query->c_str(), local_data.size()); } else { - raw_row->lease_type_ = Lease::TYPE_NA; + statement = cass_prepared_bind(tagged_statement.prepared_statement_); + if (!statement) { + isc_throw(DbOperationError, + "CqlLeaseStatsQuery::executeSelect(): unable to bind statement " + << tagged_statement.name_); + } + } + + // Set specific level of consistency if we're told to do so. + if (connection.force_consistency_) { + rc = cass_statement_set_consistency(statement, connection.consistency_); + if (rc != CASS_OK) { + cass_statement_free(statement); + isc_throw(DbOperationError, + "CqlLeaseStatsQuery::executeSelect(): unable to set statement " + "consistency for statement " + << tagged_statement.name_ + << ", Cassandra error code: " << cass_error_desc(rc)); + } + } + + CqlCommon::bindData(local_data, statement); + + // Everything's ready. Call the actual statement. + future = cass_session_execute(connection.session_, statement); + if (!future) { + cass_statement_free(statement); + isc_throw(DbOperationError, + "CqlLeaseStatsQuery::executeSelect(): no CassFuture for statement " + << tagged_statement.name_); + } + + // Wait for the statement execution to complete. + cass_future_wait(future); + const std::string error = connection.checkFutureError( + "CqlLeaseStatsQuery::executeSelect(): cass_session_execute() != CASS_OK", + future, statement_tag); + rc = cass_future_error_code(future); + if (rc != CASS_OK) { + cass_future_free(future); + cass_statement_free(statement); + isc_throw(DbOperationError, error); + } + + // Get column values. + const CassResult* result_collection = cass_future_get_result(future); + + // lease type is always NA for v4 + if (!fetch_type_) { + lease_type_ = Lease::TYPE_NA; + } + + // Since we're currently forced to pull data for all leases, we + // iterate over them, aggregating them into cummulative LeaseStatsRows + AnyArray return_values; + CassIterator* rows = cass_iterator_from_result(result_collection); + while (cass_iterator_next(rows)) { + const CassRow* row = cass_iterator_get_row(rows); + createBindForSelect(return_values, statement_tag); + CqlCommon::getData(row, return_values); + + LeaseStatsRow raw_row(subnet_id_, static_cast(lease_type_), + lease_state_, 1); + + auto cum_row = cummulative_rows_.find(raw_row); + if (cum_row != cummulative_rows_.end()) { + cummulative_rows_[raw_row] = cum_row->second + 1; + } else { + cummulative_rows_.insert(std::make_pair(raw_row, 1)); + } } - raw_row->lease_state_ = state_; - return raw_row; + // Free resources. + cass_iterator_free(rows); + cass_result_free(result_collection); + cass_future_free(future); + cass_statement_free(statement); + return; } CqlLeaseMgr::CqlLeaseMgr(const DatabaseConnection::ParameterMap ¶meters) diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h index 7ced8f2d78..7911be567b 100644 --- a/src/lib/dhcpsrv/lease_mgr.h +++ b/src/lib/dhcpsrv/lease_mgr.h @@ -101,6 +101,7 @@ struct LeaseStatsRow { lease_state_(lease_state), state_count_(state_count) { } + /// @brief Less-than operator bool operator< (const LeaseStatsRow &rhs) const { if (subnet_id_ < rhs.subnet_id_) { return (true); diff --git a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc index 12685c6c3a..25fe6cfe05 100644 --- a/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_lease_mgr_unittest.cc @@ -724,4 +724,20 @@ TEST_F(CqlLeaseMgrTest, DISABLED_wipeLeases6) { testWipeLeases6(); } +struct Tom { + Tom() : id_(0) { + std::cout << "new:" << this << std::endl; + } + + ~Tom() { + std::cout << "del:" << this << std::endl; + } + + bool operator< (const Tom &rhs) const { + return (rhs.id_ < this->id_); + } + + int id_; +}; + } // namespace