]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3253] Added In-place alarm checking
authorThomas Markwalder <tmark@isc.org>
Wed, 28 Feb 2024 21:00:19 +0000 (16:00 -0500)
committerThomas Markwalder <tmark@isc.org>
Mon, 18 Mar 2024 19:12:14 +0000 (19:12 +0000)
src/hooks/dhcp/perfmon/alarm_store.*
    AlarmStore::checkDurationSample() - new function to check a sample
    against an alarm in-place

src/hooks/dhcp/perfmon/tests/alarm_store_unittests.cc
    checkDurationSampleTest(uint16_t family)  - new test function and tests

src/hooks/dhcp/perfmon/alarm_store.cc
src/hooks/dhcp/perfmon/alarm_store.h
src/hooks/dhcp/perfmon/tests/alarm_store_unittests.cc
src/hooks/dhcp/perfmon/tests/monitored_duration_store_unittests.cc

index 582daf8b018cd16dc43fb0463e8711ac7583f820..563d1e351fae2e8ecc565e786fc10bb406af0292 100644 (file)
@@ -39,6 +39,27 @@ AlarmStore::validateKey(const std::string& label, DurationKeyPtr key) const {
     }
 }
 
+AlarmPtr
+AlarmStore::checkDurationSample(DurationKeyPtr key, const Duration& sample,
+                                const Duration& report_interval) {
+    validateKey("checkDurationSample", key);
+
+    MultiThreadingLock lock(*mutex_);
+    auto& index = alarms_.get<AlarmPrimaryKeyTag>();
+    auto alarm_iter = index.find(*key);
+
+    // If we find an alarm the check the sample.  Alarm::checkSample()
+    //does not alter the key so it can be done in-place.
+    if (alarm_iter != index.end() &&
+        (*alarm_iter)->checkSample(sample, report_interval)) {
+        // Alarm state needs to be reported, return a copy of the alarm.
+        return (AlarmPtr(new Alarm(**alarm_iter)));
+    }
+
+    // Nothing to alarm.
+    return(AlarmPtr());
+}
+
 AlarmPtr
 AlarmStore::addAlarm(AlarmPtr alarm) {
     {
index 35a6a3ef617f196bdba4179797e3068e7ef8664a..46fa1e109fff90a82b8b0ae8793fa9bf1ddcd75a 100644 (file)
@@ -81,6 +81,27 @@ public:
     /// @brief Destructor
     ~AlarmStore() = default;
 
+    /// @brief Checks a sample against an alarm.
+    ///
+    /// If the alarm exists in the store for the duration key, then Alarm::checkSample()
+    /// is invoked on the in-store alarm.  If this returns true, indicating an alarm
+    /// state change, then a copy of the in-store alarm is returned, otherwise an empty
+    /// pointer is returned.
+    ///
+    /// If no alarm exists in the store, then it simply returns an empty pointer.
+    ///
+    /// This function does not/must not modify any index keys.
+    ///
+    /// @param key key value of the alarm to check.
+    /// @param sample duration value to check.
+    /// @param report_interval amount of time that must elapse between high
+    /// water reports.
+    ///
+    /// @return A copy of the updated alarm if it should be reported, an empty
+    /// pointer otherwise.
+    AlarmPtr checkDurationSample(DurationKeyPtr key, const Duration& sample,
+                                 const Duration& report_interval);
+
     /// @brief Creates a new Alarm and adds it to the store
     ///
     /// @param key key value of the Alarm to create.
index 7d22486db3ef305bfd92751b872613bb0b718cee..5564e26b9e8b0ee684717b0d04e5740c2dc84a3e 100644 (file)
@@ -247,7 +247,6 @@ public:
                          " - family mismatch, key is v4, store is v6");
     }
 
-
     /// @brief Verify that alarms in the store can be updated.
     ///
     /// @param family protocol family to test, AF_INET or AF_INET6
@@ -256,9 +255,9 @@ public:
 
         // Add the duration to the store.
         AlarmPtr alarm;
-        ASSERT_NO_THROW(alarm.reset(new Alarm(*makeKey(family), milliseconds(10),
+        ASSERT_NO_THROW_LOG(alarm.reset(new Alarm(*makeKey(family), milliseconds(10),
                                               milliseconds(250))));
-        ASSERT_NO_THROW(store.addAlarm(alarm));
+        ASSERT_NO_THROW_LOG(store.addAlarm(alarm));
 
         // Fetch it.
         AlarmPtr found;
@@ -272,7 +271,7 @@ public:
         // Now change the thresholds and update it.
         alarm->setLowWater(milliseconds(125));
         alarm->setHighWater(milliseconds(500));
-        ASSERT_NO_THROW(store.updateAlarm(alarm));
+        ASSERT_NO_THROW_LOG(store.updateAlarm(alarm));
 
         // Fetch it again.
         ASSERT_NO_THROW_LOG(found = store.getAlarm(alarm));
@@ -325,6 +324,70 @@ public:
                          "AlarmStore::updateAlarm"
                          " - family mismatch, key is v4, store is v6");
     }
+
+    /// @brief Verify checkDurationSample() valid behavior.
+    ///
+    /// @param family protocol family to test, AF_INET or AF_INET6
+    void checkDurationSampleTest(uint16_t family) {
+        AlarmStore store(family);
+
+        Duration under_low_water(milliseconds(50));
+        Duration low_water(milliseconds(100));
+        Duration mid_range(milliseconds(175));
+        Duration high_water(milliseconds(250));
+        Duration over_high_water(milliseconds(300));
+        Duration report_interval(milliseconds(10));
+
+        DurationKeyPtr key(makeKey(family));
+        AlarmPtr reportable;
+        ASSERT_NO_THROW_LOG(reportable = store.checkDurationSample(key, over_high_water,
+                                                                   report_interval));
+        ASSERT_FALSE(reportable);
+
+        // Add an alarm for the key to the store.
+        AlarmPtr alarm;
+        ASSERT_NO_THROW_LOG(alarm.reset(new Alarm(*key, low_water, high_water)));
+        ASSERT_NO_THROW_LOG(store.addAlarm(alarm));
+
+        // Fetch it.
+        AlarmPtr found;
+        ASSERT_NO_THROW_LOG(found = store.getAlarm(alarm));
+        ASSERT_TRUE(found);
+
+        // Check a sample at mid range. Should not return the alarm.
+        ASSERT_NO_THROW_LOG(reportable = store.checkDurationSample(key, mid_range,
+                                                                   report_interval));
+        ASSERT_FALSE(reportable);
+
+        // Check a sample over high water.  Should return the alarm.
+        ASSERT_NO_THROW_LOG(reportable = store.checkDurationSample(key, over_high_water,
+                                                                   report_interval));
+        ASSERT_TRUE(reportable);
+        EXPECT_EQ(reportable->getState(), Alarm::TRIGGERED);
+
+        // Check a sample over high water but before report interval elapses.
+        // Should not return the alarm.
+        ASSERT_NO_THROW_LOG(reportable = store.checkDurationSample(key, over_high_water,
+                                                                   report_interval));
+        ASSERT_FALSE(reportable);
+
+        // Sleep beyond the report interval.
+        usleep(15 * 1000);
+
+        // Check a sample over high water after report interval elapses.
+        // Should return the alarm.
+        ASSERT_NO_THROW_LOG(reportable = store.checkDurationSample(key, over_high_water,
+                                                                   report_interval));
+        ASSERT_TRUE(reportable);
+        EXPECT_EQ(reportable->getState(), Alarm::TRIGGERED);
+
+        // Check a sample below low water.
+        // Should return the alarm.
+        ASSERT_NO_THROW_LOG(reportable = store.checkDurationSample(key, under_low_water,
+                                                                   report_interval));
+        ASSERT_TRUE(reportable);
+        EXPECT_EQ(reportable->getState(), Alarm::CLEAR);
+    }
 };
 
 TEST_F(AlarmStoreTest, addAlarm) {
@@ -426,5 +489,22 @@ TEST_F(AlarmStoreTest, updateAlarmInvalidMultiThreading) {
     updateAlarmInvalidTest();
 }
 
+TEST_F(AlarmStoreTest, checkDurationSample) {
+    checkDurationSampleTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, checkDurationSampleMultiThreading) {
+    MultiThreadingTest mt;
+    checkDurationSampleTest(AF_INET);
+}
+
+TEST_F(AlarmStoreTest, checkDurationSample6) {
+    checkDurationSampleTest(AF_INET6);
+}
+
+TEST_F(AlarmStoreTest, checkDurationSample6MultiThreading) {
+    MultiThreadingTest mt;
+    checkDurationSampleTest(AF_INET6);
+}
 
 } // end of anonymous namespace
index 338fe01e40cd9918bce564d35eec3100eba21d3f..038d71120d118dad89162115380a46fcb43f76d6 100644 (file)
@@ -392,6 +392,7 @@ public:
         EXPECT_EQ(previous_interval->getTotalDuration(), (five_ms) * 2);
     }
 
+    /// @todo TAKE THIS OUT
     /// @brief Test tool for gauging speed.
     ///
     /// This test is really just a development tool for gauging performance.
@@ -410,6 +411,15 @@ public:
         for (int s = 0; s < num_subnets; ++s) {
             keys.push_back(makeKey(family, s));
         }
+
+        auto start_time = PktEvent::now();
+
+        for (auto k : keys) {
+            store.addDuration(k);
+        }
+
+        auto add_keys_time = PktEvent::now();
+
         size_t num_passes = 100;
         size_t report_count = 0;
         Duration two_us(microseconds(2));
@@ -421,10 +431,16 @@ public:
             }
         }
 
-        std::cout << "report count: " << report_count << std::endl;
+        auto add_samples_time = PktEvent::now();
+
+        EXPECT_GT(report_count, 0);
         auto durations = store.getAll();
-        EXPECT_EQ(durations->size(), 100);
+        EXPECT_EQ(durations->size(), num_subnets);
 
+        std::cout << "add keys time   : " << (add_keys_time - start_time) << std::endl
+                  << "add samples time: " << (add_samples_time - add_keys_time) << std::endl
+                  << "time per sample: "
+                  << (add_samples_time - add_keys_time) / (num_subnets * num_passes) << std::endl;
     }
 };
 
@@ -545,6 +561,7 @@ TEST_F(MonitoredDurationStoreTest, addDurationSample6MultiThreading) {
     addDurationSampleTest(AF_INET6);
 }
 
+/// @todo TAKE THESE OUT
 TEST_F(MonitoredDurationStoreTest, speedCheck) {
     speedCheckTest(AF_INET);
 }
@@ -563,5 +580,4 @@ TEST_F(MonitoredDurationStoreTest, speedCheck6MultiThreading) {
     speedCheckTest(AF_INET6);
 }
 
-
 } // end of anonymous namespace