]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1959] Counter for unsent lease updates
authorMarcin Siodelski <marcin@isc.org>
Tue, 27 Jul 2021 17:57:13 +0000 (19:57 +0200)
committerMarcin Siodelski <marcin@isc.org>
Wed, 22 Sep 2021 06:09:39 +0000 (08:09 +0200)
src/hooks/dhcp/high_availability/communication_state.cc
src/hooks/dhcp/high_availability/communication_state.h
src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc
src/hooks/dhcp/high_availability/tests/ha_test.h

index 242fe7729a0b82254c02d8ab589d0a3b5ffee5ce..c7e28371e9d3fb5626f5e2c7c8d24c50fc44f6a6 100644 (file)
@@ -23,6 +23,7 @@
 #include <boost/pointer_cast.hpp>
 
 #include <functional>
+#include <limits>
 #include <sstream>
 #include <utility>
 
@@ -59,7 +60,8 @@ CommunicationState::CommunicationState(const IOServicePtr& io_service,
       heartbeat_impl_(0), partner_state_(-1), partner_scopes_(),
       clock_skew_(0, 0, 0, 0), last_clock_skew_warn_(),
       my_time_at_skew_(), partner_time_at_skew_(),
-      analyzed_messages_count_(0), mutex_(new mutex()) {
+      analyzed_messages_count_(0), unsent_update_count_(0),
+      partner_unsent_update_count_{0, 0}, mutex_(new mutex()) {
 }
 
 CommunicationState::~CommunicationState() {
@@ -471,6 +473,69 @@ CommunicationState::getReport() const {
     return (report);
 }
 
+uint64_t
+CommunicationState::getUnsentUpdateCount() const {
+    if (MultiThreadingMgr::instance().getMode()) {
+        std::lock_guard<std::mutex> lk(*mutex_);
+        return (unsent_update_count_);
+    } else {
+        return (unsent_update_count_);
+    }
+}
+
+void
+CommunicationState::increaseUnsentUpdateCount() {
+    if (MultiThreadingMgr::instance().getMode()) {
+        std::lock_guard<std::mutex> lk(*mutex_);
+        increaseUnsentUpdateCountInternal();
+    } else {
+        increaseUnsentUpdateCountInternal();
+    }
+}
+
+void
+CommunicationState::increaseUnsentUpdateCountInternal() {
+    // Protect against setting the incremented value to zero.
+    // The zero value is reserved for a server startup.
+    if (unsent_update_count_ < std::numeric_limits<uint64_t>::max()) {
+        ++unsent_update_count_;
+    } else {
+        unsent_update_count_ = 1;
+    }
+}
+
+bool
+CommunicationState::hasPartnerNewUnsentUpdates() const {
+    if (MultiThreadingMgr::instance().getMode()) {
+        std::lock_guard<std::mutex> lk(*mutex_);
+        return (hasPartnerNewUnsentUpdatesInternal());
+    } else {
+        return (hasPartnerNewUnsentUpdatesInternal());
+    }
+}
+
+bool
+CommunicationState::hasPartnerNewUnsentUpdatesInternal() const {
+    return (partner_unsent_update_count_.second > 0 &&
+            (partner_unsent_update_count_.first != partner_unsent_update_count_.second));
+}
+
+void
+CommunicationState::setPartnerUnsentUpdateCount(uint64_t unsent_update_count) {
+    if (MultiThreadingMgr::instance().getMode()) {
+        std::lock_guard<std::mutex> lk(*mutex_);
+        setPartnerUnsentUpdateCountInternal(unsent_update_count);
+    } else {
+        setPartnerUnsentUpdateCountInternal(unsent_update_count);
+    }
+}
+
+void
+CommunicationState::setPartnerUnsentUpdateCountInternal(uint64_t unsent_update_count) {
+    partner_unsent_update_count_.first = partner_unsent_update_count_.second;
+    partner_unsent_update_count_.second = unsent_update_count;
+}
+
 CommunicationState4::CommunicationState4(const IOServicePtr& io_service,
                                          const HAConfigPtr& config)
     : CommunicationState(io_service, config), connecting_clients_() {
index 3d2ddc8a6d128d03df63b7d0b448db25491d137f..2eb66e384e6a748d19bfe2267f860d7ed961b6f6 100644 (file)
@@ -29,6 +29,7 @@
 #include <mutex>
 #include <set>
 #include <string>
+#include <utility>
 
 namespace isc {
 namespace ha {
@@ -459,6 +460,66 @@ private:
     /// @return The time elapsed.
     boost::posix_time::time_duration updatePokeTimeInternal();
 
+public:
+
+    /// @brief Returns a total number of unsent lease updates.
+    uint64_t getUnsentUpdateCount() const;
+
+    /// @brief Increases a total number of unsent lease updates by 1.
+    ///
+    /// This method should be called when the server has allocated a
+    /// lease but decided to not send the lease update to its partner.
+    /// If the server is in the partner-down state it allocates new
+    /// leases but doesn't send lease updates because the partner is
+    /// unavailable.
+    ///
+    /// This method protects against setting the value to 0 in an
+    /// unlikely event of the overflow. The zero is reserved for the
+    /// server startup case.
+    void increaseUnsentUpdateCount();
+
+private:
+
+    /// @brief Thread unsafe implementation of the @c increaseUnsentUpdateCount.
+    void increaseUnsentUpdateCountInternal();
+
+public:
+
+    /// @brief Checks if the partner allocated new leases for which it hasn't sent
+    /// any lease updates.
+    ///
+    /// It compares a previous and current value of the @c parent_unsent_update_count_.
+    /// If the current value is 0 and the previous value is non-zero it indicates
+    /// that the partner was restarted.
+    ///
+    /// @return true if the partner has allocated new leases for which it didn't
+    /// send lease updates, false otherwise.
+    bool hasPartnerNewUnsentUpdates() const;
+
+private:
+
+    /// @brief Thread unsafe implementation of the @c hasPartnerNewUnsentUpdates.
+    ///
+    /// @return true if the partner has allocated new leases for which it didn't
+    /// send lease updates, false otherwise.
+    bool hasPartnerNewUnsentUpdatesInternal() const;
+
+public:
+
+    /// @brief Saves new total number of unsent lease updates from the partner.
+    ///
+    /// @param unsent_updates_count new total number of unsent lease updates from
+    /// the partner.
+    void setPartnerUnsentUpdateCount(uint64_t unsent_update_count);
+
+private:
+
+    /// @brief Thread unsafe implementation of the @c setPartnerUnsentUpdateCount.
+    ///
+    /// @param unsent_updates_count new total number of unsent lease updates from
+    /// the partner.
+    void setPartnerUnsentUpdateCountInternal(uint64_t unsent_update_count);
+
 protected:
     /// @brief Pointer to the common IO service instance.
     asiolink::IOServicePtr io_service_;
@@ -502,6 +563,24 @@ protected:
     /// @brief Total number of analyzed messages to be responded by partner.
     size_t analyzed_messages_count_;
 
+    /// @brief Total number of unsent lease updates.
+    ///
+    /// The lease updates are not sent when the server is in the partner
+    /// down state. The server counts the number of lease updates which
+    /// haven't been sent to the partner because the partner was unavailable.
+    /// The partner receives this value in a response to a heartbeat message
+    /// and can use it to determine if it should synchronize its lease
+    /// database.
+    uint64_t unsent_update_count_;
+
+    /// @brief Previous and current total number of unsent lease updates
+    /// from the partner.
+    ///
+    /// This value is returned in response to a heartbeat command and saved
+    /// using the @c setPartnerUnsentUpdateCount. The previous value is
+    /// preserved so the values can be compared in the state handlers.
+    std::pair<uint64_t, uint64_t> partner_unsent_update_count_;
+
     /// @brief The mutex used to protect internal state.
     const boost::scoped_ptr<std::mutex> mutex_;
 };
index 12a8ff84b8cc3abdd3c3a19565afe7311d871299..ab65a5e28a5fc1a32ebb9cd45782000f8dff7bab 100644 (file)
@@ -19,7 +19,9 @@
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
+#include <limits>
 #include <functional>
+#include <limits>
 #include <sstream>
 
 using namespace isc;
@@ -103,6 +105,13 @@ public:
     /// @brief Tests unusual values used to create the report.
     void getReportDefaultValuesTest();
 
+    /// @brief Tests that unsent updates count can be incremented and fetched.
+    void getUnsentUpdateCountTest();
+
+    /// @brief Tests that unsent updates count from partner can be set and
+    /// a difference from previous value detected.
+    void hasPartnerNewUnsentUpdatesTest();
+
     /// @brief Returns test heartbeat implementation.
     ///
     /// @return Pointer to heartbeat implementation function under test.
@@ -636,6 +645,48 @@ CommunicationStateTest::getReportDefaultValuesTest() {
     EXPECT_TRUE(isEquivalent(Element::fromJSON(expected), report));
 }
 
+void
+CommunicationStateTest::getUnsentUpdateCountTest() {
+    // Initially the count should be 0.
+    EXPECT_EQ(0, state_.getUnsentUpdateCount());
+
+    // Increasing the value by 1 several times.
+    EXPECT_NO_THROW(state_.increaseUnsentUpdateCount());
+    EXPECT_EQ(1, state_.getUnsentUpdateCount());
+    EXPECT_NO_THROW(state_.increaseUnsentUpdateCount());
+    EXPECT_EQ(2, state_.getUnsentUpdateCount());
+    EXPECT_NO_THROW(state_.increaseUnsentUpdateCount());
+    EXPECT_EQ(3, state_.getUnsentUpdateCount());
+
+    // Test that the method under test protects against an overflow
+    // resetting the value to 0.
+    state_.unsent_update_count_ = std::numeric_limits<uint64_t>::max();
+    state_.increaseUnsentUpdateCount();
+    EXPECT_EQ(1, state_.getUnsentUpdateCount());
+}
+
+void
+CommunicationStateTest::hasPartnerNewUnsentUpdatesTest() {
+    // Initially the counts should be 0.
+    EXPECT_FALSE(state_.hasPartnerNewUnsentUpdates());
+
+    // Set a positive value. It should be noticed.
+    EXPECT_NO_THROW(state_.setPartnerUnsentUpdateCount(5));
+    EXPECT_TRUE(state_.hasPartnerNewUnsentUpdates());
+
+    // No change, no new unsent updates.
+    EXPECT_NO_THROW(state_.setPartnerUnsentUpdateCount(5));
+    EXPECT_FALSE(state_.hasPartnerNewUnsentUpdates());
+
+    // Change it again. New updates.
+    EXPECT_NO_THROW(state_.setPartnerUnsentUpdateCount(10));
+    EXPECT_TRUE(state_.hasPartnerNewUnsentUpdates());
+
+    // Set it to 0 to simulate restart. No updates.
+    EXPECT_NO_THROW(state_.setPartnerUnsentUpdateCount(0));
+    EXPECT_FALSE(state_.hasPartnerNewUnsentUpdates());
+}
+
 TEST_F(CommunicationStateTest, partnerStateTest) {
     partnerStateTest();
 }
@@ -762,4 +813,22 @@ TEST_F(CommunicationStateTest, getReportDefaultValuesTestMultiThreading) {
     getReportDefaultValuesTest();
 }
 
+TEST_F(CommunicationStateTest, getUnsentUpdateCountTest) {
+    getUnsentUpdateCountTest();
+}
+
+TEST_F(CommunicationStateTest, getUnsentUpdateCountTestMultiThreading) {
+    MultiThreadingMgr::instance().setMode(true);
+    getUnsentUpdateCountTest();
+}
+
+TEST_F(CommunicationStateTest, hasPartnerNewUnsentUpdatesTest) {
+    hasPartnerNewUnsentUpdatesTest();
+}
+
+TEST_F(CommunicationStateTest, hasPartnerNewUnsentUpdatesTestMultiThreading) {
+    MultiThreadingMgr::instance().setMode(true);
+    hasPartnerNewUnsentUpdatesTest();
+}
+
 }
index 8c244c8af17879c857dc05114c4026fef45e7024..ae2dde759055851ade397cf3027370bcf4e06f7c 100644 (file)
@@ -59,6 +59,7 @@ public:
     using StateType::last_clock_skew_warn_;
     using StateType::my_time_at_skew_;
     using StateType::partner_time_at_skew_;
+    using StateType::unsent_update_count_;
 };
 
 /// @brief Type of the NakedCommunicationState for DHCPv4.