Skip to content

Commit 488deff

Browse files
committed
IcingaDB: Fix dependency runtime deletion & creation
1 parent fe559ef commit 488deff

File tree

7 files changed

+95
-90
lines changed

7 files changed

+95
-90
lines changed

lib/icinga/checkable-dependency.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,20 @@ std::vector<DependencyGroup::Ptr> Checkable::GetDependencyGroups() const
4343
return {m_DependencyGroups.begin(), m_DependencyGroups.end()};
4444
}
4545

46-
void Checkable::AddDependency(const Dependency::Ptr& dependency, bool refreshGlobalRegistry)
46+
/**
47+
* Add the provided dependency to the current Checkable list of dependencies.
48+
*
49+
* Returns an existing dependency group that has been modified due to the addition of the new dependency object.
50+
* Meaning, either the dependency groups identity has changed after adding the new dependency to it, or the provided
51+
* dependency caused the creation of a new dependency group and decoupling from of the existing one. In the latter case,
52+
* the returned value is the previously existing group that the current Checkable was member of.
53+
*
54+
* @param dependency The dependency to add.
55+
* @param refreshGlobalRegistry Whether to automatically refresh the global registry or not.
56+
*
57+
* @return DependencyGroup::Ptr The dependency group that has been modified.
58+
*/
59+
DependencyGroup::Ptr Checkable::AddDependency(const Dependency::Ptr& dependency, bool refreshGlobalRegistry)
4760
{
4861
std::lock_guard lock(m_DependencyMutex);
4962
DependencyGroup::Ptr newGroup(new DependencyGroup(dependency->GetRedundancyGroup(), dependency));
@@ -53,6 +66,7 @@ void Checkable::AddDependency(const Dependency::Ptr& dependency, bool refreshGlo
5366
// If we're not going to refresh the global registry, we just need to add the dependency to the existing group.
5467
// Meaning, the dependency group itself isn't registered globally yet, so we don't need to re-register it.
5568
(*it)->AddMember(dependency);
69+
return *it;
5670
} else {
5771
if (auto existingGroup(*it); existingGroup->HasIdenticalMember(dependency)) {
5872
// There's already an identical member in the group and this is likely an exact duplicate of it,
@@ -86,11 +100,30 @@ void Checkable::AddDependency(const Dependency::Ptr& dependency, bool refreshGlo
86100
}
87101
m_DependencyGroups.emplace(DependencyGroup::Register(newGroup));
88102
}
103+
104+
// In both of the above cases, the identity of the existing group is going to probably change,
105+
// so we'll need to clean up all the database entries/relations referencing its old identity.
106+
return existingGroup;
89107
}
90108
}
109+
110+
return nullptr;
91111
}
92112

93-
void Checkable::RemoveDependency(const Dependency::Ptr& dependency)
113+
/**
114+
* Remove the provided dependency from the current Checkable list of dependencies.
115+
*
116+
* Removing a dependency from the current Checkable almost always involves refreshing the global registry as well,
117+
* regardless of whether it's a runtime deletion or not. However, it is a bit cheaper/easier operation compared to
118+
* manually identifying and merging the dependency groups.
119+
*
120+
* Returns the dependency group the provided dependency object was member of / is removed from, otherwise nullptr.
121+
*
122+
* @param dependency The dependency to remove.
123+
*
124+
* @return DependencyGroup::Ptr The dependency group the provided dependency object was removed from.
125+
*/
126+
DependencyGroup::Ptr Checkable::RemoveDependency(const Dependency::Ptr& dependency)
94127
{
95128
std::lock_guard lock(m_DependencyMutex);
96129
DependencyGroup::Ptr newGroup(new DependencyGroup(dependency->GetRedundancyGroup(), dependency));
@@ -129,7 +162,10 @@ void Checkable::RemoveDependency(const Dependency::Ptr& dependency)
129162
if (newGroup->HasMembers()) {
130163
m_DependencyGroups.emplace(DependencyGroup::Register(newGroup));
131164
}
165+
return existingGroup;
132166
}
167+
168+
return nullptr;
133169
}
134170

135171
std::vector<Dependency::Ptr> Checkable::GetDependencies() const

lib/icinga/checkable.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ class Checkable : public ObjectImpl<Checkable>
187187
/* Dependencies */
188188
void PushDependencyGroupsToRegistry();
189189
std::vector<intrusive_ptr<DependencyGroup>> GetDependencyGroups() const;
190-
void AddDependency(const intrusive_ptr<Dependency>& dependency, bool refreshGlobalRegistry = false);
191-
void RemoveDependency(const intrusive_ptr<Dependency>& dependency);
190+
intrusive_ptr<DependencyGroup> AddDependency(const intrusive_ptr<Dependency>& dependency, bool refreshGlobalRegistry = false);
191+
intrusive_ptr<DependencyGroup> RemoveDependency(const intrusive_ptr<Dependency>& dependency);
192192
std::vector<intrusive_ptr<Dependency> > GetDependencies() const;
193193
bool HasAnyDependencies() const;
194194

lib/icinga/dependency-group.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
using namespace icinga;
88

9-
boost::signals2::signal<void (const Dependency::Ptr&, const std::vector<DependencyGroup::Ptr>&)> DependencyGroup::OnMembersChanged;
9+
boost::signals2::signal<void(const DependencyGroup::Ptr&, const Dependency::Ptr&)> DependencyGroup::OnMembersChanged;
1010

1111
std::mutex DependencyGroup::m_RegistryMutex;
1212
DependencyGroup::RegistryType DependencyGroup::m_Registry;

lib/icinga/dependency.cpp

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,8 @@ void Dependency::OnAllConfigLoaded()
205205
void Dependency::Start(bool runtimeCreated)
206206
{
207207
if (runtimeCreated) {
208-
std::vector<DependencyGroup::Ptr> previousGroups;
209-
if (!GetRedundancyGroup().IsEmpty()) {
210-
previousGroups = m_Child->GetDependencyGroups();
211-
}
212-
213208
// We need to directly refresh the global registry here only when the Checkable itself isn't created at runtime.
214-
m_Child->AddDependency(this, m_Child->IsActive());
209+
auto modifiedGroup(m_Child->AddDependency(this, m_Child->IsActive()));
215210
m_Parent->AddReverseDependency(this);
216211

217212
try {
@@ -227,18 +222,7 @@ void Dependency::Start(bool runtimeCreated)
227222
// isn't active yet, it means that the Checkable is also created at runtime, and we don't need
228223
// to notify Icinga DB, as the Checkable's OnVersionChanged signal will do that for us.
229224
if (m_Child->IsActive()) {
230-
std::vector<DependencyGroup::Ptr> outdatedGroups;
231-
if (!previousGroups.empty()) {
232-
auto newGroups(m_Child->GetDependencyGroups());
233-
std::set_difference(
234-
previousGroups.begin(),
235-
previousGroups.end(),
236-
newGroups.begin(),
237-
newGroups.end(),
238-
std::back_inserter(outdatedGroups)
239-
);
240-
}
241-
DependencyGroup::OnMembersChanged(this, outdatedGroups);
225+
DependencyGroup::OnMembersChanged(modifiedGroup, this);
242226
}
243227
}
244228

@@ -249,36 +233,11 @@ void Dependency::Stop(bool runtimeRemoved)
249233
{
250234
ObjectImpl<Dependency>::Stop(runtimeRemoved);
251235

252-
std::vector<DependencyGroup::Ptr> previousGroups;
253-
std::set<DependencyGroup::Ptr> outdatedGroups;
254-
if (runtimeRemoved && !GetRedundancyGroup().IsEmpty()) {
255-
previousGroups = m_Child->GetDependencyGroups();
256-
// Find the dependency group that this dependency is a member of, and add it to the outdated groups list.
257-
// Otherwise, once the dependency is unregistered, we won't be able to identify the group that this dependency
258-
// was member of anymore.
259-
for (auto& dependencyGroup : previousGroups) {
260-
if (dependencyGroup->HasIdenticalMember(this)) {
261-
outdatedGroups.emplace(dependencyGroup);
262-
break;
263-
}
264-
}
265-
}
266-
267-
m_Child->RemoveDependency(this);
236+
auto modifiedGroup(m_Child->RemoveDependency(this));
268237
GetParent()->RemoveReverseDependency(this);
269238

270239
if (runtimeRemoved) {
271-
if (!previousGroups.empty()) {
272-
auto newGroups(m_Child->GetDependencyGroups());
273-
std::set_difference(
274-
previousGroups.begin(),
275-
previousGroups.end(),
276-
newGroups.begin(),
277-
newGroups.end(),
278-
std::inserter(outdatedGroups, outdatedGroups.end())
279-
);
280-
}
281-
DependencyGroup::OnMembersChanged(this, {outdatedGroups.begin(), outdatedGroups.end()});
240+
DependencyGroup::OnMembersChanged(modifiedGroup, this);
282241
}
283242
}
284243

lib/icinga/dependency.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ class DependencyGroup final : public SharedObject
190190

191191
State GetState(DependencyType dt = DependencyState, int rstack = 0) const;
192192

193-
static boost::signals2::signal<void (const Dependency::Ptr&, const std::vector<DependencyGroup::Ptr>&)> OnMembersChanged;
193+
static boost::signals2::signal<void(const DependencyGroup::Ptr&, const Dependency::Ptr&)> OnMembersChanged;
194194

195195
private:
196196
void MoveMembersTo(const DependencyGroup::Ptr& other);

lib/icingadb/icingadb-objects.cpp

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ void IcingaDB::ConfigStaticInitialize()
133133
IcingaDB::NextCheckUpdatedHandler(checkable);
134134
});
135135

136-
DependencyGroup::OnMembersChanged.connect(&IcingaDB::DependencyGroupsChangedHandler);
136+
DependencyGroup::OnMembersChanged.connect(&IcingaDB::DependencyGroupChangedHandler);
137137

138138
Service::OnHostProblemChanged.connect([](const Service::Ptr& service, const CheckResult::Ptr&, const MessageOrigin::Ptr&) {
139139
IcingaDB::HostProblemChangedHandler(service);
@@ -2803,59 +2803,69 @@ void IcingaDB::SendCustomVarsChanged(const ConfigObject::Ptr& object, const Dict
28032803
}
28042804
}
28052805

2806-
void IcingaDB::SendDependencyGroupsChanged(const Dependency::Ptr& dep, const std::vector<DependencyGroup::Ptr>& outdatedGroups)
2806+
void IcingaDB::SendDependencyGroupChanged(const DependencyGroup::Ptr& modifiedGroup, const Dependency::Ptr& member)
28072807
{
28082808
if (!m_Rcon || !m_Rcon->IsConnected()) {
28092809
return;
28102810
}
28112811

2812-
auto parent(dep->GetParent());
2813-
auto child(dep->GetChild());
2812+
auto child(member->GetChild());
28142813
if (!child->HasAnyDependencies()) {
28152814
// If the child Checkable has no parent and reverse dependencies, we can safely remove the dependency node.
28162815
DeleteRelationship(GetObjectIdentifier(child), "dependency:node");
28172816
}
28182817

28192818
RedisConnection::Queries hdels, xAdds;
2820-
auto deleteState([this, &hdels, &xAdds](const String& redisKey, const String& id) {
2819+
auto deleteState([this, &hdels, &xAdds](const String& id, const String& redisKey) {
28212820
hdels.emplace_back(RedisConnection::Query{"HDEL", m_PrefixConfigObject + redisKey, id});
28222821
xAdds.emplace_back(RedisConnection::Query{
28232822
"XADD", "icinga:runtime:state", "MAXLEN", "~", "1000000", "*", "runtime_type", "delete",
28242823
"redis_key", m_PrefixConfigObject + redisKey, "id", id
28252824
});
28262825
});
28272826

2828-
if (dep->GetRedundancyGroup().IsEmpty() && !dep->IsActive() && dep->GetExtension("ConfigObjectDeleted")) {
2829-
auto identifier(HashValue(new Array{GetObjectIdentifier(child), GetObjectIdentifier(parent)}));
2827+
auto parent(member->GetParent());
2828+
if (!modifiedGroup->IsRedundancyGroup() && !member->IsActive() && member->GetExtension("ConfigObjectDeleted")) {
2829+
auto edgeId(HashValue(new Array{GetObjectIdentifier(child), GetObjectIdentifier(parent)}));
28302830
// Remove the edge between the parent and child Checkable linked through the removed dependency.
2831-
DeleteRelationship(identifier, "dependency:edge");
2832-
// The dependency object is going to be deleted, so we need to remove its state from Redis as well.
2833-
deleteState("dependency:edge:state", identifier);
2834-
}
2831+
DeleteRelationship(edgeId, "dependency:edge");
2832+
2833+
if (!modifiedGroup->HasMembers() || !modifiedGroup->HasIdenticalMember(member)) {
2834+
DependencyGroup::Ptr dummyGroup(new DependencyGroup("", member));
2835+
// In order to recreate the edge state ID, we need to copy the members of the modified group
2836+
// to a dummy one and use its composite key to generate its previous edge state ID.
2837+
for (auto dependency : modifiedGroup->GetMembers(child.get())) {
2838+
dummyGroup->AddMember(dependency);
2839+
}
28352840

2836-
auto newGroups(child->GetDependencyGroups());
2837-
for (auto& group : outdatedGroups) {
2838-
String redundancyGroupId(group->GetIcingaDBIdentifier());
2839-
bool sameCompositeKey(redundancyGroupId == HashValue(new Array{m_EnvironmentId, group->GetCompositeKey()}));
2840-
if (!sameCompositeKey || !group->HasMembers() || std::find(newGroups.begin(), newGroups.end(), group) == newGroups.end()) {
2841+
deleteState(
2842+
// Keep this edge state ID generation in sync with the one in SerializeDependencyEdgeState.
2843+
HashValue(new Array{dummyGroup->GetCompositeKey(), GetObjectIdentifier(parent)}),
2844+
"dependency:edge:state"
2845+
);
2846+
}
2847+
} else if (modifiedGroup->IsRedundancyGroup()) {
2848+
auto newGroups(child->GetDependencyGroups());
2849+
String redundancyGroupId(modifiedGroup->GetIcingaDBIdentifier());
2850+
bool sameCompositeKey(redundancyGroupId == HashValue(new Array{m_EnvironmentId, modifiedGroup->GetCompositeKey()}));
2851+
if (!sameCompositeKey || !modifiedGroup->HasMembers() || std::find(newGroups.begin(), newGroups.end(), modifiedGroup) == newGroups.end()) {
28412852
// Remove the connection from the child Checkable to the redundancy group.
28422853
DeleteRelationship(HashValue(new Array{GetObjectIdentifier(child), redundancyGroupId}), "dependency:edge");
28432854

2844-
if (!sameCompositeKey || !group->HasMembers()) {
2845-
auto members(group->GetMembers(child.get()));
2846-
members.emplace_back(dep);
2847-
for (auto& member : members) {
2855+
if (!sameCompositeKey || !modifiedGroup->HasMembers()) {
2856+
auto members(modifiedGroup->GetMembers(child.get()));
2857+
members.emplace_back(member);
2858+
for (auto& dependency : members) {
28482859
// Remove the connection from the redundancy group to the parent Checkable of the removed
28492860
// dependency, if there's no other member in the redundancy group that references that very
2850-
// same parent Checkable or the dependency Group's Icinga DB identifier has changed.
2851-
auto groupParentId(HashValue(new Array{redundancyGroupId, GetObjectIdentifier(member->GetParent())}));
2852-
DeleteRelationship(groupParentId, "dependency:edge");
2853-
deleteState("dependency:edge:state", groupParentId);
2861+
// same parent Checkable or the redundancy group's Icinga DB identifier has changed.
2862+
auto id(HashValue(new Array{redundancyGroupId, GetObjectIdentifier(dependency->GetParent())}));
2863+
DeleteRelationship(id, "dependency:edge");
2864+
deleteState(id, "dependency:edge:state");
28542865
}
28552866

2856-
// Remove the group entirely as it either has no members left or it's Icinga DB identifier has changed.
2857-
deleteState("dependency:edge:state", redundancyGroupId);
2858-
deleteState("redundancygroup:state", redundancyGroupId);
2867+
deleteState(redundancyGroupId, "dependency:edge:state" );
2868+
deleteState(redundancyGroupId, "redundancygroup:state");
28592869
DeleteRelationship(redundancyGroupId, "dependency:node");
28602870
DeleteRelationship(redundancyGroupId, "redundancygroup");
28612871
}
@@ -2865,16 +2875,16 @@ void IcingaDB::SendDependencyGroupsChanged(const Dependency::Ptr& dep, const std
28652875
if (!hdels.empty()) {
28662876
m_Rcon->FireAndForgetQueries(std::move(hdels), Prio::RuntimeStateSync);
28672877
m_Rcon->FireAndForgetQueries(std::move(xAdds), Prio::RuntimeStateStream, {0, 1});
2878+
}
28682879

2869-
if (!newGroups.empty()) {
2870-
std::map<String, RedisConnection::Query> hMSets;
2871-
std::vector<Dictionary::Ptr> runtimeUpdates;
2872-
InsertCheckableDependencies(child, hMSets, &runtimeUpdates);
2880+
if (child->HasAnyDependencies()) {
2881+
std::map<String, RedisConnection::Query> hMSets;
2882+
std::vector<Dictionary::Ptr> runtimeUpdates;
2883+
InsertCheckableDependencies(child, hMSets, &runtimeUpdates);
2884+
ExecuteRedisTransaction(hMSets, runtimeUpdates);
28732885

2874-
ExecuteRedisTransaction(hMSets, runtimeUpdates);
2875-
UpdateState(child, StateUpdate::Full);
2876-
}
2877-
}
2886+
UpdateState(child, StateUpdate::Full);
2887+
}
28782888

28792889
// The affect{ed,s}_children might now have different outcome, so we need to update the parent Checkable as well.
28802890
SendConfigUpdate(parent, true);
@@ -3163,10 +3173,10 @@ void IcingaDB::NextCheckUpdatedHandler(const Checkable::Ptr& checkable)
31633173
}
31643174
}
31653175

3166-
void IcingaDB::DependencyGroupsChangedHandler(const Dependency::Ptr& dep, const std::vector<DependencyGroup::Ptr>& outdatedGroups)
3176+
void IcingaDB::DependencyGroupChangedHandler(const DependencyGroup::Ptr& modifiedGroup, const Dependency::Ptr& member)
31673177
{
31683178
for (auto& rw : ConfigType::GetObjectsByType<IcingaDB>()) {
3169-
rw->SendDependencyGroupsChanged(dep, outdatedGroups);
3179+
rw->SendDependencyGroupChanged(modifiedGroup, member);
31703180
}
31713181
}
31723182

lib/icingadb/icingadb.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class IcingaDB : public ObjectImpl<IcingaDB>
140140
void SendCommandEnvChanged(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
141141
void SendCommandArgumentsChanged(const ConfigObject::Ptr& command, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
142142
void SendCustomVarsChanged(const ConfigObject::Ptr& object, const Dictionary::Ptr& oldValues, const Dictionary::Ptr& newValues);
143-
void SendDependencyGroupsChanged(const Dependency::Ptr& dep, const std::vector<DependencyGroup::Ptr>& outdatedGroups);
143+
void SendDependencyGroupChanged(const DependencyGroup::Ptr& modifiedGroup, const Dependency::Ptr& member);
144144

145145
void ForwardHistoryEntries();
146146

@@ -187,7 +187,7 @@ class IcingaDB : public ObjectImpl<IcingaDB>
187187
static void FlappingChangeHandler(const Checkable::Ptr& checkable, double changeTime);
188188
static void NewCheckResultHandler(const Checkable::Ptr& checkable);
189189
static void NextCheckUpdatedHandler(const Checkable::Ptr& checkable);
190-
static void DependencyGroupsChangedHandler(const Dependency::Ptr& dep, const std::vector<DependencyGroup::Ptr>& outdatedGroups);
190+
static void DependencyGroupChangedHandler(const DependencyGroup::Ptr& modifiedGroup, const Dependency::Ptr& member);
191191
static void HostProblemChangedHandler(const Service::Ptr& service);
192192
static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double changeTime, double expiry);
193193
static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy, double changeTime);

0 commit comments

Comments
 (0)