From ed5ad46266638d99067c10e65bea10850a328dac Mon Sep 17 00:00:00 2001 From: Julian Brost Date: Thu, 10 Feb 2022 16:08:30 +0100 Subject: [PATCH] IDO: use per-instance notification_id in history When there are multiple active IDO instances on the same node, before this commit, all of them would share a single DbValue object for the notification_id column of the icinga_contactnotifications table. This resulted in the issue that one database references the notification_id in another database. This commit fixes this by using a separate DbValue value for each IDO instance. This needs a new signal as the existing OnQuery and OnMultipleQueries signals perform the same queries on all IDO instances, but different queries are needed here per instance (they only differ in the referenced DbValue). Therefore, a new signal OnMakeQueries is added that takes a std::function which is called once per IDO instance and can access callbacks to perform one or multiple queries only on this specific IDO instance. --- lib/db_ido/dbconnection.cpp | 15 ++++- lib/db_ido/dbevents.cpp | 111 +++++++++++++++++++----------------- lib/db_ido/dbobject.cpp | 1 + lib/db_ido/dbobject.hpp | 6 ++ 4 files changed, 79 insertions(+), 54 deletions(-) diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index c500e2d0f..62160d86d 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -45,8 +45,19 @@ void DbConnection::Start(bool runtimeCreated) Log(LogInformation, "DbConnection") << "'" << GetName() << "' started."; - DbObject::OnQuery.connect([this](const DbQuery& query) { ExecuteQuery(query); }); - DbObject::OnMultipleQueries.connect([this](const std::vector& multiQueries) { ExecuteMultipleQueries(multiQueries); }); + auto onQuery = [this](const DbQuery& query) { ExecuteQuery(query); }; + DbObject::OnQuery.connect(onQuery); + + auto onMultipleQueries = [this](const std::vector& multiQueries) { ExecuteMultipleQueries(multiQueries); }; + DbObject::OnMultipleQueries.connect(onMultipleQueries); + + DbObject::QueryCallbacks queryCallbacks; + queryCallbacks.Query = onQuery; + queryCallbacks.MultipleQueries = onMultipleQueries; + + DbObject::OnMakeQueries.connect([queryCallbacks](const std::function& queryFunc) { + queryFunc(queryCallbacks); + }); } void DbConnection::Stop(bool runtimeRemoved) diff --git a/lib/db_ido/dbevents.cpp b/lib/db_ido/dbevents.cpp index 91444ed63..8358824e7 100644 --- a/lib/db_ido/dbevents.cpp +++ b/lib/db_ido/dbevents.cpp @@ -837,72 +837,79 @@ void DbEvents::AddAcknowledgementInternal(const Checkable::Ptr& checkable, Ackno void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text) { - /* start and end happen at the same time */ - std::pair timeBag = ConvertTimestamp(Utility::GetTime()); + /* NotificationInsertID has to be tracked per IDO instance, therefore the OnQuery and OnMultipleQueries signals + * cannot be called directly as all IDO instances would insert rows with the same ID which is (most likely) only + * correct in one database. Instead, pass a lambda which generates the queries with new DbValue for + * NotificationInsertID each IDO instance. + */ + DbObject::OnMakeQueries([&checkable, &users, &type, &cr](const DbObject::QueryCallbacks& callbacks) { + /* start and end happen at the same time */ + std::pair timeBag = ConvertTimestamp(Utility::GetTime()); - DbQuery query1; - query1.Table = "notifications"; - query1.Type = DbQueryInsert; - query1.Category = DbCatNotification; - query1.NotificationInsertID = new DbValue(DbValueObjectInsertID, -1); + DbQuery query1; + query1.Table = "notifications"; + query1.Type = DbQueryInsert; + query1.Category = DbCatNotification; + query1.NotificationInsertID = new DbValue(DbValueObjectInsertID, -1); - Host::Ptr host; - Service::Ptr service; - tie(host, service) = GetHostService(checkable); + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); - Dictionary::Ptr fields1 = new Dictionary(); - fields1->Set("notification_type", 1); /* service */ - fields1->Set("notification_reason", MapNotificationReasonType(type)); - fields1->Set("object_id", checkable); - fields1->Set("start_time", DbValue::FromTimestamp(timeBag.first)); - fields1->Set("start_time_usec", timeBag.second); - fields1->Set("end_time", DbValue::FromTimestamp(timeBag.first)); - fields1->Set("end_time_usec", timeBag.second); + Dictionary::Ptr fields1 = new Dictionary(); + fields1->Set("notification_type", 1); /* service */ + fields1->Set("notification_reason", MapNotificationReasonType(type)); + fields1->Set("object_id", checkable); + fields1->Set("start_time", DbValue::FromTimestamp(timeBag.first)); + fields1->Set("start_time_usec", timeBag.second); + fields1->Set("end_time", DbValue::FromTimestamp(timeBag.first)); + fields1->Set("end_time_usec", timeBag.second); - if (service) - fields1->Set("state", service->GetState()); - else - fields1->Set("state", GetHostState(host)); + if (service) + fields1->Set("state", service->GetState()); + else + fields1->Set("state", GetHostState(host)); - if (cr) { - fields1->Set("output", CompatUtility::GetCheckResultOutput(cr)); - fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr)); - } + if (cr) { + fields1->Set("output", CompatUtility::GetCheckResultOutput(cr)); + fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr)); + } - fields1->Set("escalated", 0); - fields1->Set("contacts_notified", static_cast(users.size())); - fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */ + fields1->Set("escalated", 0); + fields1->Set("contacts_notified", static_cast(users.size())); + fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */ - Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName()); + Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName()); - if (endpoint) - fields1->Set("endpoint_object_id", endpoint); + if (endpoint) + fields1->Set("endpoint_object_id", endpoint); - query1.Fields = fields1; - DbObject::OnQuery(query1); + query1.Fields = fields1; + callbacks.Query(query1); - std::vector queries; + std::vector queries; - for (const User::Ptr& user : users) { - DbQuery query2; - query2.Table = "contactnotifications"; - query2.Type = DbQueryInsert; - query2.Category = DbCatNotification; + for (const User::Ptr& user : users) { + DbQuery query2; + query2.Table = "contactnotifications"; + query2.Type = DbQueryInsert; + query2.Category = DbCatNotification; - query2.Fields = new Dictionary({ - { "contact_object_id", user }, - { "start_time", DbValue::FromTimestamp(timeBag.first) }, - { "start_time_usec", timeBag.second }, - { "end_time", DbValue::FromTimestamp(timeBag.first) }, - { "end_time_usec", timeBag.second }, - { "notification_id", query1.NotificationInsertID }, - { "instance_id", 0 } /* DbConnection class fills in real ID */ - }); + query2.Fields = new Dictionary({ + { "contact_object_id", user }, + { "start_time", DbValue::FromTimestamp(timeBag.first) }, + { "start_time_usec", timeBag.second }, + { "end_time", DbValue::FromTimestamp(timeBag.first) }, + { "end_time_usec", timeBag.second }, + { "notification_id", query1.NotificationInsertID }, + { "instance_id", 0 } /* DbConnection class fills in real ID */ + }); - queries.emplace_back(std::move(query2)); - } + queries.emplace_back(std::move(query2)); + } - DbObject::OnMultipleQueries(queries); + callbacks.MultipleQueries(queries); + }); } /* statehistory */ diff --git a/lib/db_ido/dbobject.cpp b/lib/db_ido/dbobject.cpp index d63c21cf0..406bf5243 100644 --- a/lib/db_ido/dbobject.cpp +++ b/lib/db_ido/dbobject.cpp @@ -25,6 +25,7 @@ using namespace icinga; boost::signals2::signal DbObject::OnQuery; boost::signals2::signal&)> DbObject::OnMultipleQueries; +boost::signals2::signal&)> DbObject::OnMakeQueries; INITIALIZE_ONCE(&DbObject::StaticInitialize); diff --git a/lib/db_ido/dbobject.hpp b/lib/db_ido/dbobject.hpp index 1b07f6006..399b77d42 100644 --- a/lib/db_ido/dbobject.hpp +++ b/lib/db_ido/dbobject.hpp @@ -61,8 +61,14 @@ public: static DbObject::Ptr GetOrCreateByObject(const ConfigObject::Ptr& object); + struct QueryCallbacks { + std::function Query; + std::function&)> MultipleQueries; + }; + static boost::signals2::signal OnQuery; static boost::signals2::signal&)> OnMultipleQueries; + static boost::signals2::signal&)> OnMakeQueries; void SendConfigUpdateHeavy(const Dictionary::Ptr& configFields); void SendConfigUpdateLight();