diff --git a/MetricUpdater.cpp b/MetricUpdater.cpp index 5b7c16b..4874ace 100644 --- a/MetricUpdater.cpp +++ b/MetricUpdater.cpp @@ -147,6 +147,42 @@ void MetricUpdater::UpdateMetricValue( } } +// -------------------------------------------------------------------------- +// Application lifcycle +// -------------------------------------------------------------------------- +// +// When the lifecycle message is received, the state is just recorded in the +// state variable. + +void MetricUpdater::LifecycleHandler( + const ApplicationLifecycle & TheState, + const Address TheLifecycleTopic ) +{ + Theron::ConsoleOutput Output; + + ApplicationState = TheState; + + Output << "Application state updated: " << std::endl + << TheState.dump(2) << std::endl; +} + +// The message handler used the conversion operator to read out the state +// carried in the message. It is based on having a static map from the textual +// representation of the state to the enumeration. + +MetricUpdater::ApplicationLifecycle::operator State() const +{ + static std::map< std::string_view, State > LifecycleStates{ + {"NEW", State::New}, + {"READY", State::Ready}, + {"DEPLOYING", State::Deploying}, + {"RUNNING", State::Running}, + {"FAILED", State::Failed} + }; + + return LifecycleStates.at( this->at("state").get< std::string >() ); +} + // -------------------------------------------------------------------------- // SLO Violation Events // -------------------------------------------------------------------------- @@ -174,7 +210,7 @@ void MetricUpdater::SLOViolationHandler( Output << "Metric Updater: SLO violation received " << std::endl << SeverityMessage.dump(2) << std::endl; - if( !ReconfigurationInProgress && + if(( ApplicationState == ApplicationLifecycle::State::Running ) && ( AllMetricValuesSet || (!MetricValues.empty() && std::ranges::none_of( std::views::values( MetricValues ), @@ -183,41 +219,17 @@ void MetricUpdater::SLOViolationHandler( Send( Solver::ApplicationExecutionContext( SeverityMessage.at( MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >(), - MetricValues, true + MetricValues, true ), TheSolverManager ); - AllMetricValuesSet = true; - ReconfigurationInProgress = true; + AllMetricValuesSet = true; + ApplicationState = ApplicationLifecycle::State::Deploying; } else Output << "... failed to forward the application execution context (size: " << MetricValues.size() << ")" << std::endl; } -// -------------------------------------------------------------------------- -// Reconfigured application -// -------------------------------------------------------------------------- -// -// When the reconfiguration message is received it is an indication tha the -// Optimiser Controller has reconfigured the application and that the -// application is running in the new configuration found by the solver. -// It is the event that is important m not the content of the message, and -// it is therefore only used to reset the ongoing reconfiguration flag. - -void MetricUpdater::ReconfigurationDone( - const ReconfigurationMessage & TheReconfiguraton, - const Address TheReconfigurationTopic ) -{ - Theron::ConsoleOutput Output; - - ReconfigurationInProgress = false; - - Output << "Reconfiguration ongoing flag reset after receiving the following " - << "message indicating that the previous reconfiguration was" - << "completed: " << std::endl - << TheReconfiguraton.dump(2) << std::endl; -} - // -------------------------------------------------------------------------- // Constructor and destructor // -------------------------------------------------------------------------- @@ -239,13 +251,13 @@ MetricUpdater::MetricUpdater( const std::string UpdaterName, StandardFallbackHandler( Actor::GetAddress().AsString() ), NetworkingActor( Actor::GetAddress().AsString() ), MetricValues(), ValidityTime(0), AllMetricValuesSet(false), - TheSolverManager( ManagerOfSolvers ), - ReconfigurationInProgress( false ) + ApplicationState( ApplicationLifecycle::State::New ), + TheSolverManager( ManagerOfSolvers ) { RegisterHandler( this, &MetricUpdater::AddMetricSubscription ); RegisterHandler( this, &MetricUpdater::UpdateMetricValue ); RegisterHandler( this, &MetricUpdater::SLOViolationHandler ); - RegisterHandler( this, &MetricUpdater::ReconfigurationDone ); + RegisterHandler( this, &MetricUpdater::LifecycleHandler ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, @@ -254,7 +266,7 @@ MetricUpdater::MetricUpdater( const std::string UpdaterName, Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription, - ReconfigurationMessage::AMQTopic ), + ApplicationLifecycle::AMQTopic ), GetSessionLayerAddress() ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( @@ -279,7 +291,7 @@ MetricUpdater::~MetricUpdater() Send( Theron::AMQ::NetworkLayer::TopicSubscription( Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription, - ReconfigurationMessage::AMQTopic ), + ApplicationLifecycle::AMQTopic ), GetSessionLayerAddress() ); Send( Theron::AMQ::NetworkLayer::TopicSubscription( diff --git a/MetricUpdater.hpp b/MetricUpdater.hpp index 281500b..1d2d855 100644 --- a/MetricUpdater.hpp +++ b/MetricUpdater.hpp @@ -225,6 +225,74 @@ private: void UpdateMetricValue( const MetricValueUpdate & TheMetricValue, const Address TheMetricTopic ); + // -------------------------------------------------------------------------- + // Application lifecycle + // -------------------------------------------------------------------------- + // + // There is a message from the Optimiser Controller when the status of the + // application changes. The state communicated in this message shows the + // current state of the application and decides how the Solver will act to + // SLO Violations detected. + + class ApplicationLifecycle + : 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.app_state"; + + // The state of the application goes from the the initial creation of + // the cluster to deployments covering reconfigurations. Note that there is + // no state indicating that the application has terminated. + + enum class State + { + New, // Waiting for the utility evaluator + Ready, // The application is ready for deployment + Deploying, // The application is being deployed or redeployed + Running, // The application is running + Failed // The application is in an invalid state + }; + + // An arriving lifecycle message indicates a change in state and it is + // therefore a way to set a state variable directly from the message by + // a cast operator + + operator State() const; + + // Constructors and destructor + + ApplicationLifecycle( void ) + : JSONTopicMessage( AMQTopic ) + {} + + ApplicationLifecycle( const ApplicationLifecycle & Other ) + : JSONTopicMessage( Other ) + {} + + virtual ~ApplicationLifecycle() = default; + }; + + // After starting a reconfiguration with an SLO Violation, 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. The application lifecycle state must therefore be marked as + // running before the another SLO Violation will trigger the next + // reconfiguration + + ApplicationLifecycle::State ApplicationState; + + // The handler for the lifecycle message simply updates this variable by + // setting it to the state received in the lifecycle message. + + void LifecycleHandler( const ApplicationLifecycle & TheState, + const Address TheLifecycleTopic ); + // -------------------------------------------------------------------------- // SLO violations // -------------------------------------------------------------------------- @@ -295,54 +363,6 @@ 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 // --------------------------------------------------------------------------