]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3253] Added report due time finders
authorThomas Markwalder <tmark@isc.org>
Tue, 5 Mar 2024 16:14:15 +0000 (11:14 -0500)
committerThomas Markwalder <tmark@isc.org>
Mon, 18 Mar 2024 19:12:14 +0000 (19:12 +0000)
src/hooks/dhcp/perfmon/monitored_duration.*
    MonitoredDuration::getCurrentIntervalStart()
    MonitoredDuration::expireCurrentInterval() - new functions

src/hooks/dhcp/perfmon/monitored_duration_store.*
    MonitoredDurationStore::getReportsNext()
    MonitoredDurationStore::getOverdueReports() - new functions

src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc
    TEST_F(MonitoredDurationStoreTest, reportDue*) - new tests

src/hooks/dhcp/perfmon/tests/monitored_duration_unittests.cc
    TEST(MonitoredDuration, expireInterval) - new test

src/lib/dhcp/pkt.h
    static boost::posix_time::ptime& MIN_TIME()
    static boost::posix_time::ptime& MAX_TIME() - new functions

src/hooks/dhcp/perfmon/monitored_duration.cc
src/hooks/dhcp/perfmon/monitored_duration.h
src/hooks/dhcp/perfmon/monitored_duration_store.cc
src/hooks/dhcp/perfmon/monitored_duration_store.h
src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc
src/hooks/dhcp/perfmon/tests/monitored_duration_unittests.cc
src/lib/dhcp/pkt.h

index acd274fe44ae5bdb20cd70704e34a2290756b4f0..4f436bff887b658b34d8a618343297f020e068cb 100644 (file)
@@ -23,10 +23,7 @@ namespace perfmon {
 DurationDataInterval::DurationDataInterval(const Timestamp& start_time /* = PktEvent::now()*/)
     : start_time_(start_time), occurrences_(0),
       min_duration_(pos_infin), max_duration_(neg_infin),
-      total_duration_(microseconds(0)) {
-}
-
-void
+      total_duration_(microseconds(0)) { } void
 DurationDataInterval::addDuration(const Duration& duration) {
     ++occurrences_;
     if (duration < min_duration_) {
@@ -248,6 +245,12 @@ MonitoredDuration::MonitoredDuration(const MonitoredDuration& rhs)
     }
 }
 
+Timestamp
+MonitoredDuration::getCurrentIntervalStart() const {
+    return (current_interval_ ? current_interval_->getStartTime()
+                              : dhcp::PktEvent::MIN_TIME());
+}
+
 bool
 MonitoredDuration::addSample(const Duration& sample) {
     auto now = PktEvent::now();
@@ -264,6 +267,17 @@ MonitoredDuration::addSample(const Duration& sample) {
     return (do_report);
 }
 
+void
+MonitoredDuration::expireCurrentInterval() {
+    if (!current_interval_) {
+        isc_throw(InvalidOperation, "MonitoredDuration::expireInterval"
+                                    " - no current interval for: " << getLabel());
+    }
+
+    previous_interval_ = current_interval_;
+    current_interval_.reset();
+}
+
 void
 MonitoredDuration::clear() {
     current_interval_.reset();
index c784bb613769db25a5f45f86c55ee623ce759dba..314482b468b748bacb89deae2694972bdfce9aa6 100644 (file)
@@ -313,13 +313,11 @@ public:
         return (current_interval_);
     }
 
-    /// @brief Get the current interval.
+    /// @brief Get the current interval start time.
     ///
-    /// @return Pointer to the current interval if it exists or an empty pointer.
-    Timestamp getCurrentIntervalStart() const {
-        return (current_interval_ ? current_interval_->getStartTime()
-                                  : dhcp::PktEvent::EMPTY_TIME());
-    }
+    /// @return Current interval's start time or MIN_TIME if there's no
+    /// current interval.
+    Timestamp getCurrentIntervalStart() const;
 
     /// @brief Add a sample to the duration's current interval.
     ///
@@ -333,6 +331,13 @@ public:
     /// @return True if there is a newly completed (i.e. previous) interval to report.
     bool addSample(const Duration& sample);
 
+    /// @brief Concludes the current interval
+    ///
+    /// Rotates current interval to previous and resets curent interval to empty.
+    ///
+    /// @throw InvalidOperation if there is no current interval.
+    void expireCurrentInterval();
+
     /// @brief Deletes the current and previous intervals.
     void clear();
 
index ce2e006c7b614d4d4895c7d762a0134efe8358d4..3827da7519de9cc0de9d556148993c502d3645e4 100644 (file)
@@ -11,7 +11,9 @@
 #include <util/multi_threading_mgr.h>
 
 using namespace isc;
+using namespace isc::dhcp;
 using namespace isc::util;
+using namespace boost::posix_time;
 
 namespace isc {
 namespace perfmon {
@@ -177,6 +179,36 @@ MonitoredDurationStore::clear() {
     durations_.clear();
 }
 
+MonitoredDurationPtr
+MonitoredDurationStore::getReportsNext() {
+    MultiThreadingLock lock(*mutex_);
+    const auto& index = durations_.get<IntervalStartTag>();
+    // We want to find the oldest interval that is less than interval_duration in the past.
+    auto duration_iter = index.lower_bound(dhcp::PktEvent::now() - interval_duration_);
+    return (duration_iter == index.end() ? MonitoredDurationPtr()
+            : MonitoredDurationPtr(new MonitoredDuration(**duration_iter)));
+}
+
+MonitoredDurationCollectionPtr
+MonitoredDurationStore::getOverdueReports(const Timestamp& since /* = PktEvent::now() */) {
+    MultiThreadingLock lock(*mutex_);
+    // We use a lower bound of MIN + 1us to avoid dormant durations
+    static Timestamp lower_limit_time(PktEvent::MIN_TIME() + microseconds(1));
+
+    // We want to find anything since the start of time that who's start time
+    // is more than interval_duration_ in the past.
+    const auto& index = durations_.get<IntervalStartTag>();
+    auto lower_limit = index.lower_bound(lower_limit_time);
+    auto upper_limit = index.upper_bound(since - interval_duration_);
+
+    MonitoredDurationCollectionPtr collection(new MonitoredDurationCollection());
+    for (auto duration_iter = lower_limit; duration_iter != upper_limit; ++duration_iter) {
+        collection->push_back(MonitoredDurationPtr(new MonitoredDuration(**duration_iter)));
+    }
+
+    return (collection);
+}
+
 } // end of namespace perfmon
 } // end of namespace isc
 
index e4f7536a9ec912167cfbc3d9a74ec478bd79effa..6bb24e6c46d9510bb6f5eae1221afe5f71e22095 100644 (file)
@@ -149,6 +149,21 @@ public:
     /// @return a collection of all durations in the store.
     MonitoredDurationCollectionPtr getAll();
 
+    /// @brief Fetches the duration which is due to report next.
+    ///
+    /// @return pointer to the matching duration or an empty pointer if
+    /// not found.
+    MonitoredDurationPtr getReportsNext();
+
+    /// @brief Fetches all durations that are overdue to report.
+    ///
+    /// @param since timestamp to search by. Defaults to current time.
+    ///
+    /// @return a collection of the matching durations, ordered by current interval
+    /// start time.
+    MonitoredDurationCollectionPtr getOverdueReports(const Timestamp& since
+                                                     = dhcp::PktEvent::now());
+
     /// @brief Removes all durations from the store.
     void clear();
 
index 6182a07627275184993e48359e273db2efde357e..1dea08b6f79b908e8eb3d1b0c3a557d317f3f1c4 100644 (file)
@@ -392,6 +392,93 @@ public:
         EXPECT_EQ(previous_interval->getTotalDuration(), (five_ms) * 2);
     }
 
+    /// @brief Veriries getReportsNext and getOverdueReports
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    void reportDueTest(uint16_t family) {
+        // Create a store.
+        Duration interval_duration(milliseconds(100));
+        MonitoredDurationStore store(family, interval_duration);
+
+        // Create durations in the store, none of them will have intervals.
+        size_t num_subnets = 4;
+        std::vector<DurationKeyPtr> keys;
+        for (int s = 0; s < num_subnets; ++s) {
+            auto key = makeKey(family, s);
+            store.addDuration(key);
+            keys.push_back(key);
+        }
+
+        // No duration should be due to report.
+        MonitoredDurationPtr mond;
+        ASSERT_NO_THROW_LOG(mond = store.getReportsNext());
+        ASSERT_FALSE(mond);
+
+        // No durations should be over due to report.
+        MonitoredDurationCollectionPtr durations;
+        ASSERT_NO_THROW_LOG(durations = store.getOverdueReports());
+        ASSERT_TRUE(durations);
+        EXPECT_TRUE(durations->empty());
+
+        // Now add a 5ms sample to all four durations in reverse order
+        // This should give us a list of durations (by key) as follows:
+        //
+        // key[0] - interval start = now + 54ms
+        // key[1] - interval start = now + 52ms
+        // key[2] - interval start = now + 2ms
+        // key[3] - interval start = now
+        auto five_ms(milliseconds(5));
+        for (auto k = keys.rbegin(); k != keys.rend();  ++k ) {
+            ASSERT_NO_THROW_LOG(store.addDurationSample(*k, five_ms));
+            if ((*k)->getSubnetId() != 2) {
+                usleep(2 * 1000);   // put 2ms gap between them
+            } else {
+                usleep(50 * 1000);  // put 50ms gap between them.
+            }
+        }
+
+        // Key[3] should be returned by getReportsNext().
+        ASSERT_NO_THROW_LOG(mond = store.getReportsNext());
+        ASSERT_TRUE(mond);
+        EXPECT_EQ(*mond, *keys[3]) << "mond: " << mond->getLabel();
+
+        // None should be returned by getOverdueReports().
+        ASSERT_NO_THROW_LOG(durations = store.getOverdueReports());
+        ASSERT_TRUE(durations);
+        EXPECT_TRUE(durations->empty());
+
+        // Sleep for 50 ms.
+        usleep(50 * 1000);
+
+        // Key[1] should be returned by getReportsNext().
+        ASSERT_NO_THROW_LOG(mond = store.getReportsNext());
+        ASSERT_TRUE(mond);
+        EXPECT_EQ(*mond, *keys[1]) << "mond: " << mond->getLabel();
+
+        // Key[3] and key[2] should be returned by getOverdueReports().
+        ASSERT_NO_THROW_LOG(durations = store.getOverdueReports());
+        ASSERT_TRUE(durations);
+        EXPECT_EQ(durations->size(), 2);
+        EXPECT_EQ(*(*durations)[0], *keys[3]);
+        EXPECT_EQ(*(*durations)[1], *keys[2]);
+
+        // Sleep for 50 ms.
+        usleep(50 * 1000);
+
+        // None should report as reports next.
+        ASSERT_NO_THROW_LOG(mond = store.getReportsNext());
+        ASSERT_FALSE(mond);
+
+        // All should be found overdue.
+        ASSERT_NO_THROW_LOG(durations = store.getOverdueReports());
+        ASSERT_TRUE(durations);
+        EXPECT_EQ(durations->size(), keys.size());
+        EXPECT_EQ(*(*durations)[0], *keys[3]);
+        EXPECT_EQ(*(*durations)[1], *keys[2]);
+        EXPECT_EQ(*(*durations)[2], *keys[1]);
+        EXPECT_EQ(*(*durations)[3], *keys[0]);
+    }
+
     /// @brief Test tool for gauging speed.
     ///
     /// This test is really just a development tool for gauging performance.
@@ -562,4 +649,22 @@ TEST_F(MonitoredDurationStoreTest, addDurationSample6MultiThreading) {
     addDurationSampleTest(AF_INET6);
 }
 
+TEST_F(MonitoredDurationStoreTest, reportDue) {
+    reportDueTest(AF_INET);
+}
+
+TEST_F(MonitoredDurationStoreTest, reportDueMultiThreading) {
+    MultiThreadingTest mt;
+    reportDueTest(AF_INET);
+}
+
+TEST_F(MonitoredDurationStoreTest, reportDue6) {
+    reportDueTest(AF_INET);
+}
+
+TEST_F(MonitoredDurationStoreTest, reportDue6MultiThreading) {
+    MultiThreadingTest mt;
+    reportDueTest(AF_INET6);
+}
+
 } // end of anonymous namespace
index 4472902b1b4f4ac634c2a0b4e209944500cc2a45..cae425df63054d0b1f6b68c1b6419dafaa576a33 100644 (file)
@@ -481,4 +481,42 @@ TEST(MonitoredDuration, addSampleAndClear) {
     EXPECT_FALSE(mond->getPreviousInterval());
 }
 
+TEST(MonitoredDuration, expireInterval) {
+    MonitoredDurationPtr mond;
+    Duration interval_duration(milliseconds(50));
+    auto start_time = PktEvent::now();
+    auto ten_ms = milliseconds(10);
+
+    // Create valid v4 duration with interval duration of 50ms.
+    ASSERT_NO_THROW_LOG(mond.reset(new MonitoredDuration(AF_INET, DHCPDISCOVER, DHCPOFFER,
+                                                         "process_started", "process_completed",
+                                                         SUBNET_ID_GLOBAL, interval_duration)));
+    ASSERT_TRUE(mond);
+
+    // Initially there are no intervals.
+    EXPECT_FALSE(mond->getCurrentInterval());
+    EXPECT_FALSE(mond->getPreviousInterval());
+    EXPECT_EQ(mond->getCurrentIntervalStart(), PktEvent::MIN_TIME());
+
+    ASSERT_THROW_MSG(mond->expireCurrentInterval(), InvalidOperation,
+                     "MonitoredDuration::expireInterval - no current interval for:"
+                     " DHCPDISCOVER-DHCPOFFER.process_started-process_completed.0");
+
+    ASSERT_NO_THROW(mond->addSample(ten_ms));
+    auto current_interval = mond->getCurrentInterval();
+    ASSERT_TRUE(current_interval);
+    EXPECT_EQ(current_interval->getOccurrences(), 1);
+    EXPECT_EQ(current_interval->getTotalDuration(), ten_ms);
+    EXPECT_FALSE(mond->getPreviousInterval());
+    EXPECT_GE(mond->getCurrentIntervalStart(), start_time);
+    EXPECT_EQ(mond->getCurrentIntervalStart(), current_interval->getStartTime());
+
+    ASSERT_NO_THROW_LOG(mond->expireCurrentInterval());
+    EXPECT_FALSE(mond->getCurrentInterval());
+
+    auto previous_interval = mond->getPreviousInterval();
+    EXPECT_EQ(previous_interval, current_interval);
+    EXPECT_EQ(mond->getCurrentIntervalStart(), PktEvent::MIN_TIME());
+}
+
 } // end of anonymous namespace
index 86df3f97ddf7506bee2d6363674505f5b16bc83c..f8a675942ebfff15268731f04558fa61f2938851 100644 (file)
@@ -121,11 +121,27 @@ public:
     /// @brief Fetch an empty timestamp, used for logic comparisons
     ///
     /// @return an unset ptime.
-    static boost::posix_time::ptime EMPTY_TIME() {
+    static boost::posix_time::ptime& EMPTY_TIME() {
         static boost::posix_time::ptime empty_time;
         return (empty_time);
     }
 
+    /// @brief Fetches the minimum timestamp
+    ///
+    /// @return the minimum timestamp
+    static boost::posix_time::ptime& MIN_TIME() {
+        static auto min_time = boost::posix_time::ptime(boost::posix_time::min_date_time);
+        return (min_time);
+    }
+
+    /// @brief Fetches the maximum timestamp
+    ///
+    /// @return the maximum timestamp
+    static boost::posix_time::ptime& MAX_TIME() {
+        static auto max_time = boost::posix_time::ptime(boost::posix_time::max_date_time);
+        return (max_time);
+    }
+
     /// @brief Label identifying this event.
     std::string label_;