77
88using namespace icinga ;
99
10- void Checkable::AddDependencyGroup (const DependencyGroup::Ptr& dependencyGroup)
10+ /* *
11+ * Register all the dependency groups of the current Checkable to the global dependency group registry.
12+ *
13+ * Initially, each Checkable object tracks locally its own dependency groups on Icinga 2 startup, and once the start
14+ * signal of that Checkable is emitted, it pushes all the local tracked dependency groups to the global registry.
15+ * Once the global registry is populated with all the local dependency groups, this Checkable may not necessarily
16+ * contain the exact same dependency groups as it did before, as identical groups are merged together in the registry,
17+ * but it's guaranteed that it's going to have the same *number* of dependency groups as before.
18+ */
19+ void Checkable::PushDependencyGroupsToRegistry ()
1120{
12- std::unique_lock lock (m_DependencyMutex);
13- m_DependencyGroups. insert (dependencyGroup) ;
14- }
21+ std::lock_guard lock (m_DependencyMutex);
22+ decltype (m_DependencyGroups) dependencyGroups{ 0 , HashDependencyGroup, CompareDependencyGroup} ;
23+ m_DependencyGroups. swap (dependencyGroups);
1524
16- void Checkable::RemoveDependencyGroup (const DependencyGroup::Ptr& dependencyGroup)
17- {
18- std::unique_lock lock (m_DependencyMutex);
19- m_DependencyGroups.erase (dependencyGroup);
25+ for (auto & dependencyGroup : dependencyGroups) {
26+ m_DependencyGroups.emplace (DependencyGroup::Register (dependencyGroup));
27+ }
2028}
2129
2230std::vector<DependencyGroup::Ptr> Checkable::GetDependencyGroups () const
@@ -25,6 +33,97 @@ std::vector<DependencyGroup::Ptr> Checkable::GetDependencyGroups() const
2533 return {m_DependencyGroups.begin (), m_DependencyGroups.end ()};
2634}
2735
36+ void Checkable::AddDependency (const Dependency::Ptr& dependency, bool refreshGlobalRegistry)
37+ {
38+ std::lock_guard lock (m_DependencyMutex);
39+ DependencyGroup::Ptr newGroup (new DependencyGroup (dependency->GetRedundancyGroup (), dependency));
40+ if (auto it (m_DependencyGroups.find (newGroup)); it == m_DependencyGroups.end ()) {
41+ m_DependencyGroups.emplace (refreshGlobalRegistry ? DependencyGroup::Register (newGroup) : newGroup);
42+ } else if (!refreshGlobalRegistry) {
43+ // If we're not going to refresh the global registry, we just need to add the dependency to the existing group.
44+ // Meaning, the dependency group itself isn't registered globally yet, so we don't need to re-register it.
45+ (*it)->AddMember (dependency);
46+ } else {
47+ if (auto existingGroup (*it); existingGroup->HasIdenticalMember (dependency)) {
48+ // There's already an identical member in the group and this is likely an exact duplicate of it,
49+ // so it won't change the identity of the group after registration, i.e. regardless whether we're
50+ // supposed to refresh the global registry or not it's identity will remain the same.
51+ existingGroup->AddMember (dependency);
52+ } else {
53+ // We're going to either replace the existing group with "newGroup" or merge the two groups together.
54+ // Either way, it's identity will change, so we need to decouple it from the current Checkable.
55+ m_DependencyGroups.erase (it);
56+
57+ if (auto members (existingGroup->GetMembers (this )); members.size () == existingGroup->GetMemberCount ()) {
58+ // We need to unregister the existing group from the global registry, as it's hash might change
59+ // after adding the new dependency to it down below, and we want to re-register it afterwards.
60+ // This way, we'll also be able to eliminate the possibility of having two identical groups in
61+ // the registry that might occur due to the registration of the new dependency object below.
62+ DependencyGroup::Unregister (existingGroup);
63+ // The current Checkable is the only member of the group, so nothing to move around, just
64+ // add the _duplicate_ dependency to the existing group. Duplicate in the sense that it's
65+ // not identical to any of the existing members but similar enough to be part of the same
66+ // group, i.e. same parent, maybe different period, state filter, etc.
67+ existingGroup->AddMember (dependency);
68+ m_DependencyGroups.emplace (DependencyGroup::Register (existingGroup));
69+ } else {
70+ // Obviously, the current Checkable is not the only member of the existing group, and it's going to
71+ // have more members than the other child Checkables in that group after adding the new dependency
72+ // to it. So, we need to move all the members this Checkable depends on to newGroup and leave the
73+ // existing group as-is, i.e. it's identity won't change afterwards.
74+ for (auto & member : members) {
75+ existingGroup->RemoveMember (member);
76+ newGroup->AddMember (member);
77+ }
78+ m_DependencyGroups.emplace (DependencyGroup::Register (newGroup));
79+ }
80+ }
81+ }
82+ }
83+
84+ void Checkable::RemoveDependency (const Dependency::Ptr& dependency)
85+ {
86+ std::lock_guard lock (m_DependencyMutex);
87+ DependencyGroup::Ptr newGroup (new DependencyGroup (dependency->GetRedundancyGroup (), dependency));
88+ if (auto it (m_DependencyGroups.find (newGroup)); it != m_DependencyGroups.end ()) {
89+ // Obviously, this method is concerned with removing a dependency from the current Checkable, but we've
90+ // initiated a new group with the _to be removed_ dependency in it mainly to perform lookups in the set.
91+ // Now, that we've found the existing group it's member of, we need to remove the dependency from it first.
92+ newGroup->RemoveMember (dependency);
93+
94+ auto existingGroup (*it);
95+ // We're going to either replace or re-register the existing group down below, so we need to decouple it.
96+ // However, only after we've created another reference to it (see existingGroup above), so that we don't
97+ // get a dangling pointer to it in case this was the only reference to it.
98+ m_DependencyGroups.erase (it);
99+
100+ if (auto members (existingGroup->GetMembers (this )); members.size () == existingGroup->GetMemberCount ()) {
101+ // This is a very similar case to the one in AddDependency, but we're going to remove the dependency
102+ // from the group instead of adding it. Therefore, most of the reasoning given there apply here as well.
103+ DependencyGroup::Unregister (existingGroup);
104+ existingGroup->RemoveMember (dependency);
105+ newGroup = existingGroup;
106+ } else {
107+ // The current Checkable is not the only member of the existing group, and it's going to have
108+ // (more or less) fewer members than the other child Checkables within that group after unregistering the
109+ // dependency from it. Meaning, in case the current Checkable have more than one identical dependency in
110+ // the group, it'll end up having the very same dependency group as before, but with one less member.
111+ // However, instead of determining such edge cases manually, we're taking the easy way out, i.e. just move
112+ // all the members (except the one to be removed) to newGroup and re-register it in the global registry.
113+ for (auto & member : members) {
114+ existingGroup->RemoveMember (member);
115+ if (member != dependency) {
116+ newGroup->AddMember (member);
117+ }
118+ }
119+ }
120+
121+ if (newGroup->HasMembers ()) {
122+ m_DependencyGroups.emplace (DependencyGroup::Register (newGroup));
123+ }
124+ }
125+ }
126+
28127std::vector<Dependency::Ptr> Checkable::GetDependencies () const
29128{
30129 std::unique_lock<std::mutex> lock (m_DependencyMutex);
@@ -208,3 +307,45 @@ void Checkable::GetAllChildrenInternal(std::set<Checkable::Ptr>& children, int l
208307
209308 children.insert (localChildren.begin (), localChildren.end ());
210309}
310+
311+ /* *
312+ * Generate the hash of the provided dependency group.
313+ *
314+ * Note, the resulted hash is used to uniquely identify the dependency group on a per Checkable basis and not globally.
315+ * Therefore, for redundancy groups, the hash is generated based on the group name, for non-redundant groups, on the
316+ * parent Checkable name of its members.
317+ *
318+ * @param dependencyGroup The dependency group to generate the hash for.
319+ *
320+ * @return size_t - Returns the hash of the provided dependency group.
321+ */
322+ size_t Checkable::HashDependencyGroup (const DependencyGroup::Ptr& dependencyGroup)
323+ {
324+ return std::hash<String>()(
325+ dependencyGroup->IsRedundancyGroup () ? dependencyGroup->GetName () : dependencyGroup->PeekParentCheckableName ()
326+ );
327+ }
328+
329+ /* *
330+ * Check the equality of the provided dependency groups.
331+ *
332+ * Please note that the equality check is based on criteria that uniquely identify the dependency group
333+ * within a given Checkable only and aren't meant to be used globally.
334+ *
335+ * @param lhs The left-hand side dependency group to compare.
336+ * @param rhs The right-hand side dependency group to compare.
337+ *
338+ * @return bool - Returns true if the provided dependency groups turned to be equal, otherwise false.
339+ */
340+ bool Checkable::CompareDependencyGroup (const DependencyGroup::Ptr& lhs, const DependencyGroup::Ptr& rhs)
341+ {
342+ if (lhs->IsRedundancyGroup () != rhs->IsRedundancyGroup ()) {
343+ return false ;
344+ }
345+
346+ if (lhs->IsRedundancyGroup ()) {
347+ return lhs->GetName () == rhs->GetName ();
348+ }
349+
350+ return lhs->PeekParentCheckableName () == rhs->PeekParentCheckableName ();
351+ }
0 commit comments