From 2bf97485093395d576e3ce500fc48c084f40ad1c Mon Sep 17 00:00:00 2001 From: Geir Horn Date: Thu, 16 May 2024 14:48:01 +0200 Subject: [PATCH] Correcting the mess caused by Gerrit Change-Id: If80c41555a93773a76f927036e88c3c6229c031f --- AMPLSolver.cpp | 3 +- Dockerfile | 2 +- MetricUpdater.cpp | 70 +++++++++++++++++++++++++++++++++++++---------- MetricUpdater.hpp | 62 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 114 insertions(+), 23 deletions(-) diff --git a/AMPLSolver.cpp b/AMPLSolver.cpp index 3865871..caeab6a 100644 --- a/AMPLSolver.cpp +++ b/AMPLSolver.cpp @@ -247,7 +247,8 @@ void AMPLSolver::SolveProblem( { Theron::ConsoleOutput Output; - Output << "AMPL Solver: Application Execution Context received. Problem Undefined = " << ProblemUndefined << std::endl + Output << "AMPL Solver: Application Execution Context received. Problem Undefined = " + << std::boolalpha << ProblemUndefined << std::endl << TheContext.dump(2) << std::endl; // There is nothing to do if the application model is missing. diff --git a/Dockerfile b/Dockerfile index 7624ccd..2d116f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN mkdir -p /solver WORKDIR /solver # Development framework, dependencies -RUN dnf --assumeyes install gcc-c++-13.2.1-7.fc39 make-1:4.4.1-2.fc39 git-core-2.44.0-1.fc39 boost-devel-1.81.0-8.fc39 ccache-4.8.2-2.fc39 qpid-proton-cpp-devel-0.38.0-4.fc39 json-c-0.17-1.fc39 json-devel-3.11.2-3.fc39 json-glib-1.8.0-1.fc39 jsoncpp-1.9.5-5.fc39 jsoncpp-devel-1.9.5-5.fc39 coin-or-Couenne-0.5.8-12.fc39 wget-1.21.3-7.fc39 && \ +RUN dnf --assumeyes install gcc-c++-13.2.1-7.fc39 make-1:4.4.1-2.fc39 git-core-2.45.0-1.fc39 boost-devel-1.81.0-8.fc39 ccache-4.8.2-2.fc39 qpid-proton-cpp-devel-0.38.0-4.fc39 json-c-0.17-1.fc39 json-devel-3.11.2-3.fc39 json-glib-1.8.0-1.fc39 jsoncpp-1.9.5-5.fc39 jsoncpp-devel-1.9.5-5.fc39 coin-or-Couenne-0.5.8-12.fc39 wget-1.21.3-7.fc39 && \ dnf clean all && \ git clone https://github.com/jarro2783/cxxopts.git CxxOpts && \ git clone https://github.com/GeirHo/TheronPlusPlus.git Theron++ && \ diff --git a/MetricUpdater.cpp b/MetricUpdater.cpp index 4874ace..f455eaa 100644 --- a/MetricUpdater.cpp +++ b/MetricUpdater.cpp @@ -59,16 +59,16 @@ void MetricUpdater::AddMetricSubscription( TheMetricNames.insert( MetricRecordPointer->first ); + // If a new metric was added, a subscription will be set up for this + // new metric, and the flag indicating that values have been received + // for all metrics will be reset. + if( MetricAdded ) - { Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, std::string( MetricValueUpdate::MetricValueRootString ) + MetricRecordPointer->first ), GetSessionLayerAddress() ); - - AllMetricValuesSet = false; - } } // There could be some metric value records that were defined by the @@ -86,6 +86,16 @@ void MetricUpdater::AddMetricSubscription( MetricValues.erase( TheMetric ); } + + // Finally the number of metrics that does not yet have a value is counted + // to ensure that these must be received before the application context + // can be forwarded to the solver manager. + + if( MetricValues.empty() ) + UnsetMetrics = 1; + else + UnsetMetrics = std::ranges::count_if( std::views::values( MetricValues ), + [](const auto & MetricValue){ return MetricValue.is_null(); } ); } else { @@ -101,6 +111,10 @@ void MetricUpdater::AddMetricSubscription( throw std::invalid_argument( ErrorMessage.str() ); } + + Theron::ConsoleOutput Output; + Output << "Received metric subscription request: " << std::endl + << MetricDefinitions.dump(2) << std::endl; } // The metric update value is received whenever any of subscribed forecasters @@ -132,6 +146,12 @@ void MetricUpdater::AddMetricSubscription( void MetricUpdater::UpdateMetricValue( const MetricValueUpdate & TheMetricValue, const Address TheMetricTopic) { + Theron::ConsoleOutput Output; + + Output << "Metric value received: " << std::endl + << " Topic: " << TheMetricTopic.AsString() << std::endl + << TheMetricValue.dump(2) << std::endl; + Theron::AMQ::TopicName TheTopic = TheMetricTopic.AsString().erase( 0, MetricValueUpdate::MetricValueRootString.size() ); @@ -144,6 +164,17 @@ void MetricUpdater::UpdateMetricValue( ValidityTime = std::max( ValidityTime, TheMetricValue.at( MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >() ); + + if( UnsetMetrics ) + UnsetMetrics = std::ranges::count_if( std::views::values( MetricValues ), + [](const auto & MetricValue){ return MetricValue.is_null(); } ); + + Output << "Metric " << TheTopic << " has new value " + << MetricValues.at( TheTopic ) << std::endl; + } + else + { + Output << TheTopic << " is not a known metric and ignored " << std::endl; } } @@ -202,6 +233,11 @@ MetricUpdater::ApplicationLifecycle::operator State() const // message will just be ignored. In order to avoid the scan over all metrics // to see if they are set, a boolean flag will be used and set once all metrics // have values. Then future scans will be avoided. +// The message will be ignored if not all metric values have been received +// or if there are no metric values defined. In both cases the SLO violation +// message will just be ignored. In order to avoid the scan over all metrics +// to see if they are set, a boolean flag will be used and set once all metrics +// have values. Then future scans will be avoided. void MetricUpdater::SLOViolationHandler( const SLOViolation & SeverityMessage, const Address TheSLOTopic ) @@ -211,23 +247,29 @@ void MetricUpdater::SLOViolationHandler( << SeverityMessage.dump(2) << std::endl; if(( ApplicationState == ApplicationLifecycle::State::Running ) && - ( AllMetricValuesSet || - (!MetricValues.empty() && - std::ranges::none_of( std::views::values( MetricValues ), - [](const auto & MetricValue){ return MetricValue.is_null(); } ))) ) + ( UnsetMetrics == 0 ) ) { Send( Solver::ApplicationExecutionContext( SeverityMessage.at( MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >(), - MetricValues, true + MetricValues, true ), TheSolverManager ); - AllMetricValuesSet = true; ApplicationState = ApplicationLifecycle::State::Deploying; } else + { Output << "... failed to forward the application execution context (size: " - << MetricValues.size() << ")" << std::endl; + << MetricValues.size() << "," << " Unset: " << UnsetMetrics + << ")" << std::endl; + + if( MetricValues.empty() ) + Output << "The Metric Value map is empty! " << std::endl; + else + for( auto & MetricRecord : MetricValues ) + Output << MetricRecord.first << " with value " + << MetricRecord.second.dump(2) << " end " << std::endl; + } } // -------------------------------------------------------------------------- @@ -250,15 +292,15 @@ MetricUpdater::MetricUpdater( const std::string UpdaterName, : Actor( UpdaterName ), StandardFallbackHandler( Actor::GetAddress().AsString() ), NetworkingActor( Actor::GetAddress().AsString() ), - MetricValues(), ValidityTime(0), AllMetricValuesSet(false), + MetricValues(), ValidityTime(0), UnsetMetrics(1), ApplicationState( ApplicationLifecycle::State::New ), TheSolverManager( ManagerOfSolvers ) { RegisterHandler( this, &MetricUpdater::AddMetricSubscription ); RegisterHandler( this, &MetricUpdater::UpdateMetricValue ); - RegisterHandler( this, &MetricUpdater::SLOViolationHandler ); RegisterHandler( this, &MetricUpdater::LifecycleHandler ); - + RegisterHandler( this, &MetricUpdater::SLOViolationHandler ); + Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, MetricTopic::AMQTopic ), diff --git a/MetricUpdater.hpp b/MetricUpdater.hpp index 1d2d855..9366bbb 100644 --- a/MetricUpdater.hpp +++ b/MetricUpdater.hpp @@ -110,14 +110,14 @@ private: Solver::TimePointType ValidityTime; - // When an SLO violation message is received the current vector of metric - // values should be sent as an application execution context (message) to the - // Solution Manager actor that will invoke a solver to find the optimal - // configuration for this configuration. The Metric Updater must therefore - // know the address of the Soler Manager, and this must be passed to - // the constructor. + // The metric context is not complete before at least one value has been + // received for each metric. It is therefore a counter keeping track of + // metric values that are defined, but has not yet seen their first value + // update. An SLO violation message will only result in the triggering of + // a serch for a solution if all metric values have a value so that a + // proper metric context can be forwarded to the solver. - bool AllMetricValuesSet; + unsigned int UnsetMetrics; // -------------------------------------------------------------------------- // Subscribing to metric prediction values @@ -363,6 +363,54 @@ private: const Address TheSolverManager; + // After the sending of the application's excution context, one should not + // initiate another reconfiguration because the state may the possibly be + // inconsistent with the SLO Violation Detector belieivng that the old + // configuration is still in effect while the new configuration is being + // enacted. It is therefore a flag that will be set by the SLO Violation + // handler indicating that a reconfiguration is ongoing. + + bool ReconfigurationInProgress; + + // When a reconfiguration has been enacted by the Optimiser Controller and + // a new configuration is confirmed to be running on the new platofrm, it + // will send a message to inform all other components that the + // reconfiguration has happened. The event is just the reception of the + // message and its content will not be processed, so there are no keys for + // the JSON map received. + + class ReconfigurationMessage + : public Theron::AMQ::JSONTopicMessage + { + public: + + // The topic for the reconfiguration finished messages is defined by the + // optimiser as the sender. + + static constexpr std::string_view AMQTopic + = "eu.nebulouscloud.optimiser.controller.reconfiguration"; + + // Constructors + + ReconfigurationMessage( void ) + : JSONTopicMessage( AMQTopic ) + {} + + ReconfigurationMessage( const ReconfigurationMessage & Other ) + : JSONTopicMessage( Other ) + {} + + virtual ~ReconfigurationMessage() = default; + }; + + // The handler for this message will actually not use its contents, but only + // note that the reconfiguration has been completed to reset the + // reconfiguration in progress flag allowing future SLO Violation Events to + // triger new reconfigurations. + + void ReconfigurationDone( const ReconfigurationMessage & TheReconfiguraton, + const Address TheReconfigurationTopic ); + // -------------------------------------------------------------------------- // Constructor and destructor // --------------------------------------------------------------------------