[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/StarryInternet/sysadmin/master/src/Configurator.cpp [Back]  [Original]

#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);
    }
}

Web Proxy Viewer  |  New URL  |  Original Page