@@ -127,7 +127,8 @@ QualityObjectsType AggregatorRunner::aggregate()
127127 ILOG (Debug, Trace) << " Aggregate called in AggregatorRunner, QOs in cache: " << mQualityObjects .size () << ENDM ;
128128
129129 QualityObjectsType allQOs;
130- for (auto const & [aggregatorName, aggregator] : mAggregatorsMap ) {
130+ for (auto const & aggregator : mAggregators ) {
131+ string aggregatorName = aggregator->getName ();
131132 ILOG (Info, Devel) << " Processing aggregator: " << aggregatorName << ENDM ;
132133
133134 if (updatePolicyManager.isReady (aggregatorName)) {
@@ -200,17 +201,93 @@ void AggregatorRunner::initAggregators()
200201
201202 // For every aggregator definition, create an Aggregator
202203 for (const auto & [aggregatorName, aggregatorConfig] : mConfigFile ->getRecursive (" qc.aggregators" )) {
203-
204204 ILOG (Info, Devel) << " >> Aggregator name : " << aggregatorName << ENDM ;
205+
205206 if (aggregatorConfig.get <bool >(" active" , true )) {
206- // create Aggregator and store it.
207- auto aggregator = make_shared<Aggregator>(aggregatorName, aggregatorConfig);
208- aggregator->init ();
209- updatePolicyManager.addPolicy (aggregator->getName (), aggregator->getPolicyName (), aggregator->getObjectsNames (), aggregator->getAllObjectsOption (), false );
210- mAggregatorsMap [aggregatorName] = aggregator;
207+ try {
208+ auto aggregator = make_shared<Aggregator>(aggregatorName, aggregatorConfig);
209+ aggregator->init ();
210+ updatePolicyManager.addPolicy (aggregator->getName (),
211+ aggregator->getPolicyName (),
212+ aggregator->getObjectsNames (),
213+ aggregator->getAllObjectsOption (),
214+ false );
215+ mAggregators .push_back (aggregator);
216+ } catch (...) {
217+ // catch the configuration exception and print it to avoid losing it
218+ ILOG (Error, Ops) << " Error creating aggregator '" << aggregatorName << " '"
219+ << current_diagnostic (true ) << ENDM ;
220+ continue ; // skip this aggregator, it might still fail fatally later if another aggregator depended on it
221+ }
211222 }
212223 }
213224 }
225+
226+ reorderAggregators ();
227+ }
228+
229+ bool AggregatorRunner::areSourcesIn (const std::vector<AggregatorSource>& sources,
230+ const std::vector<std::shared_ptr<Aggregator>>& aggregators)
231+ {
232+ for (auto source : sources) {
233+ auto it = find_if (aggregators.begin (), aggregators.end (),
234+ [&](const std::shared_ptr<Aggregator>& agg) { return (agg->getName () == source.name ); });
235+ if (it == aggregators.end ()) {
236+ return false ;
237+ }
238+ }
239+
240+ return true ;
241+ }
242+
243+ void AggregatorRunner::reorderAggregators ()
244+ {
245+ // Implementation
246+ // This is a simple, light-weight, but sub-optimal implementation.
247+ // One could build a proper tree (e.g. with boost Graph) and then apply a complex algorithm to order
248+ // the nodes and find cycles.
249+ // Instead this implementation goes through the aggregators and for each checks whether
250+ // there are no dependencies or if they are all fulfilled. If it is the case the aggregator
251+ // is moved at the end of the resulting vector.
252+ // In case we have looped through all the remaining aggregators and nothing has been done,
253+ // ie. no aggregator has its dependencies fulfilled, we stop and raise an error.
254+ // This means that there is a cycle or that one of the dependencies does not exist.
255+ // Note that by "fulfilled" we mean that all the sources of an aggregator are already
256+ // in the result vector.
257+
258+ std::vector<std::shared_ptr<Aggregator>> originals = mAggregators ;
259+ std::vector<std::shared_ptr<Aggregator>> results;
260+ bool modificationLastIteration = true ;
261+ // As long as there are items in original and we did some modifications in the last iteration
262+ while (!originals.empty () && modificationLastIteration) {
263+ modificationLastIteration = false ;
264+ std::vector<std::shared_ptr<Aggregator>> toBeMoved; // we need it because we cannot modify the vectors while iterating over them
265+ // Loop over remaining items in the original list
266+ for (const auto & orig : originals) {
267+ // if no Aggregator dependencies or Aggregator sources are all already in result
268+ auto sources = orig->getSources (aggregator);
269+ if (sources.empty () || areSourcesIn (sources, results)) {
270+ // move from original to result
271+ toBeMoved.push_back (orig);
272+ modificationLastIteration = true ;
273+ }
274+ }
275+ // move the items from one vector to the other
276+ for (const auto & item : toBeMoved) {
277+ results.push_back (item);
278+ originals.erase (std::remove (originals.begin (), originals.end (), item), originals.end ());
279+ }
280+ }
281+
282+ if (!originals.empty ()) {
283+ string msg =
284+ " Error in the aggregators definition : either there is a cycle "
285+ " or an aggregator depends on an aggregator that does not exist." ;
286+ ILOG (Error, Ops) << msg << ENDM ;
287+ BOOST_THROW_EXCEPTION (FatalException () << errinfo_details (msg));
288+ }
289+ assert (results.size () != mAggregators .size ());
290+ mAggregators = results;
214291}
215292
216293void AggregatorRunner::sendPeriodicMonitoring ()
0 commit comments