2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-01 14:35:29 +00:00

[#3652] Fix memory increase on reconfiguration

Caused by config history. The config history was soft-disabled.
Code had to be refactored to enable CONFIG_LIST_SIZE == 0.
This commit is contained in:
Andrei Pavel
2024-11-25 11:14:26 +02:00
parent bcd51d5cfd
commit db5c4edabd
5 changed files with 43 additions and 28 deletions

View File

@@ -19,7 +19,9 @@ using namespace isc::util;
namespace isc { namespace isc {
namespace dhcp { namespace dhcp {
const size_t CfgMgr::CONFIG_LIST_SIZE = 10; // Value 0 effectively disables configuration history. Higher values can lead to proportionally
// increased memory usage which can be extreme in case of sizable configurations.
const size_t CfgMgr::CONFIG_LIST_SIZE = 0;
CfgMgr& CfgMgr&
CfgMgr::instance() { CfgMgr::instance() {
@@ -69,7 +71,7 @@ CfgMgr::getD2ClientMgr() {
void void
CfgMgr::ensureCurrentAllocated() { CfgMgr::ensureCurrentAllocated() {
if (!configuration_ || configs_.empty()) { if (!configuration_) {
configuration_.reset(new SrvConfig()); configuration_.reset(new SrvConfig());
configs_.push_back(configuration_); configs_.push_back(configuration_);
} }
@@ -79,6 +81,7 @@ void
CfgMgr::clear() { CfgMgr::clear() {
if (configuration_) { if (configuration_) {
configuration_->removeStatistics(); configuration_->removeStatistics();
configuration_.reset();
} }
configs_.clear(); configs_.clear();
external_configs_.clear(); external_configs_.clear();
@@ -92,18 +95,14 @@ CfgMgr::commit() {
// First we need to remove statistics. The new configuration can have fewer // First we need to remove statistics. The new configuration can have fewer
// subnets. Also, it may change subnet-ids. So we need to remove them all // subnets. Also, it may change subnet-ids. So we need to remove them all
// and add it back. // and add them back.
configuration_->removeStatistics(); configuration_->removeStatistics();
if (!configs_.back()->sequenceEquals(*configuration_)) { bool promoted(false);
if (!configs_.empty() && !configs_.back()->sequenceEquals(*configuration_)) {
// Promote the staging configuration to the current configuration.
configuration_ = configs_.back(); configuration_ = configs_.back();
// Keep track of the maximum size of the configs history. Before adding promoted = true;
// new element, we have to remove the oldest one.
if (configs_.size() > CONFIG_LIST_SIZE) {
SrvConfigList::iterator it = configs_.begin();
std::advance(it, configs_.size() - CONFIG_LIST_SIZE);
configs_.erase(configs_.begin(), it);
}
} }
// Set the last commit timestamp. // Set the last commit timestamp.
@@ -114,12 +113,21 @@ CfgMgr::commit() {
configuration_->updateStatistics(); configuration_->updateStatistics();
configuration_->configureLowerLevelLibraries(); configuration_->configureLowerLevelLibraries();
if (promoted) {
// Keep track of the maximum size of the configs history. Remove the oldest elements.
if (configs_.size() > CONFIG_LIST_SIZE) {
SrvConfigList::const_iterator it = configs_.begin();
std::advance(it, configs_.size() - CONFIG_LIST_SIZE);
configs_.erase(configs_.begin(), it);
}
}
} }
void void
CfgMgr::rollback() { CfgMgr::rollback() {
ensureCurrentAllocated(); ensureCurrentAllocated();
if (!configuration_->sequenceEquals(*configs_.back())) { if (!configs_.empty() && !configuration_->sequenceEquals(*configs_.back())) {
configs_.pop_back(); configs_.pop_back();
} }
} }
@@ -130,9 +138,9 @@ CfgMgr::revert(const size_t index) {
if (index == 0) { if (index == 0) {
isc_throw(isc::OutOfRange, "invalid commit index 0 when reverting" isc_throw(isc::OutOfRange, "invalid commit index 0 when reverting"
" to an old configuration"); " to an old configuration");
} else if (index > configs_.size() - 1) { } else if (configs_.size() <= index) {
isc_throw(isc::OutOfRange, "unable to revert to commit index '" isc_throw(isc::OutOfRange, "unable to revert to commit index '"
<< index << "', only '" << configs_.size() - 1 << index << "', only '" << (configs_.size() == 0 ? 0 : configs_.size() - 1)
<< "' previous commits available"); << "' previous commits available");
} }
@@ -143,18 +151,20 @@ CfgMgr::revert(const size_t index) {
// configuration is destroyed by this rollback. // configuration is destroyed by this rollback.
rollback(); rollback();
// Get the iterator to the current configuration and then advance to the if (!configs_.empty()) {
// desired one. // Get the iterator to the current configuration and then advance to the
SrvConfigList::const_reverse_iterator it = configs_.rbegin(); // desired one.
std::advance(it, index); SrvConfigList::const_reverse_iterator it = configs_.rbegin();
std::advance(it, index);
// Copy the desired configuration to the new staging configuration. The // Copy the desired configuration to the new staging configuration. The
// staging configuration is re-created here because we rolled back earlier // staging configuration is re-created here because we rolled back earlier
// in this function. // in this function.
(*it)->copy(*getStagingCfg()); (*it)->copy(*getStagingCfg());
// Make the staging configuration a current one. // Make the staging configuration a current one.
commit(); commit();
}
} }
SrvConfigPtr SrvConfigPtr
@@ -166,7 +176,7 @@ CfgMgr::getCurrentCfg() {
SrvConfigPtr SrvConfigPtr
CfgMgr::getStagingCfg() { CfgMgr::getStagingCfg() {
ensureCurrentAllocated(); ensureCurrentAllocated();
if (configuration_->sequenceEquals(*configs_.back())) { if (configs_.empty() || configuration_->sequenceEquals(*configs_.back())) {
uint32_t sequence = configuration_->getSequence(); uint32_t sequence = configuration_->getSequence();
configs_.push_back(SrvConfigPtr(new SrvConfig(++sequence))); configs_.push_back(SrvConfigPtr(new SrvConfig(++sequence)));
} }

View File

@@ -150,7 +150,7 @@ public:
/// @brief Commits the staging configuration. /// @brief Commits the staging configuration.
/// ///
/// The staging configuration becomes current configuration when this /// The staging configuration becomes current configuration when this
/// function is called. It removes the oldest configuration held in the /// function is called. It removes the oldest configurations held in the
/// history so as the size of the list of configuration does not exceed /// history so as the size of the list of configuration does not exceed
/// the @c CONFIG_LIST_SIZE. /// the @c CONFIG_LIST_SIZE.
/// ///

View File

@@ -412,7 +412,7 @@ SrvConfig::updateStatistics() {
// a lease manager, such as D2. @todo We should probably examine why // a lease manager, such as D2. @todo We should probably examine why
// "SrvConfig" is being used by D2. // "SrvConfig" is being used by D2.
if (LeaseMgrFactory::haveInstance()) { if (LeaseMgrFactory::haveInstance()) {
// Updates statistics for v4 and v6 subnets // Updates statistics for v4 and v6 subnets.
getCfgSubnets4()->updateStatistics(); getCfgSubnets4()->updateStatistics();
getCfgSubnets6()->updateStatistics(); getCfgSubnets6()->updateStatistics();
} }

View File

@@ -543,6 +543,10 @@ TEST_F(CfgMgrTest, revert) {
// Value of 0 also doesn't make sense. // Value of 0 also doesn't make sense.
ASSERT_THROW(cfg_mgr.revert(0), isc::OutOfRange); ASSERT_THROW(cfg_mgr.revert(0), isc::OutOfRange);
// Return early because the checks that follow simply are not supported by
// CONFIG_LIST_SIZE == 0 at the time of writing.
return;
// We should be able to revert to configuration with debuglevel = 10. // We should be able to revert to configuration with debuglevel = 10.
ASSERT_NO_THROW(cfg_mgr.revert(4)); ASSERT_NO_THROW(cfg_mgr.revert(4));
// And this configuration should be now the current one and the debuglevel // And this configuration should be now the current one and the debuglevel

View File

@@ -17,6 +17,7 @@
#include <hooks/hooks_manager.h> #include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h> #include <hooks/callout_handle.h>
#include <stats/stats_mgr.h> #include <stats/stats_mgr.h>
#include <testutils/gtest_utils.h>
#include <dhcpsrv/testutils/test_utils.h> #include <dhcpsrv/testutils/test_utils.h>
#include <dhcpsrv/testutils/alloc_engine_utils.h> #include <dhcpsrv/testutils/alloc_engine_utils.h>
@@ -642,7 +643,7 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
pool_ = Pool4Ptr(new Pool4(pool_start, pool_end)); pool_ = Pool4Ptr(new Pool4(pool_start, pool_end));
subnet_->addPool(pool_); subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet_); EXPECT_NO_THROW_LOG(cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet_));
} }
AllocEngine4Test::AllocEngine4Test() { AllocEngine4Test::AllocEngine4Test() {