#include <boost/pointer_cast.hpp>
#include <functional>
+#include <limits>
#include <sstream>
#include <utility>
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() {
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_() {
#include <mutex>
#include <set>
#include <string>
+#include <utility>
namespace isc {
namespace ha {
/// @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_;
/// @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_;
};
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
+#include <limits>
#include <functional>
+#include <limits>
#include <sstream>
using namespace isc;
/// @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.
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();
}
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();
+}
+
}
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.