mirror of
https://github.com/Icinga/icinga2.git
synced 2025-09-25 18:48:50 +02:00
Prevent worst-case exponential complexity in dependency evaluation
So far, calling Checkable::IsReachable() traversed all possible paths to it's parents. In case a parent is reachable via multiple paths, all it's parents were evaluated multiple times, result in a worst-case exponential complexity. With this commit, the implementation keeps track of which checkables were already visited and uses the already-computed reachability instead of repeating the computation, ensuring a worst-case linear runtime within the graph size.
This commit is contained in:
parent
43f1e6f3a1
commit
63e9ef58ba
@ -25,6 +25,14 @@ DependencyStateChecker::DependencyStateChecker(DependencyType dt)
|
|||||||
*/
|
*/
|
||||||
bool DependencyStateChecker::IsReachable(Checkable::ConstPtr checkable, int rstack)
|
bool DependencyStateChecker::IsReachable(Checkable::ConstPtr checkable, int rstack)
|
||||||
{
|
{
|
||||||
|
// If the reachability of this checkable was already computed, return it directly. Otherwise, already create a
|
||||||
|
// temporary map entry that says that this checkable is unreachable so that the different cases returning false
|
||||||
|
// don't have to deal with updating the cache, but only the final return true does. Cyclic dependencies are invalid,
|
||||||
|
// hence recursive calls won't access the potentially not yet correct cached value.
|
||||||
|
if (auto [it, inserted] = m_Cache.insert({checkable, false}); !inserted) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
if (rstack > Dependency::MaxDependencyRecursionLevel) {
|
if (rstack > Dependency::MaxDependencyRecursionLevel) {
|
||||||
Log(LogWarning, "Checkable")
|
Log(LogWarning, "Checkable")
|
||||||
<< "Too many nested dependencies (>" << Dependency::MaxDependencyRecursionLevel << ") for checkable '"
|
<< "Too many nested dependencies (>" << Dependency::MaxDependencyRecursionLevel << ") for checkable '"
|
||||||
@ -53,6 +61,9 @@ bool DependencyStateChecker::IsReachable(Checkable::ConstPtr checkable, int rsta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: This must do the map lookup again. The iterator from above must not be used as a m_Cache.insert() inside a
|
||||||
|
// recursive may have invalidated it.
|
||||||
|
m_Cache[checkable] = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +250,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
DependencyType m_DependencyType;
|
DependencyType m_DependencyType;
|
||||||
|
std::unordered_map<Checkable::ConstPtr, bool> m_Cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user