First complete Solver release
Change-Id: Icd5a77c6400a57db93bdd65e61a022af95f8bacb
This commit is contained in:
parent
d2324e42a3
commit
58b37118fb
@ -174,7 +174,11 @@ void AMPLSolver::DefineProblem(const Solver::OptimisationProblem & TheProblem,
|
|||||||
// The default values for the data will be loaded from the data file. This
|
// The default values for the data will be loaded from the data file. This
|
||||||
// operation is the same as the one done for data messages, and to avoid
|
// operation is the same as the one done for data messages, and to avoid
|
||||||
// code duplication the handler is just invoked using the address of this
|
// code duplication the handler is just invoked using the address of this
|
||||||
// solver Actor as the the sender is not important for this update.
|
// solver Actor as the the sender is not important for this update. However,
|
||||||
|
// if the information is missing from the message, no data file should be
|
||||||
|
// loaded. It is necessary to convert the content to a string since the
|
||||||
|
// JSON library only sees the string and not its length before it has been
|
||||||
|
// unwrapped.
|
||||||
|
|
||||||
if( TheProblem.contains( DataFileMessage::Keys::DataFile ) &&
|
if( TheProblem.contains( DataFileMessage::Keys::DataFile ) &&
|
||||||
TheProblem.contains( DataFileMessage::Keys::NewData ) )
|
TheProblem.contains( DataFileMessage::Keys::NewData ) )
|
||||||
@ -255,7 +259,8 @@ void AMPLSolver::SolveProblem(
|
|||||||
// supported as values.
|
// supported as values.
|
||||||
|
|
||||||
for( const auto & [ TheName, MetricValue ] :
|
for( const auto & [ TheName, MetricValue ] :
|
||||||
Solver::MetricValueType( TheContext.at( Solver::ExecutionContext ) ) )
|
Solver::MetricValueType( TheContext.at(
|
||||||
|
Solver::ApplicationExecutionContext::Keys::ExecutionContext ) ) )
|
||||||
SetAMPLParameter( TheName, MetricValue );
|
SetAMPLParameter( TheName, MetricValue );
|
||||||
|
|
||||||
// Setting the given objective as the active objective and all other
|
// Setting the given objective as the active objective and all other
|
||||||
@ -264,8 +269,10 @@ void AMPLSolver::SolveProblem(
|
|||||||
|
|
||||||
std::string OptimisationGoal;
|
std::string OptimisationGoal;
|
||||||
|
|
||||||
if( TheContext.contains( Solver::ObjectiveFunctionLabel ) )
|
if( TheContext.contains(
|
||||||
OptimisationGoal = TheContext.at( Solver::ObjectiveFunctionLabel );
|
Solver::ApplicationExecutionContext::Keys::ObjectiveFunctionLabel ) )
|
||||||
|
OptimisationGoal = TheContext.at(
|
||||||
|
Solver::ApplicationExecutionContext::Keys::ObjectiveFunctionLabel );
|
||||||
else if( !DefaultObjectiveFunction.empty() )
|
else if( !DefaultObjectiveFunction.empty() )
|
||||||
OptimisationGoal = DefaultObjectiveFunction;
|
OptimisationGoal = DefaultObjectiveFunction;
|
||||||
else
|
else
|
||||||
@ -337,7 +344,8 @@ void AMPLSolver::SolveProblem(
|
|||||||
// application execution context has the deployment flag set.
|
// application execution context has the deployment flag set.
|
||||||
|
|
||||||
Solver::Solution::VariableValuesType VariableValues;
|
Solver::Solution::VariableValuesType VariableValues;
|
||||||
bool DeploymentFlagSet = TheContext.at( DeploymentFlag ).get<bool>();
|
bool DeploymentFlagSet
|
||||||
|
= TheContext.at( Solver::Solution::Keys::DeploymentFlag ).get<bool>();
|
||||||
|
|
||||||
for( auto Variable : ProblemDefinition.getVariables() )
|
for( auto Variable : ProblemDefinition.getVariables() )
|
||||||
{
|
{
|
||||||
@ -351,7 +359,8 @@ void AMPLSolver::SolveProblem(
|
|||||||
// The found solution can then be returned to the requesting actor or topic
|
// The found solution can then be returned to the requesting actor or topic
|
||||||
|
|
||||||
Send( Solver::Solution(
|
Send( Solver::Solution(
|
||||||
TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(),
|
TheContext.at(
|
||||||
|
Solver::Solution::Keys::TimeStamp ).get< Solver::TimePointType >(),
|
||||||
OptimisationGoal, ObjectiveValues, VariableValues,
|
OptimisationGoal, ObjectiveValues, VariableValues,
|
||||||
DeploymentFlagSet
|
DeploymentFlagSet
|
||||||
), TheRequester );
|
), TheRequester );
|
||||||
|
@ -40,7 +40,7 @@ namespace NebulOuS
|
|||||||
void MetricUpdater::AddMetricSubscription(
|
void MetricUpdater::AddMetricSubscription(
|
||||||
const MetricTopic & MetricDefinitions, const Address OptimiserController )
|
const MetricTopic & MetricDefinitions, const Address OptimiserController )
|
||||||
{
|
{
|
||||||
JSON TheMetrics = MetricDefinitions.at( MetricList );
|
JSON TheMetrics = MetricDefinitions.at( MetricTopic::Keys::MetricList );
|
||||||
|
|
||||||
if( TheMetrics.is_array() )
|
if( TheMetrics.is_array() )
|
||||||
{
|
{
|
||||||
@ -59,16 +59,12 @@ void MetricUpdater::AddMetricSubscription(
|
|||||||
|
|
||||||
TheMetricNames.insert( MetricRecordPointer->first );
|
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 since this new metric has yet to receive
|
|
||||||
// its first value
|
|
||||||
|
|
||||||
if( MetricAdded )
|
if( MetricAdded )
|
||||||
{
|
{
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||||
std::string( MetricValueRootString ) + MetricRecordPointer->first ),
|
std::string( MetricValueUpdate::MetricValueRootString )
|
||||||
|
+ MetricRecordPointer->first ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
AllMetricValuesSet = false;
|
AllMetricValuesSet = false;
|
||||||
@ -85,7 +81,7 @@ void MetricUpdater::AddMetricSubscription(
|
|||||||
{
|
{
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
std::string( MetricValueRootString ) + TheMetric ),
|
std::string( MetricValueUpdate::MetricValueRootString ) + TheMetric ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
MetricValues.erase( TheMetric );
|
MetricValues.erase( TheMetric );
|
||||||
@ -138,14 +134,16 @@ void MetricUpdater::UpdateMetricValue(
|
|||||||
{
|
{
|
||||||
Theron::AMQ::TopicName TheTopic
|
Theron::AMQ::TopicName TheTopic
|
||||||
= TheMetricTopic.AsString().erase( 0,
|
= TheMetricTopic.AsString().erase( 0,
|
||||||
NebulOuS::MetricValueRootString.size() );
|
MetricValueUpdate::MetricValueRootString.size() );
|
||||||
|
|
||||||
if( MetricValues.contains( TheTopic ) )
|
if( MetricValues.contains( TheTopic ) )
|
||||||
{
|
{
|
||||||
MetricValues.at( TheTopic ) = TheMetricValue.at( NebulOuS::ValueLabel );
|
MetricValues.at( TheTopic )
|
||||||
|
= TheMetricValue.at( MetricValueUpdate::Keys::ValueLabel );
|
||||||
|
|
||||||
ValidityTime = std::max( ValidityTime,
|
ValidityTime = std::max( ValidityTime,
|
||||||
TheMetricValue.at( NebulOuS::TimePoint ).get< Solver::TimePointType >() );
|
TheMetricValue.at(
|
||||||
|
MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +181,8 @@ void MetricUpdater::SLOViolationHandler(
|
|||||||
[](const auto & MetricValue){ return MetricValue.is_null(); } ))) )
|
[](const auto & MetricValue){ return MetricValue.is_null(); } ))) )
|
||||||
{
|
{
|
||||||
Send( Solver::ApplicationExecutionContext(
|
Send( Solver::ApplicationExecutionContext(
|
||||||
SeverityMessage.at( NebulOuS::TimePoint ).get< Solver::TimePointType >(),
|
SeverityMessage.at(
|
||||||
|
MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >(),
|
||||||
MetricValues, true
|
MetricValues, true
|
||||||
), TheSolverManager );
|
), TheSolverManager );
|
||||||
|
|
||||||
@ -250,17 +249,17 @@ MetricUpdater::MetricUpdater( const std::string UpdaterName,
|
|||||||
|
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||||
NebulOuS::MetricSubscriptions ),
|
MetricTopic::AMQTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||||
NebulOuS::ReconfigurationTopic ),
|
ReconfigurationMessage::AMQTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||||
NebulOuS::SLOViolationTopic ),
|
SLOViolation::AMQTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,24 +274,25 @@ MetricUpdater::~MetricUpdater()
|
|||||||
{
|
{
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
NebulOuS::MetricSubscriptions ),
|
MetricTopic::AMQTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
NebulOuS::ReconfigurationTopic ),
|
ReconfigurationMessage::AMQTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
NebulOuS::SLOViolationTopic ),
|
SLOViolation::AMQTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
|
|
||||||
std::ranges::for_each( std::views::keys( MetricValues ),
|
std::ranges::for_each( std::views::keys( MetricValues ),
|
||||||
[this]( const Theron::AMQ::TopicName & TheMetricTopic ){
|
[this]( const Theron::AMQ::TopicName & TheMetricTopic ){
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
std::string( MetricValueRootString ) + TheMetricTopic ),
|
std::string( MetricValueUpdate::MetricValueRootString )
|
||||||
|
+ TheMetricTopic ),
|
||||||
GetSessionLayerAddress() );
|
GetSessionLayerAddress() );
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -68,74 +68,6 @@ using JSON = nlohmann::json; // Short form name space
|
|||||||
|
|
||||||
namespace NebulOuS
|
namespace NebulOuS
|
||||||
{
|
{
|
||||||
/*==============================================================================
|
|
||||||
|
|
||||||
Basic interface definitions
|
|
||||||
|
|
||||||
==============================================================================*/
|
|
||||||
//
|
|
||||||
// Definitions for the terminology to facilitate changing the lables of the
|
|
||||||
// various message labels without changing the code. The definitions are
|
|
||||||
// compile time constants and as such should not lead to any run-time overhead.
|
|
||||||
// The JSON attribute names may be found under the "Predicted monitoring
|
|
||||||
// metrics" section on the Wiki page [1].
|
|
||||||
|
|
||||||
constexpr std::string_view ValueLabel = "metricValue";
|
|
||||||
constexpr std::string_view TimePoint = "predictionTime";
|
|
||||||
|
|
||||||
// The topic used for receiving the message(s) defining the metrics of the
|
|
||||||
// application execution context as published by the Optimiser Controller is
|
|
||||||
// defined next.
|
|
||||||
|
|
||||||
constexpr std::string_view MetricSubscriptions
|
|
||||||
= "eu.nebulouscloud.optimiser.controller.metric_list";
|
|
||||||
|
|
||||||
// The JSON message attribute for the list of metrics is another JSON object
|
|
||||||
// stored under the following key, see the Event type III defined in
|
|
||||||
// https://158.39.75.54/projects/nebulous-collaboration-hub/wiki/slo-severity-based-violation-detector
|
|
||||||
// where the name of the metric is defined under as sub-key.
|
|
||||||
|
|
||||||
constexpr std::string_view MetricList = "metrics";
|
|
||||||
|
|
||||||
// The metric value messages will be published on different topics and to
|
|
||||||
// check if an inbound message is from a metric value topic, it is necessary
|
|
||||||
// to test against the base string for the metric value topics according to
|
|
||||||
// the Wiki-page [1]
|
|
||||||
|
|
||||||
constexpr std::string_view MetricValueRootString
|
|
||||||
= "eu.nebulouscloud.monitoring.predicted.";
|
|
||||||
|
|
||||||
// The SLO violation detector will publish a message when a reconfiguration is
|
|
||||||
// deamed necessary for a future time point called "Event type V" on the wiki
|
|
||||||
// page [3]. The event contains a probability for at least one of the SLOs
|
|
||||||
// being violated at the predicted time point. It is not clear if the assessment
|
|
||||||
// is being made by the SLO violation detector at every new metric prediction,
|
|
||||||
// or if this event is only signalled when the probability is above some
|
|
||||||
// internal threshold of the SLO violation detector.
|
|
||||||
//
|
|
||||||
// The current implementation assumes that the latter is the case, and hence
|
|
||||||
// just receiving the message indicates that a new application configuration
|
|
||||||
// should be found given the application execution context as predicted by the
|
|
||||||
// metric values recorded by the Metric Updater. Should this assumption be
|
|
||||||
// wrong, the probability must be compared with some user set threshold for
|
|
||||||
// each message, and to cater for this the probability field will always be
|
|
||||||
// compared to a threshold, currently set to zero to ensure that every event
|
|
||||||
// message will trigger a reconfiguration.
|
|
||||||
//
|
|
||||||
// The messages from the Optimizer Controller will be sent on a topic that
|
|
||||||
// should follow some standard topic convention.
|
|
||||||
|
|
||||||
constexpr std::string_view SLOViolationTopic
|
|
||||||
= "eu.nebulouscloud.monitoring.slo.severity_value";
|
|
||||||
|
|
||||||
// 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 on the following topic.
|
|
||||||
|
|
||||||
constexpr std::string_view ReconfigurationTopic
|
|
||||||
= "eu.nebulouscloud.optimiser.adaptations";
|
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
|
|
||||||
Metric Updater
|
Metric Updater
|
||||||
@ -178,14 +110,12 @@ private:
|
|||||||
|
|
||||||
Solver::TimePointType ValidityTime;
|
Solver::TimePointType ValidityTime;
|
||||||
|
|
||||||
// There is also a flag to indicate when all metric values have received
|
// When an SLO violation message is received the current vector of metric
|
||||||
// values since optimising for a application execution context defiend all
|
// values should be sent as an application execution context (message) to the
|
||||||
// metrics requires that at least one value is received for each metric. This
|
// Solution Manager actor that will invoke a solver to find the optimal
|
||||||
// condition could be tested before sending the request to find a new
|
// configuration for this configuration. The Metric Updater must therefore
|
||||||
// solution, but this means testing all metrics in a linear scan for a
|
// know the address of the Soler Manager, and this must be passed to
|
||||||
// condition that will only happen initially until all metrics have been seen
|
// the constructor.
|
||||||
// and so it is better for the performance if there is a flag to check for
|
|
||||||
// this condition.
|
|
||||||
|
|
||||||
bool AllMetricValuesSet;
|
bool AllMetricValuesSet;
|
||||||
|
|
||||||
@ -204,18 +134,35 @@ private:
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// The topic used for receiving the message(s) defining the metrics of the
|
||||||
|
// application execution context as published by the Optimiser Controller
|
||||||
|
// is the following
|
||||||
|
|
||||||
|
static constexpr std::string_view AMQTopic
|
||||||
|
= "eu.nebulouscloud.optimiser.controller.metric_list";
|
||||||
|
|
||||||
|
// The EXN middleware sending the AMQP messages for the other component
|
||||||
|
// of the NebulOuS project only sends JSON objects, meaning that the list
|
||||||
|
// of metric names to subscribe is sent as a JSON array, but it must be
|
||||||
|
// embedded in a map with a single key, see the message format described
|
||||||
|
// in the Wiki page at
|
||||||
|
// https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/1-optimiser-controller#controller-to-metric-updater-and-ems-metric-list
|
||||||
|
|
||||||
|
struct Keys
|
||||||
|
{
|
||||||
|
static constexpr std::string_view MetricList = "metrics";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
MetricTopic( void )
|
MetricTopic( void )
|
||||||
: JSONTopicMessage( std::string( MetricSubscriptions ) )
|
: JSONTopicMessage( AMQTopic )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
MetricTopic( const MetricTopic & Other )
|
MetricTopic( const MetricTopic & Other )
|
||||||
: JSONTopicMessage( Other )
|
: JSONTopicMessage( Other )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// MetricTopic( const JSONTopicMessage & Other )
|
|
||||||
// : JSONTopicMessage( Other )
|
|
||||||
// {}
|
|
||||||
|
|
||||||
virtual ~MetricTopic() = default;
|
virtual ~MetricTopic() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -241,6 +188,25 @@ private:
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// The metric value messages will be published on different topics and to
|
||||||
|
// check if an inbound message is from a metric value topic, it is
|
||||||
|
// necessary to test against the base string for the metric value topics
|
||||||
|
// according to the Wiki-page describing the Type II message:
|
||||||
|
// https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/monitoringdata-interface#type-ii-messages-predicted-monitoring-metrics
|
||||||
|
|
||||||
|
static constexpr std::string_view MetricValueRootString
|
||||||
|
= "eu.nebulouscloud.monitoring.predicted.";
|
||||||
|
|
||||||
|
// Only two of the fields in this message will be looked up and stored
|
||||||
|
// in the current application context map.
|
||||||
|
|
||||||
|
struct Keys
|
||||||
|
{
|
||||||
|
static constexpr std::string_view
|
||||||
|
ValueLabel = "metricValue",
|
||||||
|
TimePoint = "predictionTime";
|
||||||
|
};
|
||||||
|
|
||||||
MetricValueUpdate( void )
|
MetricValueUpdate( void )
|
||||||
: JSONWildcardMessage( std::string( MetricValueRootString ) )
|
: JSONWildcardMessage( std::string( MetricValueRootString ) )
|
||||||
{}
|
{}
|
||||||
@ -263,21 +229,48 @@ private:
|
|||||||
// SLO violations
|
// SLO violations
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// The SLO Violation detector publishes an event to indicate that at least
|
// The SLO violation detector will publish a message when a reconfiguration is
|
||||||
// one of the constraints for the application deployment will be violated in
|
// deamed necessary for a future time point called "Event type VI" on the wiki
|
||||||
// the predicted future, and that the search for a new solution should start.
|
// page: https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/slo-severity-based-violation-detector#output-event-type-vi
|
||||||
// This will trigger the the publication of the Solver's Application Execution
|
// The event contains a probability for at least one of the SLOs being
|
||||||
// context message. The context message will contain the current status of the
|
// violated at the predicted time point. It is not clear if the assessment
|
||||||
// metric values, and trigger a solver to find a new, optimal variable
|
// is being made by the SLO violation detector at every new metric prediction,
|
||||||
// assignment to be deployed to resolve the identified problem.
|
// or if this event is only signalled when the probability is above some
|
||||||
|
// internal threshold of the SLO violation detector.
|
||||||
|
//
|
||||||
|
// The current implementation assumes that the latter is the case, and hence
|
||||||
|
// just receiving the message indicates that a new application configuration
|
||||||
|
// should be found given the application execution context as predicted by the
|
||||||
|
// metric values recorded by the Metric Updater. Should this assumption be
|
||||||
|
// wrong, the probability must be compared with some user set threshold for
|
||||||
|
// each message, and to cater for this the probability field will always be
|
||||||
|
// compared to a threshold, currently set to zero to ensure that every event
|
||||||
|
// message will trigger a reconfiguration.
|
||||||
|
|
||||||
class SLOViolation
|
class SLOViolation
|
||||||
: public Theron::AMQ::JSONTopicMessage
|
: public Theron::AMQ::JSONTopicMessage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// The messages from the SLO Violation Detector will be sent on a topic that
|
||||||
|
// should follow some standard topic convention.
|
||||||
|
|
||||||
|
static constexpr std::string_view AMQTopic
|
||||||
|
= "eu.nebulouscloud.monitoring.slo.severity_value";
|
||||||
|
|
||||||
|
// The only information taken from this detction message is the prediction
|
||||||
|
// time which will be used as the time for the application's execution
|
||||||
|
// context when this is forwarded to the solvers for processing.
|
||||||
|
|
||||||
|
struct Keys
|
||||||
|
{
|
||||||
|
static constexpr std::string_view TimePoint = "predictionTime";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
SLOViolation( void )
|
SLOViolation( void )
|
||||||
: JSONTopicMessage( std::string( SLOViolationTopic ) )
|
: JSONTopicMessage( AMQTopic )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SLOViolation( const SLOViolation & Other )
|
SLOViolation( const SLOViolation & Other )
|
||||||
@ -311,18 +304,28 @@ private:
|
|||||||
|
|
||||||
bool ReconfigurationInProgress;
|
bool ReconfigurationInProgress;
|
||||||
|
|
||||||
// When the reconfiguration has been done and the Optimizer Controller
|
// When a reconfiguration has been enacted by the Optimiser Controller and
|
||||||
// confirms that the application is running in a new configuration, it will
|
// a new configuration is confirmed to be running on the new platofrm, it
|
||||||
// send a reconfiguration completed message. This message will just be a
|
// will send a message to inform all other components that the
|
||||||
// JSON message.
|
// 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
|
class ReconfigurationMessage
|
||||||
: public Theron::AMQ::JSONTopicMessage
|
: public Theron::AMQ::JSONTopicMessage
|
||||||
{
|
{
|
||||||
public:
|
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 )
|
ReconfigurationMessage( void )
|
||||||
: JSONTopicMessage( std::string( ReconfigurationTopic ) )
|
: JSONTopicMessage( AMQTopic )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
ReconfigurationMessage( const ReconfigurationMessage & Other )
|
ReconfigurationMessage( const ReconfigurationMessage & Other )
|
||||||
|
156
Solver.hpp
156
Solver.hpp
@ -79,52 +79,6 @@ public:
|
|||||||
// Application Execution Context
|
// Application Execution Context
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// The message is defined as a JSON message representing an attribute-value
|
|
||||||
// object. The attributes expected are defined as constant strings so that
|
|
||||||
// the actual textual representation can be changed without changing the code
|
|
||||||
//
|
|
||||||
// "Timestamp" : This is the field giving the implicit order of the
|
|
||||||
// different application execution execution contexts waiting for being
|
|
||||||
// solved when there are more requests than there are solvers available to
|
|
||||||
// work on the different problems.
|
|
||||||
|
|
||||||
static constexpr std::string_view TimeStamp = "Timestamp";
|
|
||||||
|
|
||||||
// There is also a definition for the objective function label since a multi-
|
|
||||||
// objective optimisation problem can have multiple objective functions and
|
|
||||||
// the solution is found for only one of these functions at the time even
|
|
||||||
// though all objective function values will be returned with the solution,
|
|
||||||
// the solution will maximise only the objective function whose label is
|
|
||||||
// given in the application execution context request message.
|
|
||||||
//
|
|
||||||
// The Application Execution Cntext message may contain the name of the
|
|
||||||
// objective function to maximise. If so, this should be stored under the
|
|
||||||
// key name indicated here. However, if the objective function name is not
|
|
||||||
// given, the default objective function is used. The default objective
|
|
||||||
// function will be named when defining the optimisation problem.
|
|
||||||
|
|
||||||
static constexpr std::string_view
|
|
||||||
ObjectiveFunctionLabel = "ObjectiveFunction";
|
|
||||||
|
|
||||||
// Finally, there is another JSON object that defines all the metric name and
|
|
||||||
// value pairs that define the actual execution context. Note that there must
|
|
||||||
// be at least one metric-value pair for the request to be valid.
|
|
||||||
|
|
||||||
static constexpr std::string_view ExecutionContext = "ExecutionContext";
|
|
||||||
|
|
||||||
// Finally, the execution context can come from the Metric Collector actor
|
|
||||||
// as a consequence of an SLO Violation being detected. In this case the
|
|
||||||
// optimised solution found by the solver should trigger a reconfiguration.
|
|
||||||
// However, various application execution context can also be tried for
|
|
||||||
// simulating future events and to investigate which configuration would be
|
|
||||||
// the best for these situations. In this case the optimised solution should
|
|
||||||
// not reconfigure the running application. For this reason there is a flag
|
|
||||||
// in the message indicating whether the solution should be deployed, and
|
|
||||||
// its default value is 'false' to prevent solutions form accidentially being
|
|
||||||
// deployed.
|
|
||||||
|
|
||||||
static constexpr std::string_view DeploymentFlag = "DeploySolution";
|
|
||||||
|
|
||||||
// To ensure that the execution context is correctly provided by the senders
|
// To ensure that the execution context is correctly provided by the senders
|
||||||
// The expected metric value structure is defined as a type based on the
|
// The expected metric value structure is defined as a type based on the
|
||||||
// standard unsorted map based on a JSON value object since this can hold
|
// standard unsorted map based on a JSON value object since this can hold
|
||||||
@ -161,6 +115,48 @@ public:
|
|||||||
static constexpr std::string_view AMQTopic
|
static constexpr std::string_view AMQTopic
|
||||||
= "eu.nebulouscloud.optimiser.solver.context";
|
= "eu.nebulouscloud.optimiser.solver.context";
|
||||||
|
|
||||||
|
// The keys used in the JSON message to send are defined first:
|
||||||
|
//
|
||||||
|
// "Timestamp" : This is the field giving the implicit order of the
|
||||||
|
// different application execution execution contexts waiting for being
|
||||||
|
// solved when there are more requests than there are solvers available
|
||||||
|
// to work on the different problems.
|
||||||
|
// "ObjectFunction" : There is also a definition for the objective function
|
||||||
|
// label since a multi-objective optimisation problem can have multiple
|
||||||
|
// objective functions and the solution is found for only one of these
|
||||||
|
// functions at the time even though all objective function values will
|
||||||
|
// be returned with the solution, the solution will maximise only the
|
||||||
|
// objective function whose label is given in the application execution
|
||||||
|
// context request message. The Application Execution Cntext message may
|
||||||
|
// contain the name of the objective function to maximise. If so, this
|
||||||
|
// should be stored under the key name indicated here. However, if the
|
||||||
|
// objective function name is not given, the default objective function
|
||||||
|
// is used. The default objective function will be named when defining
|
||||||
|
// the optimisation problem.
|
||||||
|
// "ExecutionContext" : Defines all the metric name and value pairs that
|
||||||
|
// define the actual execution context. Note that there must be at least
|
||||||
|
// one metric-value pair for the request to be valid.
|
||||||
|
// "DeploySolution" : The execution context can come from the Metric
|
||||||
|
// Collector actor as a consequence of an SLO Violation being detected.
|
||||||
|
// In this case the optimised solution found by the solver should trigger
|
||||||
|
// a reconfiguration. However, various application execution context can
|
||||||
|
// also be tried for simulating future events and to investigate which
|
||||||
|
// configuration would be the best for these situations. In this case the
|
||||||
|
// optimised solution should not reconfigure the running application. For
|
||||||
|
// this reason there is a flag in the message indicating whether the
|
||||||
|
// solution should be deployed, and its default value is 'false' to
|
||||||
|
// prevent solutions form accidentially being deployed.
|
||||||
|
|
||||||
|
|
||||||
|
struct Keys
|
||||||
|
{
|
||||||
|
static constexpr std::string_view
|
||||||
|
TimeStamp = "Timestamp",
|
||||||
|
ObjectiveFunctionLabel = "ObjectiveFunction",
|
||||||
|
ExecutionContext = "ExecutionContext",
|
||||||
|
DeploymentFlag = "DeploySolution";
|
||||||
|
};
|
||||||
|
|
||||||
// The full constructor takes the time point, the objective function to
|
// The full constructor takes the time point, the objective function to
|
||||||
// solve for, and the application's execution context as the metric map
|
// solve for, and the application's execution context as the metric map
|
||||||
|
|
||||||
@ -169,10 +165,10 @@ public:
|
|||||||
const MetricValueType & TheContext,
|
const MetricValueType & TheContext,
|
||||||
bool DeploySolution = false )
|
bool DeploySolution = false )
|
||||||
: JSONTopicMessage( std::string( AMQTopic ),
|
: JSONTopicMessage( std::string( AMQTopic ),
|
||||||
{ { std::string( TimeStamp ), MicroSecondTimePoint },
|
{ { Keys::TimeStamp, MicroSecondTimePoint },
|
||||||
{ std::string( ObjectiveFunctionLabel ), ObjectiveFunctionID },
|
{ Keys::ObjectiveFunctionLabel, ObjectiveFunctionID },
|
||||||
{ std::string( ExecutionContext ), TheContext },
|
{ Keys::ExecutionContext, TheContext },
|
||||||
{ std::string( DeploymentFlag ), DeploySolution }
|
{ Keys::DeploymentFlag, DeploySolution }
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
// The constructor omitting the objective function identifier is similar
|
// The constructor omitting the objective function identifier is similar
|
||||||
@ -183,9 +179,9 @@ public:
|
|||||||
const MetricValueType & TheContext,
|
const MetricValueType & TheContext,
|
||||||
bool DeploySolution = false )
|
bool DeploySolution = false )
|
||||||
: JSONTopicMessage( std::string( AMQTopic ),
|
: JSONTopicMessage( std::string( AMQTopic ),
|
||||||
{ { std::string( TimeStamp ), MicroSecondTimePoint },
|
{ { Keys::TimeStamp, MicroSecondTimePoint },
|
||||||
{ std::string( ExecutionContext ), TheContext },
|
{ Keys::ExecutionContext, TheContext },
|
||||||
{ std::string( DeploymentFlag ), DeploySolution }
|
{ Keys::DeploymentFlag, DeploySolution }
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
// The copy constructor simply passes the job on to the JSON Topic
|
// The copy constructor simply passes the job on to the JSON Topic
|
||||||
@ -206,9 +202,9 @@ public:
|
|||||||
virtual ~ApplicationExecutionContext() = default;
|
virtual ~ApplicationExecutionContext() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The handler for this message is virtual as it where the real action will
|
// The handler for this message is virtual as it is where the real action
|
||||||
// happen and the search for the optimal solution will hopefully lead to a
|
// will happen and the search for the optimal solution will hopefully lead
|
||||||
// feasible soltuion that can be returned to the sender of the applicaton
|
// to a feasible soltuion that can be returned to the sender of the applicaton
|
||||||
// context.
|
// context.
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -241,26 +237,47 @@ public:
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// There are some aliases that can be used in other Actors processing this
|
||||||
|
// message in order to ensure portability
|
||||||
|
|
||||||
using ObjectiveValuesType = MetricValueType;
|
using ObjectiveValuesType = MetricValueType;
|
||||||
using VariableValuesType = MetricValueType;
|
using VariableValuesType = MetricValueType;
|
||||||
|
|
||||||
static constexpr std::string_view ObjectiveValues = "ObjectiveValues";
|
// The topic for which the message is published is defined first
|
||||||
static constexpr std::string_view VariableValues = "VariableValues";
|
|
||||||
|
|
||||||
static constexpr std::string_view AMQTopic
|
static constexpr std::string_view AMQTopic
|
||||||
= "eu.nebulouscloud.optimiser.solver.solution";
|
= "eu.nebulouscloud.optimiser.solver.solution";
|
||||||
|
|
||||||
|
// Most of the message keys are the same as for the application execution
|
||||||
|
// context, but there are two new:
|
||||||
|
//
|
||||||
|
// "ObjectiveValues" : This holds a map of objective function names and
|
||||||
|
// their values under the currently found solution which is optimised
|
||||||
|
// for the given objective function or the default objective function.
|
||||||
|
// The other objective values is useful if one is searching for the
|
||||||
|
// Pareto front of the problem.
|
||||||
|
// "VariableValues" : This key is a map holding the variable names and
|
||||||
|
// their values found by the solver for the optimal solution. This is
|
||||||
|
// used to reconfigure the application.
|
||||||
|
|
||||||
|
struct Keys : public ApplicationExecutionContext::Keys
|
||||||
|
{
|
||||||
|
static constexpr std::string_view
|
||||||
|
ObjectiveValues = "ObjectiveValues",
|
||||||
|
VariableValues = "VariableValues";
|
||||||
|
};
|
||||||
|
|
||||||
Solution( const TimePointType MicroSecondTimePoint,
|
Solution( const TimePointType MicroSecondTimePoint,
|
||||||
const std::string ObjectiveFunctionID,
|
const std::string ObjectiveFunctionID,
|
||||||
const ObjectiveValuesType & TheObjectiveValues,
|
const ObjectiveValuesType & TheObjectiveValues,
|
||||||
const VariableValuesType & TheVariables,
|
const VariableValuesType & TheVariables,
|
||||||
bool DeploySolution )
|
bool DeploySolution )
|
||||||
: JSONTopicMessage( std::string( AMQTopic ) ,
|
: JSONTopicMessage( std::string( AMQTopic ) ,
|
||||||
{ { std::string( TimeStamp ), MicroSecondTimePoint },
|
{ { Keys::TimeStamp, MicroSecondTimePoint },
|
||||||
{ std::string( ObjectiveFunctionLabel ), ObjectiveFunctionID },
|
{ Keys::ObjectiveFunctionLabel, ObjectiveFunctionID },
|
||||||
{ std::string( ObjectiveValues ) , TheObjectiveValues },
|
{ Keys::ObjectiveValues, TheObjectiveValues },
|
||||||
{ std::string( VariableValues ), TheVariables },
|
{ Keys::VariableValues, TheVariables },
|
||||||
{ std::string( DeploymentFlag ), DeploySolution }
|
{ Keys::DeploymentFlag, DeploySolution }
|
||||||
} )
|
} )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -335,6 +352,11 @@ public:
|
|||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||||
OptimisationProblem::AMQTopic
|
OptimisationProblem::AMQTopic
|
||||||
), GetSessionLayerAddress() );
|
), GetSessionLayerAddress() );
|
||||||
|
|
||||||
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||||
|
ApplicationExecutionContext::AMQTopic
|
||||||
|
), GetSessionLayerAddress() );
|
||||||
}
|
}
|
||||||
|
|
||||||
Solver() = delete;
|
Solver() = delete;
|
||||||
@ -342,10 +364,18 @@ public:
|
|||||||
virtual ~Solver()
|
virtual ~Solver()
|
||||||
{
|
{
|
||||||
if( HasNetwork() )
|
if( HasNetwork() )
|
||||||
|
{
|
||||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
OptimisationProblem::AMQTopic
|
OptimisationProblem::AMQTopic
|
||||||
), GetSessionLayerAddress() );
|
), GetSessionLayerAddress() );
|
||||||
|
|
||||||
|
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||||
|
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||||
|
ApplicationExecutionContext::AMQTopic
|
||||||
|
), GetSessionLayerAddress() );
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -174,7 +174,8 @@ private:
|
|||||||
const Address TheRequester )
|
const Address TheRequester )
|
||||||
{
|
{
|
||||||
ContextQueue.emplace(
|
ContextQueue.emplace(
|
||||||
TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(),
|
TheContext.at( Solver::ApplicationExecutionContext::Keys::TimeStamp
|
||||||
|
).get< Solver::TimePointType >(),
|
||||||
TheContext );
|
TheContext );
|
||||||
|
|
||||||
DispatchToSolvers();
|
DispatchToSolvers();
|
||||||
|
Loading…
Reference in New Issue
Block a user