icinga2/lib/icingadb/icingadb.cpp
Julian Brost 2d080f14eb IcingaDB: start initial dump in callback instead of timer
Previously, the initial config dump was started in a timer executed
every 15 seconds. During the first execution of the timer, the Redis
connection is typically not established yet. Therefore, this delayed the
initial sync by up to 15 seconds.

This commit instead triggers the sync from a callback that is executed
after the connection is successfully established.

The timer is removed completely. On first glance, it looks like it would
ensure that a lost connection is reestablished, but this is handled
internally by RedisConnection. After the config has been dumped once,
that timer wouldn't ever attempt a reconnect anyways.
2021-01-20 09:31:27 +01:00

150 lines
3.3 KiB
C++

/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "icingadb/icingadb.hpp"
#include "icingadb/icingadb-ti.cpp"
#include "icingadb/redisconnection.hpp"
#include "remote/eventqueue.hpp"
#include "base/json.hpp"
#include "icinga/checkable.hpp"
#include "icinga/host.hpp"
#include <boost/algorithm/string.hpp>
#include <memory>
#include <utility>
using namespace icinga;
#define MAX_EVENTS_DEFAULT 5000
using Prio = RedisConnection::QueryPriority;
static const char * const l_LuaPublishStats = R"EOF(
local xa = {'XADD', KEYS[1], '*'}
for i = 1, #ARGV do
table.insert(xa, ARGV[i])
end
local id = redis.call(unpack(xa))
local xr = redis.call('XRANGE', KEYS[1], '-', '+')
for i = 1, #xr - 1 do
redis.call('XDEL', KEYS[1], xr[i][1])
end
return id
)EOF";
REGISTER_TYPE(IcingaDB);
IcingaDB::IcingaDB()
: m_Rcon(nullptr)
{
m_Rcon = nullptr;
m_WorkQueue.SetName("IcingaDB");
m_PrefixConfigObject = "icinga:config:";
m_PrefixConfigCheckSum = "icinga:checksum:";
m_PrefixStateObject = "icinga:config:state:";
}
/**
* Starts the component.
*/
void IcingaDB::Start(bool runtimeCreated)
{
ObjectImpl<IcingaDB>::Start(runtimeCreated);
Log(LogInformation, "IcingaDB")
<< "'" << GetName() << "' started.";
m_ConfigDumpInProgress = false;
m_ConfigDumpDone = false;
m_WorkQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); });
m_Rcon = new RedisConnection(GetHost(), GetPort(), GetPath(), GetPassword(), GetDbIndex());
m_Rcon->SetConnectedCallback([this](boost::asio::yield_context& yc) {
m_WorkQueue.Enqueue([this]() { OnConnectedHandler(); });
});
m_Rcon->Start();
m_StatsTimer = new Timer();
m_StatsTimer->SetInterval(1);
m_StatsTimer->OnTimerExpired.connect([this](const Timer * const&) { PublishStatsTimerHandler(); });
m_StatsTimer->Start();
m_WorkQueue.SetName("IcingaDB");
m_Rcon->SuppressQueryKind(Prio::CheckResult);
m_Rcon->SuppressQueryKind(Prio::State);
}
void IcingaDB::ExceptionHandler(boost::exception_ptr exp)
{
Log(LogCritical, "IcingaDB", "Exception during redis query. Verify that Redis is operational.");
Log(LogDebug, "IcingaDB")
<< "Exception during redis operation: " << DiagnosticInformation(exp);
}
void IcingaDB::OnConnectedHandler()
{
AssertOnWorkQueue();
if (m_ConfigDumpInProgress || m_ConfigDumpDone)
return;
/* Config dump */
m_ConfigDumpInProgress = true;
PublishStats();
UpdateAllConfigObjects();
m_ConfigDumpDone = true;
m_ConfigDumpInProgress = false;
}
void IcingaDB::PublishStatsTimerHandler(void)
{
PublishStats();
}
void IcingaDB::PublishStats()
{
if (!m_Rcon || !m_Rcon->IsConnected())
return;
Dictionary::Ptr status = GetStats();
status->Set("config_dump_in_progress", m_ConfigDumpInProgress);
status->Set("timestamp", TimestampToMilliseconds(Utility::GetTime()));
std::vector<String> eval ({"EVAL", l_LuaPublishStats, "1", "icinga:stats"});
{
ObjectLock statusLock (status);
for (auto& kv : status) {
eval.emplace_back(kv.first);
eval.emplace_back(JsonEncode(kv.second));
}
}
m_Rcon->FireAndForgetQuery(std::move(eval), Prio::Heartbeat);
}
void IcingaDB::Stop(bool runtimeRemoved)
{
Log(LogInformation, "IcingaDB")
<< "'" << GetName() << "' stopped.";
ObjectImpl<IcingaDB>::Stop(runtimeRemoved);
}
void IcingaDB::AssertOnWorkQueue()
{
ASSERT(m_WorkQueue.IsWorkerThread());
}