mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-26 23:24:09 +02:00
Introduce DependencyGroup
helper class
This commit is contained in:
parent
93d9fad565
commit
d7c9e6687e
@ -39,7 +39,7 @@ set(icinga_SOURCES
|
||||
comment.cpp comment.hpp comment-ti.hpp
|
||||
compatutility.cpp compatutility.hpp
|
||||
customvarobject.cpp customvarobject.hpp customvarobject-ti.hpp
|
||||
dependency.cpp dependency.hpp dependency-ti.hpp dependency-apply.cpp
|
||||
dependency.cpp dependency-group.cpp dependency.hpp dependency-ti.hpp dependency-apply.cpp
|
||||
downtime.cpp downtime.hpp downtime-ti.hpp
|
||||
envresolver.cpp envresolver.hpp
|
||||
eventcommand.cpp eventcommand.hpp eventcommand-ti.hpp
|
||||
|
@ -15,6 +15,18 @@ using namespace icinga;
|
||||
*/
|
||||
static constexpr int l_MaxDependencyRecursionLevel(256);
|
||||
|
||||
void Checkable::AddDependencyGroup(const DependencyGroup::Ptr& dependencyGroup)
|
||||
{
|
||||
std::unique_lock lock(m_DependencyMutex);
|
||||
m_DependencyGroups.insert(dependencyGroup);
|
||||
}
|
||||
|
||||
void Checkable::RemoveDependencyGroup(const DependencyGroup::Ptr& dependencyGroup)
|
||||
{
|
||||
std::unique_lock lock(m_DependencyMutex);
|
||||
m_DependencyGroups.erase(dependencyGroup);
|
||||
}
|
||||
|
||||
void Checkable::AddDependency(const Dependency::Ptr& dep)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_DependencyMutex);
|
||||
|
@ -57,6 +57,7 @@ enum FlappingStateFilter
|
||||
class CheckCommand;
|
||||
class EventCommand;
|
||||
class Dependency;
|
||||
class DependencyGroup;
|
||||
|
||||
/**
|
||||
* An Icinga service.
|
||||
@ -184,6 +185,8 @@ public:
|
||||
bool IsFlapping() const;
|
||||
|
||||
/* Dependencies */
|
||||
void AddDependencyGroup(const intrusive_ptr<DependencyGroup>& dependencyGroup);
|
||||
void RemoveDependencyGroup(const intrusive_ptr<DependencyGroup>& dependencyGroup);
|
||||
void AddDependency(const intrusive_ptr<Dependency>& dep);
|
||||
void RemoveDependency(const intrusive_ptr<Dependency>& dep);
|
||||
std::vector<intrusive_ptr<Dependency> > GetDependencies() const;
|
||||
@ -246,6 +249,7 @@ private:
|
||||
|
||||
/* Dependencies */
|
||||
mutable std::mutex m_DependencyMutex;
|
||||
std::set<intrusive_ptr<DependencyGroup>> m_DependencyGroups;
|
||||
std::set<intrusive_ptr<Dependency> > m_Dependencies;
|
||||
std::set<intrusive_ptr<Dependency> > m_ReverseDependencies;
|
||||
|
||||
|
340
lib/icinga/dependency-group.cpp
Normal file
340
lib/icinga/dependency-group.cpp
Normal file
@ -0,0 +1,340 @@
|
||||
/* Icinga 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
#include "icinga/dependency.hpp"
|
||||
#include "base/object-packer.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
std::mutex DependencyGroup::m_RegistryMutex;
|
||||
DependencyGroup::RegistryType DependencyGroup::m_Registry;
|
||||
|
||||
/**
|
||||
* Refresh the global registry of dependency groups.
|
||||
*
|
||||
* Registers the provided dependency object to an existing dependency group with the same redundancy
|
||||
* group name (if any), or creates a new one and registers it to the child Checkable and the registry.
|
||||
*
|
||||
* Note: This is a helper function intended for internal use only, and you should acquire the global registry mutex
|
||||
* before calling this function.
|
||||
*
|
||||
* @param dependency The dependency object to refresh the registry for.
|
||||
* @param unregister A flag indicating whether the provided dependency object should be unregistered from the registry.
|
||||
*/
|
||||
void DependencyGroup::RefreshRegistry(const Dependency::Ptr& dependency, bool unregister)
|
||||
{
|
||||
auto registerRedundancyGroup = [](const DependencyGroup::Ptr& dependencyGroup) {
|
||||
if (auto [it, inserted](m_Registry.insert(dependencyGroup.get())); !inserted) {
|
||||
DependencyGroup::Ptr existingGroup(*it);
|
||||
dependencyGroup->CopyDependenciesTo(existingGroup);
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve all the dependency groups with the same redundancy group name of the provided dependency object.
|
||||
// This allows us to shorten the lookup for the _one_ optimal group to (un)register the dependency from/to.
|
||||
auto [begin, end] = m_Registry.get<1>().equal_range(dependency->GetRedundancyGroup());
|
||||
for (auto it(begin); it != end; ++it) {
|
||||
DependencyGroup::Ptr existingGroup(*it);
|
||||
auto child(dependency->GetChild());
|
||||
if (auto dependencies(existingGroup->GetDependenciesForChild(child.get())); !dependencies.empty()) {
|
||||
m_Registry.erase(existingGroup->GetCompositeKey()); // Will be re-registered when needed down below.
|
||||
if (unregister) {
|
||||
existingGroup->RemoveDependency(dependency);
|
||||
// Remove the connection between the child Checkable and the dependency group if it has no members
|
||||
// left or the above removed member was the only member of the group that the child depended on.
|
||||
if (existingGroup->IsEmpty() || dependencies.size() == 1) {
|
||||
child->RemoveDependencyGroup(existingGroup);
|
||||
}
|
||||
}
|
||||
|
||||
size_t totalDependencies(existingGroup->GetDependenciesCount());
|
||||
// If the existing dependency group has an identical member already, or the child Checkable of the
|
||||
// dependency object is the only member of it (totalDependencies == dependencies.size()), we can simply
|
||||
// add the dependency object to the existing group.
|
||||
if (!unregister && (existingGroup->HasParentWithConfig(dependency) || totalDependencies == dependencies.size())) {
|
||||
existingGroup->AddDependency(dependency);
|
||||
} else if (!unregister || (dependencies.size() > 1 && totalDependencies >= dependencies.size())) {
|
||||
// The child Checkable is going to have a new dependency group, so we must detach the existing one.
|
||||
child->RemoveDependencyGroup(existingGroup);
|
||||
|
||||
Ptr replacementGroup(unregister ? nullptr : new DependencyGroup(existingGroup->GetRedundancyGroupName(), dependency));
|
||||
for (auto& existingDependency : dependencies) {
|
||||
if (existingDependency != dependency) {
|
||||
existingGroup->RemoveDependency(existingDependency);
|
||||
if (replacementGroup) {
|
||||
replacementGroup->AddDependency(existingDependency);
|
||||
} else {
|
||||
replacementGroup = new DependencyGroup(existingGroup->GetRedundancyGroupName(), existingDependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child->AddDependencyGroup(replacementGroup);
|
||||
registerRedundancyGroup(replacementGroup);
|
||||
}
|
||||
|
||||
if (!existingGroup->IsEmpty()) {
|
||||
registerRedundancyGroup(existingGroup);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unregister) {
|
||||
// We couldn't find any existing dependency group to register the dependency to, so we must
|
||||
// initiate a new one and attach it to the child Checkable and register to the global registry.
|
||||
DependencyGroup::Ptr newGroup(new DependencyGroup(dependency->GetRedundancyGroup()));
|
||||
newGroup->AddDependency(dependency);
|
||||
dependency->GetChild()->AddDependencyGroup(newGroup);
|
||||
registerRedundancyGroup(newGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the provided dependency to the global dependency group registry.
|
||||
*
|
||||
* @param dependency The dependency to register.
|
||||
*/
|
||||
void DependencyGroup::Register(const Dependency::Ptr& dependency)
|
||||
{
|
||||
std::lock_guard lock(m_RegistryMutex);
|
||||
RefreshRegistry(dependency, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the provided dependency from the dependency group it was member of.
|
||||
*
|
||||
* @param dependency The dependency to unregister.
|
||||
*/
|
||||
void DependencyGroup::Unregister(const Dependency::Ptr& dependency)
|
||||
{
|
||||
std::lock_guard lock(m_RegistryMutex);
|
||||
RefreshRegistry(dependency, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the size of the global dependency group registry.
|
||||
*
|
||||
* @return size_t - Returns the size of the global dependency groups registry.
|
||||
*/
|
||||
size_t DependencyGroup::GetRegistrySize()
|
||||
{
|
||||
std::lock_guard lock(m_RegistryMutex);
|
||||
return m_Registry.size();
|
||||
}
|
||||
|
||||
DependencyGroup::DependencyGroup(String name): m_RedundancyGroupName(std::move(name))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a composite key for the provided dependency.
|
||||
*
|
||||
* The composite key consists of all the properties of the provided dependency object that influence its availability.
|
||||
*
|
||||
* @param dependency The dependency object to create a composite key for.
|
||||
*
|
||||
* @return - Returns the composite key for the provided dependency.
|
||||
*/
|
||||
DependencyGroup::CompositeKeyType DependencyGroup::MakeCompositeKeyFor(const Dependency::Ptr& dependency)
|
||||
{
|
||||
return std::make_tuple(
|
||||
dependency->GetParent().get(),
|
||||
dependency->GetPeriod().get(),
|
||||
dependency->GetStateFilter(),
|
||||
dependency->GetIgnoreSoftStates()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current dependency group is empty.
|
||||
*
|
||||
* @return bool - Returns true if the current dependency group has no members, otherwise false.
|
||||
*/
|
||||
bool DependencyGroup::IsEmpty() const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
return m_Members.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all dependency objects of the current dependency group the provided child Checkable depend on.
|
||||
*
|
||||
* @param child The child Checkable to get the dependencies for.
|
||||
*
|
||||
* @return - Returns all the dependencies of the provided child Checkable in the current dependency group.
|
||||
*/
|
||||
std::vector<Dependency::Ptr> DependencyGroup::GetDependenciesForChild(const Checkable* child) const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
std::vector<Dependency::Ptr> dependencies;
|
||||
for (auto& [_, children] : m_Members) {
|
||||
auto [begin, end] = children.equal_range(child);
|
||||
std::transform(begin, end, std::back_inserter(dependencies), [](const auto& pair) {
|
||||
return pair.second;
|
||||
});
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the number of dependency objects in the current dependency group.
|
||||
*
|
||||
* This function mainly exists for optimization purposes, i.e. instead of getting a copy of the members and
|
||||
* counting them, we can directly query the number of dependencies in the group.
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t DependencyGroup::GetDependenciesCount() const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
size_t count(0);
|
||||
for (auto& [_, dependencies] : m_Members) {
|
||||
count += dependencies.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dependency object to the current dependency group.
|
||||
*
|
||||
* @param dependency The dependency to add to the dependency group.
|
||||
*/
|
||||
void DependencyGroup::AddDependency(const Dependency::Ptr& dependency)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
auto compositeKey(MakeCompositeKeyFor(dependency));
|
||||
if (auto it(m_Members.find(compositeKey)); it != m_Members.end()) {
|
||||
it->second.emplace(dependency->GetChild().get(), dependency.get());
|
||||
} else {
|
||||
m_Members.emplace(compositeKey, MemberValueType{{dependency->GetChild().get(), dependency.get()}});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a dependency object from the current dependency group.
|
||||
*
|
||||
* @param dependency The dependency to remove from the dependency group.
|
||||
*/
|
||||
void DependencyGroup::RemoveDependency(const Dependency::Ptr& dependency)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
if (auto it(m_Members.find(MakeCompositeKeyFor(dependency))); it != m_Members.end()) {
|
||||
auto [begin, end] = it->second.equal_range(dependency->GetChild().get());
|
||||
for (auto childrenIt(begin); childrenIt != end; ++childrenIt) {
|
||||
if (childrenIt->second == dependency) {
|
||||
// This will also remove the child Checkable from the multimap container
|
||||
// entirely if this was the last child of it.
|
||||
it->second.erase(childrenIt);
|
||||
// If the composite key has no more children left, we can remove it entirely as well.
|
||||
if (it->second.empty()) {
|
||||
m_Members.erase(it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the dependency objects of the current dependency group to the provided dependency group (destination).
|
||||
*
|
||||
* @param dest The dependency group to move the dependencies to.
|
||||
*/
|
||||
void DependencyGroup::CopyDependenciesTo(const DependencyGroup::Ptr& dest)
|
||||
{
|
||||
VERIFY(this != dest); // Prevent from doing something stupid, i.e. deadlocking ourselves.
|
||||
|
||||
std::lock_guard lock(m_Mutex);
|
||||
DependencyGroup::Ptr thisPtr(this); // Just in case the Checkable below was our last reference.
|
||||
for (auto& [_, children] : m_Members) {
|
||||
Checkable::Ptr previousChild;
|
||||
for (auto& [checkable, dependency] : children) {
|
||||
dest->AddDependency(dependency);
|
||||
if (!previousChild || previousChild != checkable) {
|
||||
previousChild = dependency->GetChild();
|
||||
previousChild->RemoveDependencyGroup(thisPtr);
|
||||
previousChild->AddDependencyGroup(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Icinga DB identifier for the current dependency group.
|
||||
*
|
||||
* The only usage of this function is the Icinga DB feature used to cache the unique hash of this dependency groups.
|
||||
*
|
||||
* @param identifier The Icinga DB identifier to set.
|
||||
*/
|
||||
void DependencyGroup::SetIcingaDBIdentifier(const String& identifier)
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
m_IcingaDBIdentifier = identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Icinga DB identifier for the current dependency group.
|
||||
*
|
||||
* When the identifier is not already set by Icinga DB via the SetIcingaDBIdentifier method,
|
||||
* this will just return an empty string.
|
||||
*
|
||||
* @return - Returns the Icinga DB identifier for the current dependency group.
|
||||
*/
|
||||
String DependencyGroup::GetIcingaDBIdentifier() const
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
return m_IcingaDBIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the redundancy group name of the current dependency group.
|
||||
*
|
||||
* If the current dependency group doesn't represent a redundancy group, this will return an empty string.
|
||||
*
|
||||
* @return - Returns the name of the current dependency group.
|
||||
*/
|
||||
const String& DependencyGroup::GetRedundancyGroupName() const
|
||||
{
|
||||
// We don't need to lock the mutex here, as the name is set once during
|
||||
// the object construction and never changed afterwards.
|
||||
return m_RedundancyGroupName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the unique composite key of the current dependency group.
|
||||
*
|
||||
* The composite key consists of some unique data of the group members, and should be used to generate
|
||||
* a unique deterministic hash for the dependency group. Additionally, for explicitly configured redundancy
|
||||
* groups, the non-unique dependency group name is also included on top of the composite keys.
|
||||
*
|
||||
* @return - Returns the composite key of the current dependency group.
|
||||
*/
|
||||
String DependencyGroup::GetCompositeKey()
|
||||
{
|
||||
// This a copy of the CompositeKeyType definition but with the String type instead of Checkable* and TimePeriod*.
|
||||
// This is because we need to produce a deterministic value from the composite key after each restart and that's
|
||||
// not achievable using pointers.
|
||||
using StringTuple = std::tuple<String, String, int, bool>;
|
||||
std::vector<StringTuple> compositeKeys;
|
||||
{
|
||||
std::lock_guard lock(m_Mutex);
|
||||
for (auto& [compositeKey, _] : m_Members) {
|
||||
auto [parent, tp, stateFilter, ignoreSoftStates] = compositeKey;
|
||||
compositeKeys.emplace_back(parent->GetName(), tp ? tp->GetName() : "", stateFilter, ignoreSoftStates);
|
||||
}
|
||||
}
|
||||
|
||||
// IMPORTANT: The order of the composite keys must be sorted to ensure the deterministic hash value.
|
||||
std::sort(compositeKeys.begin(), compositeKeys.end());
|
||||
|
||||
Array::Ptr data(new Array{GetRedundancyGroupName()});
|
||||
for (auto& compositeKey : compositeKeys) {
|
||||
auto [parent, tp, stateFilter, ignoreSoftStates] = compositeKey;
|
||||
data->Add(std::move(parent));
|
||||
data->Add(std::move(tp));
|
||||
data->Add(stateFilter);
|
||||
data->Add(ignoreSoftStates);
|
||||
}
|
||||
|
||||
return PackObject(data);
|
||||
}
|
@ -3,9 +3,17 @@
|
||||
#ifndef DEPENDENCY_H
|
||||
#define DEPENDENCY_H
|
||||
|
||||
#include "base/shared-object.hpp"
|
||||
#include "config/configitem.hpp"
|
||||
#include "icinga/i2-icinga.hpp"
|
||||
#include "icinga/dependency-ti.hpp"
|
||||
#include "config/configitem.hpp"
|
||||
#include "icinga/timeperiod.hpp"
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/mem_fun.hpp>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
@ -60,6 +68,139 @@ private:
|
||||
static void BeforeOnAllConfigLoadedHandler(const ConfigItems& items);
|
||||
};
|
||||
|
||||
/**
|
||||
* A DependencyGroup represents a set of dependencies that are somehow related to each other.
|
||||
*
|
||||
* Specifically, a DependencyGroup is a container for Dependency objects of different Checkables that share the same
|
||||
* child -> parent relationship config, thus forming a group of dependencies. All dependencies of a Checkable that
|
||||
* have the same "redundancy_group" attribute value set are guaranteed to be part of the same DependencyGroup object,
|
||||
* and another Checkable will join that group if and only if it has identical set of dependencies, that is, the same
|
||||
* parent(s), same redundancy group name and all other dependency attributes required to form a composite key.
|
||||
*
|
||||
* More specifically, let's say we have a dependency graph like this:
|
||||
* @verbatim
|
||||
* PP1 PP2
|
||||
* /\ /\
|
||||
* || ||
|
||||
* ––––||–––––––––––––––||–––––
|
||||
* P1 - ( "RG1" ) - P2
|
||||
* ––––––––––––––––––––––––––––
|
||||
* /\ /\
|
||||
* || ||
|
||||
* C1 C2
|
||||
* @endverbatim
|
||||
* The arrows represent a dependency relationship from bottom to top, i.e. both "C1" and "C2" depend on
|
||||
* their "RG1" redundancy group, and "P1" and "P2" depend each on their respective parents (PP1, PP2 - no group).
|
||||
* Now, as one can see, both "C1" and "C2" have identical dependencies, that is, they both depend on the same
|
||||
* redundancy group "RG1" (these could e.g. be constructed through some Apply Rules).
|
||||
*
|
||||
* So, instead of having to maintain two separate copies of that graph, we can bring that imaginary redundancy group
|
||||
* into reality by putting both "P1" and "P2" into an actual DependencyGroup object. However, we don't really put "P1"
|
||||
* and "P2" objects into that group, but rather the actual Dependency objects of both child Checkables. Therefore, the
|
||||
* group wouldn't just contain 2 dependencies, but 4 in total, i.e. 2 for each child Checkable (C1 -> {P1, P2} and
|
||||
* C2 -> {P1, P2}). This way, both child Checkables can just refer to that very same DependencyGroup object.
|
||||
*
|
||||
* However, since not all dependencies are part of a redundancy group, we also have to consider the case where
|
||||
* a Checkable has dependencies that are not part of any redundancy group, like P1 -> PP1. In such situations,
|
||||
* each of the child Checkables (e.g. P1, P2) will have their own (sharable) DependencyGroup object just like for RGs.
|
||||
* This allows us to keep the implementation simple and treat redundant and non-redundant dependencies in the same
|
||||
* way, without having to introduce any special cases everywhere. So, in the end, we'd have 3 dependency groups in
|
||||
* total, i.e. one for the redundancy group "RG1" (shared by C1 and C2), and two distinct groups for P1 and P2.
|
||||
*
|
||||
* @ingroup icinga
|
||||
*/
|
||||
class DependencyGroup final : public SharedObject
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(DependencyGroup);
|
||||
|
||||
/**
|
||||
* Defines the key type of each dependency group members.
|
||||
*
|
||||
* This tuple consists of the dependency parent Checkable, the dependency time period (nullptr if not configured),
|
||||
* the state filter, and the ignore soft states flag. Each of these values influences the availability of the
|
||||
* dependency object, and thus used to group similar dependencies from different Checkables together.
|
||||
*/
|
||||
using CompositeKeyType = std::tuple<Checkable*, TimePeriod*, int, bool>;
|
||||
|
||||
/**
|
||||
* Represents the value type of each dependency group members.
|
||||
*
|
||||
* It stores the dependency objects of any given Checkable that produce the same composite key (CompositeKeyType).
|
||||
* In other words, when looking at the dependency graph from the class description, the two dependency objects
|
||||
* {C1, C2} -> P1 produce the same composite key, thus they are mapped to the same MemberValueType container with
|
||||
* "C1" and "C2" as their keys respectively. Since Icinga 2 allows to construct different identical dependencies
|
||||
* (duplicates), we're using a multimap instead of a simple map here.
|
||||
*/
|
||||
using MemberValueType = std::unordered_multimap<const Checkable*, Dependency*>;
|
||||
using MembersMap = std::map<CompositeKeyType, MemberValueType>;
|
||||
|
||||
explicit DependencyGroup(String name);
|
||||
|
||||
static void Register(const Dependency::Ptr& dependency);
|
||||
static void Unregister(const Dependency::Ptr& dependency);
|
||||
static size_t GetRegistrySize();
|
||||
|
||||
static CompositeKeyType MakeCompositeKeyFor(const Dependency::Ptr& dependency);
|
||||
|
||||
/**
|
||||
* Check whether the current dependency group represents an explicitly configured redundancy group.
|
||||
*
|
||||
* @return bool - Returns true if it's a redundancy group, false otherwise.
|
||||
*/
|
||||
inline bool IsRedundancyGroup() const
|
||||
{
|
||||
return !m_RedundancyGroupName.IsEmpty();
|
||||
}
|
||||
|
||||
bool IsEmpty() const;
|
||||
std::vector<Dependency::Ptr> GetDependenciesForChild(const Checkable* child) const;
|
||||
size_t GetDependenciesCount() const;
|
||||
|
||||
void SetIcingaDBIdentifier(const String& identifier);
|
||||
String GetIcingaDBIdentifier() const;
|
||||
|
||||
const String& GetRedundancyGroupName() const;
|
||||
String GetCompositeKey();
|
||||
|
||||
protected:
|
||||
void AddDependency(const Dependency::Ptr& dependency);
|
||||
void RemoveDependency(const Dependency::Ptr& dependency);
|
||||
void CopyDependenciesTo(const DependencyGroup::Ptr& dest);
|
||||
|
||||
static void RefreshRegistry(const Dependency::Ptr& dependency, bool unregister);
|
||||
|
||||
private:
|
||||
mutable std::mutex m_Mutex;
|
||||
String m_IcingaDBIdentifier;
|
||||
String m_RedundancyGroupName;
|
||||
MembersMap m_Members;
|
||||
|
||||
using RegistryType = boost::multi_index_container<
|
||||
DependencyGroup*, // The type of the elements stored in the container.
|
||||
boost::multi_index::indexed_by<
|
||||
// This unique index allows to search/erase dependency groups by their composite key in an efficient manner.
|
||||
boost::multi_index::hashed_unique<
|
||||
boost::multi_index::mem_fun<DependencyGroup, String, &DependencyGroup::GetCompositeKey>,
|
||||
std::hash<String>
|
||||
>,
|
||||
// This non-unique index allows to search for dependency groups by their name, and reduces the overall
|
||||
// runtime complexity. Without this index, we would have to iterate over all elements to find the one
|
||||
// with the desired members and since containers don't allow erasing elements while iterating, we would
|
||||
// have to copy each of them to a temporary container, and then erase and reinsert them back to the original
|
||||
// container. This produces way too much overhead, and slows down the startup time of Icinga 2 significantly.
|
||||
boost::multi_index::hashed_non_unique<
|
||||
boost::multi_index::const_mem_fun<DependencyGroup, const String&, &DependencyGroup::GetName>,
|
||||
std::hash<String>
|
||||
>
|
||||
>
|
||||
>;
|
||||
|
||||
// The global registry of dependency groups.
|
||||
static std::mutex m_RegistryMutex;
|
||||
static RegistryType m_Registry;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* DEPENDENCY_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user