diff options
author | Andrei Pavel <andrei@isc.org> | 2023-04-26 14:45:29 +0200 |
---|---|---|
committer | Andrei Pavel <andrei@isc.org> | 2023-05-17 18:09:38 +0200 |
commit | 4a06d2baf0dff1d21948bd9a39f100c94a159fe0 (patch) | |
tree | 83c4bce0b946e63e204521806ad0341f747e5f46 /src/lib/stats | |
parent | [#2658] add BigIntElement (diff) | |
download | kea-4a06d2baf0dff1d21948bd9a39f100c94a159fe0.tar.xz kea-4a06d2baf0dff1d21948bd9a39f100c94a159fe0.zip |
[#2658] add big integer support to statistics
Pool capacities have been promoted in code from uint64_t to uint128_t.
Bigint is modelled as an int128_t in statistics, following the precedent of
signed integer of int64_t from statistics.
- "total-nas" is extended to 128 bits as requested in the issue.
- "total-pds" has the same risk of overflowing so it has been extended
as well.
- "total-addresses" always fits in 64 bits, but certain code forces a
128 bit value on it. See Pool::getCapacity(), Subnet::getPoolCapacity(),
Subnet::sumPoolCapacity(). It could have been truncated to a 64 bit value,
but that seems like an unnecessary complication.
Because of the disparity in signedness there is some truncation that can
happen when pool capacity values are passed on to statistics. That only happens
for the last half of the value range, so for prefix ranges larger than /1.
Diffstat (limited to 'src/lib/stats')
-rw-r--r-- | src/lib/stats/observation.cc | 63 | ||||
-rw-r--r-- | src/lib/stats/observation.h | 47 | ||||
-rw-r--r-- | src/lib/stats/stats_mgr.cc | 256 | ||||
-rw-r--r-- | src/lib/stats/stats_mgr.h | 16 | ||||
-rw-r--r-- | src/lib/stats/tests/observation_unittest.cc | 122 | ||||
-rw-r--r-- | src/lib/stats/testutils/stats_test_utils.h | 21 |
6 files changed, 323 insertions, 202 deletions
diff --git a/src/lib/stats/observation.cc b/src/lib/stats/observation.cc index c8e7b0f5fd..034f2b9745 100644 --- a/src/lib/stats/observation.cc +++ b/src/lib/stats/observation.cc @@ -9,12 +9,14 @@ #include <stats/observation.h> #include <util/chrono_time_utils.h> #include <cc/data.h> + #include <chrono> #include <utility> using namespace std; using namespace std::chrono; using namespace isc::data; +using namespace isc::util; namespace isc { namespace stats { @@ -33,6 +35,13 @@ Observation::Observation(const std::string& name, const int64_t value) : setValue(value); } +Observation::Observation(const std::string& name, const int128_t& value) : + name_(name), type_(STAT_BIG_INTEGER), + max_sample_count_(default_max_sample_count_), + max_sample_age_(default_max_sample_age_) { + setValue(value); +} + Observation::Observation(const std::string& name, const double value) : name_(name), type_(STAT_FLOAT), max_sample_count_(default_max_sample_count_), @@ -60,6 +69,10 @@ void Observation::setMaxSampleAge(const StatsDuration& duration) { setMaxSampleAgeInternal(integer_samples_, duration, STAT_INTEGER); return; } + case STAT_BIG_INTEGER: { + setMaxSampleAgeInternal(big_integer_samples_, duration, STAT_BIG_INTEGER); + return; + } case STAT_FLOAT: { setMaxSampleAgeInternal(float_samples_, duration, STAT_FLOAT); return; @@ -84,6 +97,10 @@ void Observation::setMaxSampleCount(uint32_t max_samples) { setMaxSampleCountInternal(integer_samples_, max_samples, STAT_INTEGER); return; } + case STAT_BIG_INTEGER: { + setMaxSampleCountInternal(big_integer_samples_, max_samples, STAT_BIG_INTEGER); + return; + } case STAT_FLOAT: { setMaxSampleCountInternal(float_samples_, max_samples, STAT_FLOAT); return; @@ -107,6 +124,11 @@ void Observation::addValue(const int64_t value) { setValue(current.first + value); } +void Observation::addValue(const int128_t& value) { + BigIntegerSample current = getBigInteger(); + setValue(current.first + value); +} + void Observation::addValue(const double value) { FloatSample current = getFloat(); setValue(current.first + value); @@ -126,6 +148,10 @@ void Observation::setValue(const int64_t value) { setValueInternal(value, integer_samples_, STAT_INTEGER); } +void Observation::setValue(const int128_t& value) { + setValueInternal(value, big_integer_samples_, STAT_BIG_INTEGER); +} + void Observation::setValue(const double value) { setValueInternal(value, float_samples_, STAT_FLOAT); } @@ -145,6 +171,10 @@ size_t Observation::getSize() const { size = getSizeInternal(integer_samples_, STAT_INTEGER); return (size); } + case STAT_BIG_INTEGER: { + size = getSizeInternal(big_integer_samples_, STAT_BIG_INTEGER); + return (size); + } case STAT_FLOAT: { size = getSizeInternal(float_samples_, STAT_FLOAT); return (size); @@ -223,6 +253,10 @@ IntegerSample Observation::getInteger() const { return (getValueInternal<IntegerSample>(integer_samples_, STAT_INTEGER)); } +BigIntegerSample Observation::getBigInteger() const { + return (getValueInternal<BigIntegerSample>(big_integer_samples_, STAT_BIG_INTEGER)); +} + FloatSample Observation::getFloat() const { return (getValueInternal<FloatSample>(float_samples_, STAT_FLOAT)); } @@ -256,6 +290,10 @@ std::list<IntegerSample> Observation::getIntegers() const { return (getValuesInternal<IntegerSample>(integer_samples_, STAT_INTEGER)); } +std::list<BigIntegerSample> Observation::getBigIntegers() const { + return (getValuesInternal<BigIntegerSample>(big_integer_samples_, STAT_BIG_INTEGER)); +} + std::list<FloatSample> Observation::getFloats() const { return (getValuesInternal<FloatSample>(float_samples_, STAT_FLOAT)); } @@ -370,6 +408,9 @@ std::string Observation::typeToText(Type type) { case STAT_INTEGER: tmp << "integer"; break; + case STAT_BIG_INTEGER: + tmp << "big integer"; + break; case STAT_FLOAT: tmp << "float"; break; @@ -414,6 +455,23 @@ Observation::getJSON() const { } break; } + case STAT_BIG_INTEGER: { + std::list<BigIntegerSample> const& samples(getBigIntegers()); + + // Iterate over all elements in the list and alternately add + // value and timestamp to the entry. + for (BigIntegerSample const& i : samples) { + entry = isc::data::Element::createList(); + value = isc::data::Element::create(i.first); + timestamp = isc::data::Element::create(isc::util::clockToText(i.second)); + + entry->add(value); + entry->add(timestamp); + + list->add(entry); + } + break; + } case STAT_FLOAT: { std::list<FloatSample> s = getFloats(); @@ -480,6 +538,11 @@ void Observation::reset() { setValue(static_cast<int64_t>(0)); return; } + case STAT_BIG_INTEGER: { + big_integer_samples_.clear(); + setValue(int128_t(0)); + return; + } case STAT_FLOAT: { float_samples_.clear(); setValue(0.0); diff --git a/src/lib/stats/observation.h b/src/lib/stats/observation.h index f1e0117ef4..883d7e7d1d 100644 --- a/src/lib/stats/observation.h +++ b/src/lib/stats/observation.h @@ -9,9 +9,13 @@ #include <cc/data.h> #include <exceptions/exceptions.h> +#include <util/bigints.h> + #include <boost/shared_ptr.hpp> + #include <chrono> #include <list> + #include <stdint.h> namespace isc { @@ -55,6 +59,9 @@ inline long toSeconds(const StatsDuration& dur) { /// @brief Integer (implemented as signed 64-bit integer) typedef std::pair<int64_t, SampleClock::time_point> IntegerSample; +/// @brief BigInteger (implemented as signed 128-bit integer) +typedef std::pair<isc::util::int128_t, SampleClock::time_point> BigIntegerSample; + /// @brief Float (implemented as double precision) typedef std::pair<double, SampleClock::time_point> FloatSample; @@ -93,10 +100,11 @@ public: /// int64_t and double. If convincing use cases appear to change them /// to something else, we may change the underlying type. enum Type { - STAT_INTEGER, ///< this statistic is unsigned 64-bit integer value - STAT_FLOAT, ///< this statistic is a floating point value - STAT_DURATION,///< this statistic represents time duration - STAT_STRING ///< this statistic represents a string + STAT_INTEGER, ///< this statistic is signed 64-bit integer value + STAT_BIG_INTEGER, ///< this statistic is signed 128-bit integer value + STAT_FLOAT, ///< this statistic is a floating point value + STAT_DURATION, ///< this statistic represents time duration + STAT_STRING ///< this statistic represents a string }; /// @brief Constructor for integer observations @@ -105,6 +113,12 @@ public: /// @param value integer value observed. Observation(const std::string& name, const int64_t value); + /// @brief Constructor for big integer observations + /// + /// @param name observation name + /// @param value integer value observed. + Observation(const std::string& name, const isc::util::int128_t& value); + /// @brief Constructor for floating point observations /// /// @param name observation name @@ -184,6 +198,12 @@ public: /// @throw InvalidStatType if statistic is not integer void setValue(const int64_t value); + /// @brief Records big integer observation + /// + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void setValue(const isc::util::int128_t& value); + /// @brief Records absolute floating point observation /// /// @param value floating point value observed @@ -208,6 +228,12 @@ public: /// @throw InvalidStatType if statistic is not integer void addValue(const int64_t value); + /// @brief Records incremental integer observation + /// + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void addValue(const isc::util::int128_t& value); + /// @brief Records incremental floating point observation /// /// @param value floating point value observed @@ -258,6 +284,11 @@ public: /// @throw InvalidStatType if statistic is not integer IntegerSample getInteger() const; + /// @brief Returns observed integer sample + /// @return observed sample (value + timestamp) + /// @throw InvalidStatType if statistic is not integer + BigIntegerSample getBigInteger() const; + /// @brief Returns observed float sample /// @return observed sample (value + timestamp) /// @throw InvalidStatType if statistic is not fp @@ -278,6 +309,11 @@ public: /// @throw InvalidStatType if statistic is not integer std::list<IntegerSample> getIntegers() const; + /// @brief Returns observed big-integer samples + /// @return list of observed samples (value + timestamp) + /// @throw InvalidStatType if statistic is not integer + std::list<BigIntegerSample> getBigIntegers() const; + /// @brief Returns observed float samples /// @return list of observed samples (value + timestamp) /// @throw InvalidStatType if statistic is not fp @@ -425,6 +461,9 @@ private: /// @brief Storage for integer samples std::list<IntegerSample> integer_samples_; + /// @brief Storage for big integer samples + std::list<BigIntegerSample> big_integer_samples_; + /// @brief Storage for floating point samples std::list<FloatSample> float_samples_; diff --git a/src/lib/stats/stats_mgr.cc b/src/lib/stats/stats_mgr.cc index 58b36abeed..37536d31d0 100644 --- a/src/lib/stats/stats_mgr.cc +++ b/src/lib/stats/stats_mgr.cc @@ -11,7 +11,10 @@ #include <cc/data.h> #include <cc/command_interpreter.h> #include <util/multi_threading_mgr.h> +#include <util/bigints.h> + #include <boost/make_shared.hpp> + #include <chrono> using namespace std; @@ -35,92 +38,68 @@ StatsMgr::StatsMgr() : void StatsMgr::setValue(const string& name, const int64_t value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); +} + +void +StatsMgr::setValue(const string& name, const int128_t& value) { + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::setValue(const string& name, const double value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::setValue(const string& name, const StatsDuration& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::setValue(const string& name, const string& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setValueInternal(name, value); - } else { - setValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + setValueInternal(name, value); } void StatsMgr::addValue(const string& name, const int64_t value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); +} + +void +StatsMgr::addValue(const string& name, const int128_t& value) { + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } void StatsMgr::addValue(const string& name, const double value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } void StatsMgr::addValue(const string& name, const StatsDuration& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } void StatsMgr::addValue(const string& name, const string& value) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addValueInternal(name, value); - } else { - addValueInternal(name, value); - } + MultiThreadingLock lock(*mutex_); + addValueInternal(name, value); } ObservationPtr StatsMgr::getObservation(const string& name) const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getObservationInternal(name)); - } else { - return (getObservationInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (getObservationInternal(name)); } ObservationPtr @@ -132,12 +111,8 @@ StatsMgr::getObservationInternal(const string& name) const { void StatsMgr::addObservation(const ObservationPtr& stat) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - addObservationInternal(stat); - } else { - addObservationInternal(stat); - } + MultiThreadingLock lock(*mutex_); + addObservationInternal(stat); } void @@ -149,12 +124,8 @@ StatsMgr::addObservationInternal(const ObservationPtr& stat) { bool StatsMgr::deleteObservation(const string& name) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (deleteObservationInternal(name)); - } else { - return (deleteObservationInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (deleteObservationInternal(name)); } bool @@ -166,12 +137,8 @@ StatsMgr::deleteObservationInternal(const string& name) { bool StatsMgr::setMaxSampleAge(const string& name, const StatsDuration& duration) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (setMaxSampleAgeInternal(name, duration)); - } else { - return (setMaxSampleAgeInternal(name, duration)); - } + MultiThreadingLock lock(*mutex_); + return (setMaxSampleAgeInternal(name, duration)); } bool @@ -187,12 +154,8 @@ StatsMgr::setMaxSampleAgeInternal(const string& name, bool StatsMgr::setMaxSampleCount(const string& name, uint32_t max_samples) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (setMaxSampleCountInternal(name, max_samples)); - } else { - return (setMaxSampleCountInternal(name, max_samples)); - } + MultiThreadingLock lock(*mutex_); + return (setMaxSampleCountInternal(name, max_samples)); } bool @@ -208,12 +171,8 @@ StatsMgr::setMaxSampleCountInternal(const string& name, void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleAgeAllInternal(duration); - } else { - setMaxSampleAgeAllInternal(duration); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleAgeAllInternal(duration); } void @@ -223,12 +182,8 @@ StatsMgr::setMaxSampleAgeAllInternal(const StatsDuration& duration) { void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleCountAllInternal(max_samples); - } else { - setMaxSampleCountAllInternal(max_samples); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleCountAllInternal(max_samples); } void @@ -238,12 +193,8 @@ StatsMgr::setMaxSampleCountAllInternal(uint32_t max_samples) { void StatsMgr::setMaxSampleAgeDefault(const StatsDuration& duration) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleAgeDefaultInternal(duration); - } else { - setMaxSampleAgeDefaultInternal(duration); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleAgeDefaultInternal(duration); } void @@ -253,12 +204,8 @@ StatsMgr::setMaxSampleAgeDefaultInternal(const StatsDuration& duration) { void StatsMgr::setMaxSampleCountDefault(uint32_t max_samples) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - setMaxSampleCountDefaultInternal(max_samples); - } else { - setMaxSampleCountDefaultInternal(max_samples); - } + MultiThreadingLock lock(*mutex_); + setMaxSampleCountDefaultInternal(max_samples); } void @@ -268,12 +215,8 @@ StatsMgr::setMaxSampleCountDefaultInternal(uint32_t max_samples) { const StatsDuration& StatsMgr::getMaxSampleAgeDefault() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getMaxSampleAgeDefaultInternal()); - } else { - return (getMaxSampleAgeDefaultInternal()); - } + MultiThreadingLock lock(*mutex_); + return (getMaxSampleAgeDefaultInternal()); } const StatsDuration& @@ -283,12 +226,8 @@ StatsMgr::getMaxSampleAgeDefaultInternal() const { uint32_t StatsMgr::getMaxSampleCountDefault() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getMaxSampleCountDefaultInternal()); - } else { - return (getMaxSampleCountDefaultInternal()); - } + MultiThreadingLock lock(*mutex_); + return (getMaxSampleCountDefaultInternal()); } uint32_t @@ -298,12 +237,8 @@ StatsMgr::getMaxSampleCountDefaultInternal() const { bool StatsMgr::reset(const string& name) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (resetInternal(name)); - } else { - return (resetInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (resetInternal(name)); } bool @@ -318,12 +253,8 @@ StatsMgr::resetInternal(const string& name) { bool StatsMgr::del(const string& name) { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (delInternal(name)); - } else { - return (delInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (delInternal(name)); } bool @@ -333,12 +264,8 @@ StatsMgr::delInternal(const string& name) { void StatsMgr::removeAll() { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - removeAllInternal(); - } else { - removeAllInternal(); - } + MultiThreadingLock lock(*mutex_); + removeAllInternal(); } void @@ -348,12 +275,8 @@ StatsMgr::removeAllInternal() { ConstElementPtr StatsMgr::get(const string& name) const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getInternal(name)); - } else { - return (getInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (getInternal(name)); } ConstElementPtr @@ -368,12 +291,8 @@ StatsMgr::getInternal(const string& name) const { ConstElementPtr StatsMgr::getAll() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getAllInternal()); - } else { - return (getAllInternal()); - } + MultiThreadingLock lock(*mutex_); + return (getAllInternal()); } ConstElementPtr @@ -383,12 +302,8 @@ StatsMgr::getAllInternal() const { void StatsMgr::resetAll() { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - resetAllInternal(); - } else { - resetAllInternal(); - } + MultiThreadingLock lock(*mutex_); + resetAllInternal(); } void @@ -398,12 +313,8 @@ StatsMgr::resetAllInternal() { size_t StatsMgr::getSize(const string& name) const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (getSizeInternal(name)); - } else { - return (getSizeInternal(name)); - } + MultiThreadingLock lock(*mutex_); + return (getSizeInternal(name)); } size_t @@ -417,12 +328,8 @@ StatsMgr::getSizeInternal(const string& name) const { size_t StatsMgr::count() const { - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - return (countInternal()); - } else { - return (countInternal()); - } + MultiThreadingLock lock(*mutex_); + return (countInternal()); } size_t @@ -545,16 +452,10 @@ StatsMgr::statisticSetMaxSampleAgeAllHandler(const ConstElementPtr& params) { if (!StatsMgr::getStatDuration(params, duration, error)) { return (createAnswer(CONTROL_RESULT_ERROR, error)); } - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - StatsMgr::instance().setMaxSampleCountDefaultInternal(0); - StatsMgr::instance().setMaxSampleAgeDefaultInternal(duration); - StatsMgr::instance().setMaxSampleAgeAllInternal(duration); - } else { - StatsMgr::instance().setMaxSampleCountDefaultInternal(0); - StatsMgr::instance().setMaxSampleAgeDefaultInternal(duration); - StatsMgr::instance().setMaxSampleAgeAllInternal(duration); - } + MultiThreadingLock lock(*mutex_); + StatsMgr::instance().setMaxSampleCountDefaultInternal(0); + StatsMgr::instance().setMaxSampleAgeDefaultInternal(duration); + StatsMgr::instance().setMaxSampleAgeAllInternal(duration); return (createAnswer(CONTROL_RESULT_SUCCESS, "All statistics duration limit are set.")); } @@ -570,14 +471,9 @@ StatsMgr::statisticSetMaxSampleCountAllHandler(const ConstElementPtr& params) { error = "'max-samples' parameter must not be zero"; return (createAnswer(CONTROL_RESULT_ERROR, error)); } - if (MultiThreadingMgr::instance().getMode()) { - lock_guard<mutex> lock(*mutex_); - StatsMgr::instance().setMaxSampleCountDefaultInternal(max_samples); - StatsMgr::instance().setMaxSampleCountAllInternal(max_samples); - } else { - StatsMgr::instance().setMaxSampleCountDefaultInternal(max_samples); - StatsMgr::instance().setMaxSampleCountAllInternal(max_samples); - } + MultiThreadingLock lock(*mutex_); + StatsMgr::instance().setMaxSampleCountDefaultInternal(max_samples); + StatsMgr::instance().setMaxSampleCountAllInternal(max_samples); return (createAnswer(CONTROL_RESULT_SUCCESS, "All statistics count limit are set.")); } diff --git a/src/lib/stats/stats_mgr.h b/src/lib/stats/stats_mgr.h index 564df43976..c1e688d290 100644 --- a/src/lib/stats/stats_mgr.h +++ b/src/lib/stats/stats_mgr.h @@ -9,6 +9,8 @@ #include <stats/observation.h> #include <stats/context.h> +#include <util/bigints.h> + #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> @@ -79,6 +81,13 @@ public: /// @throw InvalidStatType if statistic is not integer void setValue(const std::string& name, const int64_t value); + /// @brief Records an absolute big integer observation. + /// + /// @param name name of the observation + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void setValue(const std::string& name, const isc::util::int128_t& value); + /// @brief Records absolute floating point observation. /// /// @param name name of the observation @@ -107,6 +116,13 @@ public: /// @throw InvalidStatType if statistic is not integer void addValue(const std::string& name, const int64_t value); + /// @brief Records an incremental big integer observation. + /// + /// @param name name of the observation + /// @param value integer value observed + /// @throw InvalidStatType if statistic is not integer + void addValue(const std::string& name, const isc::util::int128_t& value); + /// @brief Records incremental floating point observation. /// /// @param name name of the observation diff --git a/src/lib/stats/tests/observation_unittest.cc b/src/lib/stats/tests/observation_unittest.cc index ad1036809c..dc0a4d9826 100644 --- a/src/lib/stats/tests/observation_unittest.cc +++ b/src/lib/stats/tests/observation_unittest.cc @@ -6,10 +6,13 @@ #include <config.h> -#include <stats/observation.h> #include <exceptions/exceptions.h> +#include <stats/observation.h> #include <util/chrono_time_utils.h> +#include <util/bigints.h> + #include <boost/shared_ptr.hpp> + #include <gtest/gtest.h> #include <iostream> @@ -19,6 +22,7 @@ using namespace isc; using namespace isc::stats; +using namespace isc::util; using namespace std::chrono; namespace { @@ -51,13 +55,15 @@ public: a("alpha", static_cast<int64_t>(1234)), // integer b("beta", 12.34), // float c("gamma", dur1234), // duration - d("delta", "1234") { // string + d("delta", "1234"), // string + e("epsilon", int128_t(12e34)) { // big integer } Observation a; Observation b; Observation c; Observation d; + Observation e; }; // Basic tests for the Observation constructors. This test checks whether @@ -67,29 +73,40 @@ TEST_F(ObservationTest, constructor) { EXPECT_EQ(Observation::STAT_FLOAT, b.getType()); EXPECT_EQ(Observation::STAT_DURATION, c.getType()); EXPECT_EQ(Observation::STAT_STRING, d.getType()); + EXPECT_EQ(Observation::STAT_BIG_INTEGER, e.getType()); EXPECT_EQ(1234, a.getInteger().first); EXPECT_EQ(12.34, b.getFloat().first); EXPECT_EQ(dur1234, c.getDuration().first); EXPECT_EQ("1234", d.getString().first); + EXPECT_EQ(int128_t(12e34), e.getBigInteger().first); // Let's check that attempting to get a different type // than used will cause an exception. EXPECT_THROW(a.getFloat(), InvalidStatType); EXPECT_THROW(a.getDuration(), InvalidStatType); EXPECT_THROW(a.getString(), InvalidStatType); + EXPECT_THROW(a.getBigInteger(), InvalidStatType); EXPECT_THROW(b.getInteger(), InvalidStatType); EXPECT_THROW(b.getDuration(), InvalidStatType); EXPECT_THROW(b.getString(), InvalidStatType); + EXPECT_THROW(b.getBigInteger(), InvalidStatType); EXPECT_THROW(c.getInteger(), InvalidStatType); EXPECT_THROW(c.getFloat(), InvalidStatType); EXPECT_THROW(c.getString(), InvalidStatType); + EXPECT_THROW(c.getBigInteger(), InvalidStatType); EXPECT_THROW(d.getInteger(), InvalidStatType); EXPECT_THROW(d.getFloat(), InvalidStatType); EXPECT_THROW(d.getDuration(), InvalidStatType); + EXPECT_THROW(d.getBigInteger(), InvalidStatType); + + EXPECT_THROW(e.getInteger(), InvalidStatType); + EXPECT_THROW(e.getFloat(), InvalidStatType); + EXPECT_THROW(e.getDuration(), InvalidStatType); + EXPECT_THROW(e.getString(), InvalidStatType); } // This test checks whether it is possible to set to an absolute value for all @@ -99,30 +116,40 @@ TEST_F(ObservationTest, setValue) { EXPECT_NO_THROW(b.setValue(56e+78)); EXPECT_NO_THROW(c.setValue(dur5678)); EXPECT_NO_THROW(d.setValue("fiveSixSevenEight")); - + EXPECT_NO_THROW(e.setValue(int128_t(43e21))); EXPECT_EQ(5678, a.getInteger().first); EXPECT_EQ(56e+78, b.getFloat().first); EXPECT_EQ(dur5678, c.getDuration().first); EXPECT_EQ("fiveSixSevenEight", d.getString().first); + EXPECT_EQ(int128_t(43e21), e.getBigInteger().first); // Now check whether setting value to a different type does // throw an exception EXPECT_THROW(a.setValue(56e+78), InvalidStatType); EXPECT_THROW(a.setValue(dur5678), InvalidStatType); EXPECT_THROW(a.setValue("fiveSixSevenEight"), InvalidStatType); + EXPECT_THROW(a.setValue(int128_t(43e21)), InvalidStatType); EXPECT_THROW(b.setValue(static_cast<int64_t>(5678)), InvalidStatType); EXPECT_THROW(b.setValue(dur5678), InvalidStatType); EXPECT_THROW(b.setValue("fiveSixSevenEight"), InvalidStatType); + EXPECT_THROW(b.setValue(int128_t(43e21)), InvalidStatType); EXPECT_THROW(c.setValue(static_cast<int64_t>(5678)), InvalidStatType); EXPECT_THROW(c.setValue(56e+78), InvalidStatType); EXPECT_THROW(c.setValue("fiveSixSevenEight"), InvalidStatType); + EXPECT_THROW(c.setValue(int128_t(43e21)), InvalidStatType); EXPECT_THROW(d.setValue(static_cast<int64_t>(5678)), InvalidStatType); EXPECT_THROW(d.setValue(56e+78), InvalidStatType); EXPECT_THROW(d.setValue(dur5678), InvalidStatType); + EXPECT_THROW(d.setValue(int128_t(43e21)), InvalidStatType); + + EXPECT_THROW(e.setValue(int64_t(5678)), InvalidStatType); + EXPECT_THROW(e.setValue(56e+78), InvalidStatType); + EXPECT_THROW(e.setValue(dur5678), InvalidStatType); + EXPECT_THROW(e.setValue("fiveSixSevenEight"), InvalidStatType); } // This test checks whether it is possible to add value to existing @@ -135,16 +162,19 @@ TEST_F(ObservationTest, addValue) { EXPECT_NO_THROW(b.addValue(56.78)); EXPECT_NO_THROW(c.addValue(dur5678)); EXPECT_NO_THROW(d.addValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.addValue(int128_t(43e21))); EXPECT_EQ(6912, a.getInteger().first); EXPECT_EQ(69.12, b.getFloat().first); EXPECT_EQ(dur681012, c.getDuration().first); EXPECT_EQ("1234fiveSixSevenEight", d.getString().first); + EXPECT_EQ(int128_t(12e34) + int128_t(43e21), e.getBigInteger().first); ASSERT_EQ(a.getSize(), 2); ASSERT_EQ(b.getSize(), 2); ASSERT_EQ(c.getSize(), 2); ASSERT_EQ(d.getSize(), 2); + ASSERT_EQ(e.getSize(), 2); } // This test checks if collecting more than one sample @@ -153,37 +183,42 @@ TEST_F(ObservationTest, moreThanOne) { // Arrays of 4 types of samples int64_t int_samples[3] = {1234, 6912, 5678}; double float_samples[3] = {12.34, 69.12, 56e+78}; - StatsDuration duration_samples[3] = {dur1234, - dur681012, dur5678}; + StatsDuration duration_samples[3] = {dur1234, dur681012, dur5678}; std::string string_samples[3] = {"1234", "1234fiveSixSevenEight", "fiveSixSevenEight"}; + int128_t bigint_samples[3] = {int128_t(12e34), int128_t(12e34) + int128_t(43e21), + int128_t(43e21)}; EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678))); EXPECT_NO_THROW(b.addValue(56.78)); EXPECT_NO_THROW(c.addValue(dur5678)); EXPECT_NO_THROW(d.addValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.addValue(int128_t(43e21))); EXPECT_NO_THROW(a.setValue(static_cast<int64_t>(5678))); EXPECT_NO_THROW(b.setValue(56e+78)); EXPECT_NO_THROW(c.setValue(dur5678)); EXPECT_NO_THROW(d.setValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.setValue(int128_t(43e21))); ASSERT_EQ(a.getSize(), 3); ASSERT_EQ(b.getSize(), 3); ASSERT_EQ(c.getSize(), 3); ASSERT_EQ(d.getSize(), 3); + ASSERT_EQ(e.getSize(), 3); ASSERT_NO_THROW(a.getIntegers()); ASSERT_NO_THROW(b.getFloats()); ASSERT_NO_THROW(c.getDurations()); ASSERT_NO_THROW(d.getStrings()); + ASSERT_NO_THROW(e.getBigIntegers()); std::list<IntegerSample> samples_int = a.getIntegers(); // List of all integer samples std::list<FloatSample> samples_float = b.getFloats(); // List of all float samples std::list<DurationSample> samples_dur = c.getDurations(); // List of all duration samples std::list<StringSample> samples_str = d.getStrings(); // List of all string samples + std::list<BigIntegerSample> samples_bigint = e.getBigIntegers(); // List of all big integer samples uint32_t i = 2; // Index pointed to the end of array of samples - for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) { EXPECT_EQ(int_samples[i], static_cast<int64_t>((*it).first)); --i; @@ -203,6 +238,11 @@ TEST_F(ObservationTest, moreThanOne) { EXPECT_EQ(string_samples[i], (*it).first); --i; } + i = 2; + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(bigint_samples[i], sample.first); + --i; + } } // This test checks whether the size of storage @@ -213,27 +253,32 @@ TEST_F(ObservationTest, getSize) { ASSERT_EQ(b.getSize(), 1); ASSERT_EQ(c.getSize(), 1); ASSERT_EQ(d.getSize(), 1); + ASSERT_EQ(e.getSize(), 1); a.addValue(static_cast<int64_t>(5678)); b.addValue(56.78); c.addValue(dur5678); d.addValue("fiveSixSevenEight"); + e.addValue(int128_t(43e21)); EXPECT_NO_THROW(a.getSize()); EXPECT_NO_THROW(b.getSize()); EXPECT_NO_THROW(c.getSize()); EXPECT_NO_THROW(d.getSize()); + EXPECT_NO_THROW(e.getSize()); // Check if size of storages is equal to 2 ASSERT_EQ(a.getSize(), 2); ASSERT_EQ(b.getSize(), 2); ASSERT_EQ(c.getSize(), 2); ASSERT_EQ(d.getSize(), 2); + ASSERT_EQ(e.getSize(), 2); a.setValue(static_cast<int64_t>(5678)); b.setValue(56e+78); c.setValue(dur5678); d.setValue("fiveSixSevenEight"); + e.setValue(int128_t(43e21)); EXPECT_NO_THROW(a.getSize()); EXPECT_NO_THROW(b.getSize()); @@ -245,6 +290,7 @@ TEST_F(ObservationTest, getSize) { ASSERT_EQ(b.getSize(), 3); ASSERT_EQ(c.getSize(), 3); ASSERT_EQ(d.getSize(), 3); + ASSERT_EQ(e.getSize(), 3); } // Checks whether setting amount limits works properly @@ -279,18 +325,23 @@ TEST_F(ObservationTest, setCountLimit) { for (uint32_t i = 0; i < 21; ++i) { d.setValue(string_samples[i]); } + for (uint32_t i = 0; i < 21; ++i) { + e.setValue(int128_t(int_samples[i])); + } // Getting all 4 types of samples after inserting 21 values std::list<IntegerSample> samples_int = a.getIntegers(); std::list<FloatSample> samples_float = b.getFloats(); std::list<DurationSample> samples_duration = c.getDurations(); std::list<StringSample> samples_string = d.getStrings(); + std::list<BigIntegerSample> samples_bigint = e.getBigIntegers(); // Check if size of storages is equal to 20 ASSERT_EQ(a.getSize(), 20); ASSERT_EQ(b.getSize(), 20); ASSERT_EQ(c.getSize(), 20); ASSERT_EQ(d.getSize(), 20); + ASSERT_EQ(e.getSize(), 20); // And whether stored values are correct uint32_t i = 20; // index of the last element in array of test's samples @@ -313,23 +364,31 @@ TEST_F(ObservationTest, setCountLimit) { EXPECT_EQ((*it).first, string_samples[i]); --i; } + i = 20; // index of last element in array of test's samples + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(sample.first, int_samples[i]); + --i; + } // Change size of storage to smaller one ASSERT_NO_THROW(a.setMaxSampleCount(10)); ASSERT_NO_THROW(b.setMaxSampleCount(10)); ASSERT_NO_THROW(c.setMaxSampleCount(10)); ASSERT_NO_THROW(d.setMaxSampleCount(10)); + ASSERT_NO_THROW(e.setMaxSampleCount(10)); samples_int = a.getIntegers(); samples_float = b.getFloats(); samples_duration = c.getDurations(); samples_string = d.getStrings(); + samples_bigint = e.getBigIntegers(); // Check if size of storages is equal to 10 ASSERT_EQ(a.getSize(), 10); ASSERT_EQ(b.getSize(), 10); ASSERT_EQ(c.getSize(), 10); ASSERT_EQ(d.getSize(), 10); + ASSERT_EQ(e.getSize(), 10); // And whether storages contain only the 10 newest values i = 20; // index of last element in array of test's samples @@ -352,34 +411,44 @@ TEST_F(ObservationTest, setCountLimit) { EXPECT_EQ((*it).first, string_samples[i]); --i; } + i = 20; // index of last element in array of test's samples + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(sample.first, int_samples[i]); + --i; + } // Resize max_sample_count to greater ASSERT_NO_THROW(a.setMaxSampleCount(50)); ASSERT_NO_THROW(b.setMaxSampleCount(50)); ASSERT_NO_THROW(c.setMaxSampleCount(50)); ASSERT_NO_THROW(d.setMaxSampleCount(50)); + ASSERT_NO_THROW(e.setMaxSampleCount(50)); // Check if size of storages did not change without adding new value ASSERT_EQ(a.getSize(), 10); ASSERT_EQ(b.getSize(), 10); ASSERT_EQ(c.getSize(), 10); ASSERT_EQ(d.getSize(), 10); + ASSERT_EQ(e.getSize(), 10); // Add new values to each type of Observation a.setValue(static_cast<int64_t>(21)); b.setValue(21.0); c.setValue(milliseconds(21)); d.setValue("v"); + e.setValue(int128_t(21)); samples_int = a.getIntegers(); samples_float = b.getFloats(); samples_duration = c.getDurations(); samples_string = d.getStrings(); + samples_bigint = e.getBigIntegers(); ASSERT_EQ(a.getSize(), 11); ASSERT_EQ(b.getSize(), 11); ASSERT_EQ(c.getSize(), 11); ASSERT_EQ(d.getSize(), 11); + ASSERT_EQ(e.getSize(), 11); i = 21; // index of last element in array of test's samples for (std::list<IntegerSample>::iterator it = samples_int.begin(); it != samples_int.end(); ++it) { @@ -401,7 +470,11 @@ TEST_F(ObservationTest, setCountLimit) { EXPECT_EQ((*it).first, string_samples[i]); --i; } - + i = 21; // index of last element in array of test's samples + for (BigIntegerSample const& sample : samples_bigint) { + EXPECT_EQ(sample.first, int_samples[i]); + --i; + } } // Checks whether setting age limits works properly @@ -451,42 +524,50 @@ TEST_F(ObservationTest, getLimits) { EXPECT_EQ(b.getMaxSampleAge().first, false); EXPECT_EQ(c.getMaxSampleAge().first, false); EXPECT_EQ(d.getMaxSampleAge().first, false); + EXPECT_EQ(e.getMaxSampleAge().first, false); EXPECT_EQ(a.getMaxSampleCount().first, true); EXPECT_EQ(b.getMaxSampleCount().first, true); EXPECT_EQ(c.getMaxSampleCount().first, true); EXPECT_EQ(d.getMaxSampleCount().first, true); + EXPECT_EQ(e.getMaxSampleCount().first, true); EXPECT_EQ(a.getMaxSampleCount().second, 20); EXPECT_EQ(b.getMaxSampleCount().second, 20); EXPECT_EQ(c.getMaxSampleCount().second, 20); EXPECT_EQ(d.getMaxSampleCount().second, 20); + EXPECT_EQ(e.getMaxSampleCount().second, 20); // change limit to time duration ASSERT_NO_THROW(a.setMaxSampleAge(dur453)); ASSERT_NO_THROW(b.setMaxSampleAge(dur453)); ASSERT_NO_THROW(c.setMaxSampleAge(dur453)); ASSERT_NO_THROW(d.setMaxSampleAge(dur453)); + ASSERT_NO_THROW(e.setMaxSampleAge(dur453)); EXPECT_EQ(a.getMaxSampleAge().first, true); EXPECT_EQ(b.getMaxSampleAge().first, true); EXPECT_EQ(c.getMaxSampleAge().first, true); EXPECT_EQ(d.getMaxSampleAge().first, true); + EXPECT_EQ(e.getMaxSampleAge().first, true); EXPECT_EQ(a.getMaxSampleAge().second, dur453); EXPECT_EQ(b.getMaxSampleAge().second, dur453); EXPECT_EQ(c.getMaxSampleAge().second, dur453); EXPECT_EQ(d.getMaxSampleAge().second, dur453); + EXPECT_EQ(e.getMaxSampleAge().second, dur453); EXPECT_EQ(a.getMaxSampleCount().first, false); EXPECT_EQ(b.getMaxSampleCount().first, false); EXPECT_EQ(c.getMaxSampleCount().first, false); EXPECT_EQ(d.getMaxSampleCount().first, false); + EXPECT_EQ(e.getMaxSampleCount().first, false); EXPECT_EQ(a.getMaxSampleCount().second, 20); EXPECT_EQ(b.getMaxSampleCount().second, 20); EXPECT_EQ(c.getMaxSampleCount().second, 20); EXPECT_EQ(d.getMaxSampleCount().second, 20); + EXPECT_EQ(e.getMaxSampleCount().second, 20); } // limit defaults are tested with StatsMgr. @@ -525,7 +606,6 @@ TEST_F(ObservationTest, integerToJSON) { std::string exp = "[ [ 1234, \"" + isc::util::clockToText(a.getInteger().second) + "\" ]" + first_sample; - std::cout << a.getJSON()->str() << std::endl; EXPECT_EQ(exp, a.getJSON()->str()); } @@ -546,7 +626,6 @@ TEST_F(ObservationTest, floatToJSON) { std::string exp = "[ [ 1234.5, \"" + isc::util::clockToText(b.getFloat().second) + "\" ]" + first_sample; - std::cout << b.getJSON()->str() << std::endl; EXPECT_EQ(exp, b.getJSON()->str()); } @@ -564,7 +643,6 @@ TEST_F(ObservationTest, durationToJSON) { std::string exp = "[ [ \"01:02:03.004000\", \"" + isc::util::clockToText(c.getDuration().second) + "\" ]" + first_sample; - std::cout << c.getJSON()->str() << std::endl; EXPECT_EQ(exp, c.getJSON()->str()); } @@ -581,31 +659,50 @@ TEST_F(ObservationTest, stringToJSON) { std::string exp = "[ [ \"Lorem ipsum dolor sit amet\", \"" + isc::util::clockToText(d.getString().second) + "\" ]" + first_sample; - std::cout << d.getJSON()->str() << std::endl; EXPECT_EQ(exp, d.getJSON()->str()); } +// Checks whether a big integer statistic can generate proper JSON structures. +// See https://gitlab.isc.org/isc-projects/kea/wikis/designs/Stats-design +// for details. +TEST_F(ObservationTest, bigIntegerToJSON) { + // String which contains first added sample + std::string first_sample = ", [ 120000000000000007304085773727301632, \"" + + isc::util::clockToText(e.getBigInteger().second) + "\" ] ]"; + + e.setValue(int128_t(43e21)); + + std::string exp = "[ [ 43000000000000002097152, \"" + + isc::util::clockToText(e.getBigInteger().second) + "\" ]" + first_sample; + + EXPECT_EQ(exp, e.getJSON()->str()); +} + // Checks whether reset() resets the statistics properly. TEST_F(ObservationTest, reset) { EXPECT_NO_THROW(a.addValue(static_cast<int64_t>(5678))); EXPECT_NO_THROW(b.addValue(56.78)); EXPECT_NO_THROW(c.addValue(dur5678)); EXPECT_NO_THROW(d.addValue("fiveSixSevenEight")); + EXPECT_NO_THROW(e.addValue(int128_t(43e21))); a.reset(); // integer b.reset(); // float c.reset(); // duration d.reset(); // string + e.reset(); // big integer EXPECT_EQ(0, a.getInteger().first); EXPECT_EQ(0.0, b.getFloat().first); EXPECT_EQ(StatsDuration::zero(), c.getDuration().first); EXPECT_EQ("", d.getString().first); + EXPECT_EQ(0, e.getBigInteger().first); ASSERT_EQ(a.getSize(), 1); ASSERT_EQ(b.getSize(), 1); ASSERT_EQ(c.getSize(), 1); ASSERT_EQ(d.getSize(), 1); + ASSERT_EQ(e.getSize(), 1); } // Checks whether an observation can keep its name. @@ -614,6 +711,7 @@ TEST_F(ObservationTest, names) { EXPECT_EQ("beta", b.getName()); EXPECT_EQ("gamma", c.getName()); EXPECT_EQ("delta", d.getName()); + EXPECT_EQ("epsilon", e.getName()); } -} +} // namespace diff --git a/src/lib/stats/testutils/stats_test_utils.h b/src/lib/stats/testutils/stats_test_utils.h index 808dced39a..cad71a35d1 100644 --- a/src/lib/stats/testutils/stats_test_utils.h +++ b/src/lib/stats/testutils/stats_test_utils.h @@ -27,12 +27,21 @@ typedef std::map<std::string, int64_t> StatMap; /// /// @param name StatsMgr name for the statistic to check. /// @param expected_value expected value of the statistic. -inline void checkStat(const std::string& name, const int64_t expected_value) { - using namespace isc::stats; - ObservationPtr obs = StatsMgr::instance().getObservation(name); - ASSERT_TRUE(obs) << " stat: " << name << " not found "; - ASSERT_EQ(expected_value, obs->getInteger().first) - << " stat: " << name << " value wrong"; +inline void checkStat(const std::string& name, + const isc::util::int128_t expected_value) { + ObservationPtr const obs(isc::stats::StatsMgr::instance().getObservation(name)); + + ASSERT_TRUE(obs) << "stat " << name << " not found "; + if (obs->getType() == Observation::STAT_INTEGER) { + EXPECT_EQ(expected_value, obs->getInteger().first) + << " wrong value for stat " << name; + } else if (obs->getType() == Observation::STAT_BIG_INTEGER) { + EXPECT_EQ(expected_value, obs->getBigInteger().first) + << " wrong value for stat " << name; + } else { + GTEST_FAIL() << "unexpected statistic type: " + << Observation::typeToText(obs->getType()); + } } /// @brief Check if a statistic does not exists. |