From: Marcin Siodelski Date: Tue, 27 Jul 2021 17:57:13 +0000 (+0200) Subject: [#1959] Counter for unsent lease updates X-Git-Tag: Kea-2.0.0~85 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0c0ea06b1790e8076c5091f8ba63843ba81b39c1;p=thirdparty%2Fkea.git [#1959] Counter for unsent lease updates --- diff --git a/src/hooks/dhcp/high_availability/communication_state.cc b/src/hooks/dhcp/high_availability/communication_state.cc index 242fe7729a..c7e28371e9 100644 --- a/src/hooks/dhcp/high_availability/communication_state.cc +++ b/src/hooks/dhcp/high_availability/communication_state.cc @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -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 lk(*mutex_); + return (unsent_update_count_); + } else { + return (unsent_update_count_); + } +} + +void +CommunicationState::increaseUnsentUpdateCount() { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard 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::max()) { + ++unsent_update_count_; + } else { + unsent_update_count_ = 1; + } +} + +bool +CommunicationState::hasPartnerNewUnsentUpdates() const { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard 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 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_() { diff --git a/src/hooks/dhcp/high_availability/communication_state.h b/src/hooks/dhcp/high_availability/communication_state.h index 3d2ddc8a6d..2eb66e384e 100644 --- a/src/hooks/dhcp/high_availability/communication_state.h +++ b/src/hooks/dhcp/high_availability/communication_state.h @@ -29,6 +29,7 @@ #include #include #include +#include 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 partner_unsent_update_count_; + /// @brief The mutex used to protect internal state. const boost::scoped_ptr mutex_; }; diff --git a/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc b/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc index 12a8ff84b8..ab65a5e28a 100644 --- a/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc +++ b/src/hooks/dhcp/high_availability/tests/communication_state_unittest.cc @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include 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::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(); +} + } diff --git a/src/hooks/dhcp/high_availability/tests/ha_test.h b/src/hooks/dhcp/high_availability/tests/ha_test.h index 8c244c8af1..ae2dde7590 100644 --- a/src/hooks/dhcp/high_availability/tests/ha_test.h +++ b/src/hooks/dhcp/high_availability/tests/ha_test.h @@ -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.