#include "Configurator.h"
#include "ExternalRunner.h"
#include
#include
namespace
{
log4cxx::LoggerPtr spLogger(log4cxx::Logger::getLogger("Configurator"));
}
Configurator::Configurator(IConfigurator* storage, IHistoryStorage* history,
size_t maxTransactions)
: mSystemStore(storage)
, mCommitHistory(this, history, 100)
, mInFlightItems(maxTransactions, this)
, mServiceHooksCallback()
{
BOOST_ASSERT(storage);
mServiceHooksCallback = [](const ConfigPairList&, bool)
{
return folly::makeFuture();
};
}
Configurator::~Configurator()
{
}
folly::Future
Configurator::Commit(IConfigurator::ClientId clientId,
sysadminctl::CommitConfig config_opt)
{
if (mInFlightItems.Exists(clientId) && !mInFlightItems.IsBucketLocked(clientId))
{
mInFlightItems.LockBucket(clientId);
auto changed = PruneUnchangedValues(clientId);
CommitHistory::CommitId commitId = 0;
if (changed.size() > 0)
{
commitId = mCommitHistory.LogCommit(changed);
}
SaveLastItems(clientId);
auto toCommit = mInFlightItems.GetBucket(clientId).get();
for (const auto& item : toCommit)
{
mSystemStore->Set(item, clientId);
}
mSystemStore->Commit(clientId);
auto run_services = config_opt == sysadminctl::CommitConfig::DEFAULT;
if (run_services || config_opt == sysadminctl::CommitConfig::TEMPLATE_ONLY)
{
return mServiceHooksCallback(changed,
run_services).thenValue([this, clientId, commitId](auto /*unused*/)
{
LOG4CXX_INFO(spLogger, "All commit signals completed successfully. XID: " Drop(clientId);
return commitId;
}).thenError(folly::tag_t{}, [this, clientId, commitId](const auto& err) -> CommitHistory::CommitId
{
LOG4CXX_ERROR(spLogger, "A commit signal failed, Re-attempt the commit: " Get(key);
}
void Configurator::RegisterCommitHook(ICommandInterface::CommitHook hook)
{
mServiceHooksCallback = hook;
}
ConfigPairMap Configurator::GetAll() const
{
return mSystemStore->GetAll();
}
std::vector Configurator::GetAllKeys(const ConfigPair::Key& ns) const
{
BOOST_ASSERT(ns.IsWildcard());
return mSystemStore->GetAllKeys(ns);
}
bool Configurator::TypeCheck(const ConfigPair& pair) const
{
return mSystemStore->TypeCheck(pair);
}
void Configurator::Erase(const ConfigPair::Key& key, IConfigurator::ClientId clientId)
{
mInFlightItems.Erase(clientId, key);
}
ConfigPairList Configurator::InFlightItems(IConfigurator::ClientId clientId)
{
auto maybebucket = mInFlightItems.GetBucket(clientId);
if (!maybebucket)
{
return ConfigPairList();
}
return maybebucket.get();
}
folly::Future
Configurator::Rollback(CommitHistory::CommitId id)
{
return mCommitHistory.Rollback(id);
}
ConfigPairList Configurator::PruneUnchangedValues(IConfigurator::ClientId clientId)
{
ConfigPairList pruned;
auto maybeItems = mInFlightItems.GetBucket(clientId);
if (!maybeItems)
{
return pruned;
}
std::unordered_map merged;
auto items = maybeItems.get();
for (const auto& item : items)
{
// Always prune last.* keys cause they can't be hook'd on
if (item.GetKey().SubkeyMatches(ConfigKey("last.*")))
{
continue;
}
if (merged.count(item.GetKey()))
{
merged.erase(item.GetKey());
}
merged.emplace(item.GetKey(), item);
}
for (const auto& item : merged)
{
auto last_key = ConfigKey(item.first.ToString());
auto old_item = UnwrapSinglePair(mSystemStore->Get(last_key));
if (!old_item || old_item.get().GetValue() != item.second.GetValue())
{
pruned.push_back(item.second);
}
}
return pruned;
}
void Configurator::SaveLastItems(IConfigurator::ClientId clientId)
{
// TODO: Arguably, this is a needless iteration, we should really be iterating over only the
// changed values
auto maybeItems = mInFlightItems.GetBucket(clientId);
if (!maybeItems)
{
return;
}
ConfigPairList oldItems;
auto items = maybeItems.get();
for (const auto& item : items)
{
auto old_item = UnwrapSinglePair(mSystemStore->Get(item.GetKey()));
// We don't need to save last.* versions of defaults
if (item.GetKey().SubkeyMatches(ConfigKey("default.*")) ||
item.GetKey().SubkeyMatches(ConfigKey("last.*")))
{
continue;
}
ConfigKey last_key("last." + item.GetKey().ToString());
if (old_item)
{
oldItems.push_back(ConfigPair(last_key, old_item.get().GetValue()));
}
else
{
oldItems.push_back(ConfigPair(last_key));
}
}
for (const auto& last : oldItems)
{
mInFlightItems.Insert(clientId, last);
}
}