HAConfig::HAConfig()
: this_server_name_(), ha_mode_(HOT_STANDBY), send_lease_updates_(true),
sync_leases_(true), sync_timeout_(60000), sync_page_limit_(10000),
- heartbeat_delay_(10000), max_response_delay_(60000), max_ack_delay_(10000),
- max_unacked_clients_(10), wait_backup_ack_(false), peers_(),
- state_machine_(new StateMachineConfig()) {
+ delayed_updates_limit_(0), heartbeat_delay_(10000), max_response_delay_(60000),
+ max_ack_delay_(10000), max_unacked_clients_(10), wait_backup_ack_(false),
+ peers_(), state_machine_(new StateMachineConfig()) {
}
HAConfig::PeerConfigPtr
" hot standby configuration");
}
+ // The server must not transition to communication-recovery state in
+ // hot-standby mode.
+ if (delayed_updates_limit_ > 0) {
+ isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
+ " the hot standby configuration");
+ }
+
} else if (ha_mode_ == PASSIVE_BACKUP) {
if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
isc_throw(HAConfigValidationError, "secondary servers not allowed in the"
isc_throw(HAConfigValidationError, "primary server required in the"
" passive backup configuration");
}
+
+ // The server must not transition to communication-recovery state in
+ // passive-backup mode.
+ if (delayed_updates_limit_ > 0) {
+ isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
+ " the passive backup configuration");
+ }
}
}
sync_page_limit_ = sync_page_limit;
}
+ /// @brief Returns the maximum number of lease updates which can be held
+ /// unsent in the communication-recovery state.
+ ///
+ /// If the server is in the communication-recovery state it is unable to
+ /// send lease updates to the partner. Instead it keeps lease updates
+ /// hoping to send them when the communication is resumed. This value
+ /// designates a limit of how many such updates can be held. If this
+ /// number is exceeded the server continues to respond to the clients
+ /// but will have to go through regular lease database synchronization
+ /// when the communication is resumed.
+ ///
+ /// @return Limit of the lease backlog size in communication-recovery.
+ uint32_t getDelayedUpdatesLimit() const {
+ return (delayed_updates_limit_);
+ }
+
+ /// @brief Sets new limit for the number of lease updates to be held
+ /// unsent in the communication-recovery state.
+ ///
+ /// If the server is in the communication-recovery state it is unable to
+ /// send lease updates to the partner. Instead it keeps lease updates
+ /// hoping to send them when the communication is resumed. This value
+ /// designates a limit of how many such updates can be held. If this
+ /// number is exceeded the server continues to respond to the clients
+ /// but will have to go through regular lease database synchronization
+ /// when the communication is resumed.
+ ///
+ /// @param delayed_updates_limit new limit.
+ void setDelayedUpdatesLimit(const uint32_t delayed_updates_limit) {
+ delayed_updates_limit_ = delayed_updates_limit;
+ }
+
+ /// @brief Convenience function checking if communication recovery is allowed.
+ ///
+ /// Communication recovery is only allowed in load-balancing configurations.
+ /// It is enabled by setting delayed-updates-limit to a value greater
+ /// than 0.
+ ///
+ /// @return true if communication recovery is enabled, false otherwise.
+ bool amAllowingCommRecovery() const {
+ return (delayed_updates_limit_ > 0);
+ }
+
/// @brief Returns heartbeat delay in milliseconds.
///
/// This value indicates the delay in sending a heartbeat command after
uint32_t sync_timeout_; ///< Timeout for syncing lease database (ms)
uint32_t sync_page_limit_; ///< Page size limit while synchronizing
///< leases.
+ uint32_t delayed_updates_limit_; ///< Maximum number of lease updates held
+ ///< for later send in communication-recovery.
uint32_t heartbeat_delay_; ///< Heartbeat delay in milliseconds.
uint32_t max_response_delay_; ///< Max delay in response to heartbeats.
uint32_t max_ack_delay_; ///< Maximum DHCP message ack delay.
namespace {
+/// @brief Default values for HA load balancing.
+const SimpleDefaults HA_CONFIG_LB_DEFAULTS = {
+ { "delayed-updates-limit", Element::integer, "100" },
+};
+
/// @brief Default values for HA configuration.
const SimpleDefaults HA_CONFIG_DEFAULTS = {
+ { "delayed-updates-limit", Element::integer, "0" },
{ "heartbeat-delay", Element::integer, "10000" },
{ "max-ack-delay", Element::integer, "10000" },
{ "max-response-delay", Element::integer, "60000" },
// Get the HA configuration.
ElementPtr c = config_vec[0];
- // Set default values.
+ // Get 'mode'. That's the first thing to gather because the defaults we
+ // apply to the configuration depend on the mode.
+ config_storage->setHAMode(getString(c, "mode"));
+
+ // Set load-balancing specific defaults.
+ if (config_storage->getHAMode() == HAConfig::LOAD_BALANCING) {
+ setDefaults(c, HA_CONFIG_LB_DEFAULTS);
+ }
+ // Set general defaults.
setDefaults(c, HA_CONFIG_DEFAULTS);
// HA configuration must be a map.
isc_throw(ConfigError, "expected list of maps in the HA configuration");
}
- // It must contains peers section.
+ // It must contain peers section.
if (!c->contains("peers")) {
isc_throw(ConfigError, "'peers' parameter missing in HA configuration");
}
}
}
-
// We have made major sanity checks, so let's try to gather some values.
// Get 'this-server-name'.
config_storage->setThisServerName(getString(c, "this-server-name"));
- // Get 'mode'.
- config_storage->setHAMode(getString(c, "mode"));
-
// Get 'send-lease-updates'.
config_storage->setSendLeaseUpdates(getBoolean(c, "send-lease-updates"));
uint32_t sync_page_limit = getAndValidateInteger<uint32_t>(c, "sync-page-limit");
config_storage->setSyncPageLimit(sync_page_limit);
+ // Get 'delayed-updates-limit'.
+ uint32_t delayed_updates_limit = getAndValidateInteger<uint32_t>(c, "delayed-updates-limit");
+ config_storage->setDelayedUpdatesLimit(delayed_updates_limit);
+
// Get 'heartbeat-delay'.
uint16_t heartbeat_delay = getAndValidateInteger<uint16_t>(c, "heartbeat-delay");
config_storage->setHeartbeatDelay(heartbeat_delay);
const HAConfigPtr& config, const HAServerType& server_type)
: io_service_(io_service), network_state_(network_state), config_(config),
server_type_(server_type), client_(*io_service), communication_state_(),
- query_filter_(config), mutex_(), pending_requests_(), lease4_update_backlog_(100),
- lease6_update_backlog_(100) {
+ query_filter_(config), mutex_(), pending_requests_(),
+ lease4_update_backlog_(config->getDelayedUpdatesLimit()),
+ lease6_update_backlog_(config->getDelayedUpdatesLimit()) {
if (server_type == HAServerType::DHCPv4) {
communication_state_.reset(new CommunicationState4(io_service_, config));
defineEvent(HA_MAINTENANCE_NOTIFY_EVT, "HA_MAINTENANCE_NOTIFY_EVT");
defineEvent(HA_MAINTENANCE_START_EVT, "HA_MAINTENANCE_START_EVT");
defineEvent(HA_MAINTENANCE_CANCEL_EVT, "HA_MAINTENANCE_CANCEL_EVT");
-}
+ }
void
HAService::verifyEvents() {
if (shouldPartnerDown()) {
verboseTransition(HA_PARTNER_DOWN_ST);
- } else if (config_->getHAMode() == HAConfig::LOAD_BALANCING) {
+ } else if (config_->amAllowingCommRecovery()) {
verboseTransition(HA_COMMUNICATION_RECOVERY_ST);
} else {
" \"sync-leases\": false,"
" \"sync-timeout\": 20000,"
" \"sync-page-limit\": 3,"
+ " \"delayed-updates-limit\": 111,"
" \"heartbeat-delay\": 8,"
" \"max-response-delay\": 11,"
" \"max-ack-delay\": 5,"
EXPECT_FALSE(impl->getConfig()->amSyncingLeases());
EXPECT_EQ(20000, impl->getConfig()->getSyncTimeout());
EXPECT_EQ(3, impl->getConfig()->getSyncPageLimit());
+ EXPECT_EQ(111, impl->getConfig()->getDelayedUpdatesLimit());
+ EXPECT_TRUE(impl->getConfig()->amAllowingCommRecovery());
EXPECT_EQ(8, impl->getConfig()->getHeartbeatDelay());
EXPECT_EQ(11, impl->getConfig()->getMaxResponseDelay());
EXPECT_EQ(5, impl->getConfig()->getMaxAckDelay());
EXPECT_TRUE(impl->getConfig()->amSyncingLeases());
EXPECT_EQ(60000, impl->getConfig()->getSyncTimeout());
EXPECT_EQ(10000, impl->getConfig()->getSyncPageLimit());
+ EXPECT_EQ(0, impl->getConfig()->getDelayedUpdatesLimit());
+ EXPECT_FALSE(impl->getConfig()->amAllowingCommRecovery());
EXPECT_EQ(10000, impl->getConfig()->getHeartbeatDelay());
EXPECT_EQ(10000, impl->getConfig()->getMaxAckDelay());
EXPECT_EQ(10, impl->getConfig()->getMaxUnackedClients());
" },"
" {"
" \"name\": \"server2\","
- " \"url\": \":http//127.0.0.1:8080/\","
+ " \"url\": \"http://127.0.0.1:8080/\","
" \"basic-auth-user\": \"foo:bar\","
" \"role\": \"secondary\","
" \"auto-failover\": true"
"user 'foo:bar' must not contain a ':' in peer 'server2'");
}
+// Test that setting delayed-updates-limit is not allowed in hot-standby mode.
+TEST_F(HAConfigTest, hotStandbyDelayedUpdatesLimit) {
+ testInvalidConfig(
+ "["
+ " {"
+ " \"this-server-name\": \"server1\","
+ " \"mode\": \"hot-standby\","
+ " \"delayed-updates-limit\": 1,"
+ " \"peers\": ["
+ " {"
+ " \"name\": \"server1\","
+ " \"url\": \"http://127.0.0.1:8080/\","
+ " \"role\": \"primary\","
+ " \"auto-failover\": false"
+ " },"
+ " {"
+ " \"name\": \"server2\","
+ " \"url\": \"http://127.0.0.1:8080/\","
+ " \"role\": \"standby\","
+ " \"auto-failover\": true"
+ " }"
+ " ]"
+ " }"
+ "]",
+ "'delayed-updates-limit' must be set to 0 in the hot standby configuration");
+}
+
+// Test that setting delayed-updates-limit is not allowed in passive-backup mode.
+TEST_F(HAConfigTest, passiveBackupDelayedUpdatesLimit) {
+ testInvalidConfig(
+ "["
+ " {"
+ " \"this-server-name\": \"server1\","
+ " \"mode\": \"passive-backup\","
+ " \"delayed-updates-limit\": 1,"
+ " \"peers\": ["
+ " {"
+ " \"name\": \"server1\","
+ " \"url\": \"http://127.0.0.1:8080/\","
+ " \"role\": \"primary\""
+ " },"
+ " {"
+ " \"name\": \"server2\","
+ " \"url\": \"http://127.0.0.1:8080/\","
+ " \"role\": \"backup\""
+ " }"
+ " ]"
+ " }"
+ "]",
+ "'delayed-updates-limit' must be set to 0 in the passive backup configuration");
+}
// Test that conversion of the role names works correctly.
TEST_F(HAConfigTest, stringToRole) {
// Test that primary server in hot standby configuration processes all queries.
TEST_F(HAServiceTest, hotStandbyScopeSelectionThisPrimary) {
- // Create HA configuration for load balancing.
- HAConfigPtr config_storage = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
- config_storage->setHAMode("hot-standby");
+ HAConfigPtr config_storage = createValidConfiguration(HAConfig::HOT_STANDBY);
config_storage->getPeerConfig("server2")->setRole("standby");
// ... and HA service using this configuration.
// Test that secondary server in hot standby configuration processes no queries.
TEST_F(HAServiceTest, hotStandbyScopeSelectionThisStandby) {
- // Create HA configuration for load balancing.
- HAConfigPtr config_storage = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
- config_storage->setHAMode("hot-standby");
+ HAConfigPtr config_storage = createValidConfiguration(HAConfig::HOT_STANDBY);
config_storage->getPeerConfig("server2")->setRole("standby");
config_storage->setThisServerName("server2");
// me to transition to the waiting state and then synchronize my lease
// database.
TEST_F(HAServiceStateMachineTest, waitingParterDownHotStandbyPartnerDown) {
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
// Start the server: offline ---> WAITING state.
FinalState(HA_READY_ST));
}
+// This test verifies that the load balancing server does not transition to
+// the communication recovery state when delayed-updates-limit is set
+// to 0.
+TEST_F(HAServiceStateMachineTest, noCommunicationRecoverytransitionsLoadBalancingPrimary) {
+ partner_->startup();
+
+ HAConfigPtr valid_config = createValidConfiguration();
+ valid_config->setDelayedUpdatesLimit(0);
+ startService(valid_config);
+
+ testTransition(MyState(HA_LOAD_BALANCING_ST), PartnerState(HA_UNAVAILABLE_ST),
+ FinalState(HA_LOAD_BALANCING_ST));
+}
+
// This test checks that the server in the load balancing mode transitions to
// the "terminated" state when the clock skew gets high.
TEST_F(HAServiceStateMachineTest, terminateTransitionsLoadBalancingPrimary) {
TEST_F(HAServiceStateMachineTest, stateTransitionsHotStandbyPrimary) {
partner_->startup();
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
TEST_F(HAServiceStateMachineTest, noSyncingTransitionsHotStandbyPrimary) {
partner_->startup();
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
valid_config->setSyncLeases(false);
TEST_F(HAServiceStateMachineTest, terminateTransitionsHotStandbyPrimary) {
partner_->startup();
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
partner_.reset(new HAPartner(listener_, factory_));
partner_->startup();
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
partner_.reset(new HAPartner(listener_, factory_));
partner_->startup();
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
valid_config->setSyncLeases(false);
partner_.reset(new HAPartner(listener_, factory_));
partner_->startup();
- HAConfigPtr valid_config = createValidConfiguration();
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
// Turn it into hot-standby configuration.
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
// and whether the DHCP service is disabled or enabled in certain states.
// This is primary server.
TEST_F(HAServiceStateMachineTest, scopesServingHotStandbyPrimary) {
- HAConfigPtr valid_config = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
// This test verifies that auto-failover setting does not affect scopes
// handling by the primary server in the hot-standby mode.
TEST_F(HAServiceStateMachineTest, scopesServingHotStandbyPrimaryNoFailover) {
- HAConfigPtr valid_config = createValidConfiguration();
- // Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->getPeerConfig("server2")->setRole("standby");
// Disable auto-failover.
// while being in various states. The HA configuration is hot standby and
// the server is primary.
TEST_F(HAServiceStateMachineTest, shouldSendLeaseUpdatesHotStandbyPrimary) {
- HAConfigPtr valid_config = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
// This test verifies if the server would send heartbeat to the partner
// while being in various states. The HA configuration is hot standby.
TEST_F(HAServiceStateMachineTest, heartbeatHotStandby) {
- HAConfigPtr valid_config = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
- valid_config->setHAMode("hot-standby");
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
// and whether the DHCP service is disabled or enabled in certain states.
// This is standby server.
TEST_F(HAServiceStateMachineTest, scopesServingHotStandbyStandby) {
- HAConfigPtr valid_config = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
// This test verifies that the standby server does not take ownership
// of the primary server's scope when auto-failover is set to false
TEST_F(HAServiceStateMachineTest, scopesServingHotStandbyStandbyNoFailover) {
- HAConfigPtr valid_config = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
// Disable auto-failover.
// while being in various states. The HA configuration is hot standby and
// the server is secondary.
TEST_F(HAServiceStateMachineTest, shouldSendLeaseUpdatesHotStandbyStandby) {
- HAConfigPtr valid_config = createValidConfiguration();
-
- // Turn it into hot-standby configuration.
+ HAConfigPtr valid_config = createValidConfiguration(HAConfig::HOT_STANDBY);
valid_config->setThisServerName("server2");
- valid_config->setHAMode("hot-standby");
valid_config->getPeerConfig("server2")->setRole("standby");
startService(valid_config);
HAConfigPtr
-HATest::createValidConfiguration() const {
+HATest::createValidConfiguration(const HAConfig::HAMode& ha_mode) const {
HAConfigPtr config_storage(new HAConfig());
HAConfigParser parser;
- parser.parse(config_storage, createValidJsonConfiguration());
+ parser.parse(config_storage, createValidJsonConfiguration(ha_mode));
return (config_storage);
}
/// @brief Return HA configuration with three servers.
///
+ /// @param ha_mode HA operation mode (default is load balancing).
/// @return Pointer to the parsed configuration.
- HAConfigPtr createValidConfiguration() const;
+ HAConfigPtr createValidConfiguration(const HAConfig::HAMode& ha_mode =
+ HAConfig::LOAD_BALANCING) const;
/// @brief Return passive-backup configuration.
///
void
QueryFilterTest::hotStandbyThisPrimary() {
- HAConfigPtr config = createValidConfiguration();
-
- config->setHAMode("hot-standby");
+ HAConfigPtr config = createValidConfiguration(HAConfig::HOT_STANDBY);
config->getPeerConfig("server2")->setRole("standby");
QueryFilter filter(config);
void
QueryFilterTest::hotStandbyThisSecondary() {
- HAConfigPtr config = createValidConfiguration();
-
- config->setHAMode("hot-standby");
+ HAConfigPtr config = createValidConfiguration(HAConfig::HOT_STANDBY);
config->getPeerConfig("server2")->setRole("standby");
config->setThisServerName("server2");
void
QueryFilterTest::hotStandbyThisBackup() {
- HAConfigPtr config = createValidConfiguration();
-
- config->setHAMode("hot-standby");
+ HAConfigPtr config = createValidConfiguration(HAConfig::HOT_STANDBY);
config->getPeerConfig("server2")->setRole("standby");
config->setThisServerName("server3");
void
QueryFilterTest::hotStandbyThisPrimary6() {
- HAConfigPtr config = createValidConfiguration();
-
- config->setHAMode("hot-standby");
+ HAConfigPtr config = createValidConfiguration(HAConfig::HOT_STANDBY);
config->getPeerConfig("server2")->setRole("standby");
QueryFilter filter(config);
void
QueryFilterTest::hotStandbyThisSecondary6() {
- HAConfigPtr config = createValidConfiguration();
-
- config->setHAMode("hot-standby");
+ HAConfigPtr config = createValidConfiguration(HAConfig::HOT_STANDBY);
config->getPeerConfig("server2")->setRole("standby");
config->setThisServerName("server2");
void
QueryFilterTest::hotStandbyThisBackup6() {
- HAConfigPtr config = createValidConfiguration();
-
- config->setHAMode("hot-standby");
+ HAConfigPtr config = createValidConfiguration(HAConfig::HOT_STANDBY);
config->getPeerConfig("server2")->setRole("standby");
config->setThisServerName("server3");