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
|
||||
// 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
|
||||
// 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 ) &&
|
||||
TheProblem.contains( DataFileMessage::Keys::NewData ) )
|
||||
@ -255,7 +259,8 @@ void AMPLSolver::SolveProblem(
|
||||
// supported as values.
|
||||
|
||||
for( const auto & [ TheName, MetricValue ] :
|
||||
Solver::MetricValueType( TheContext.at( Solver::ExecutionContext ) ) )
|
||||
Solver::MetricValueType( TheContext.at(
|
||||
Solver::ApplicationExecutionContext::Keys::ExecutionContext ) ) )
|
||||
SetAMPLParameter( TheName, MetricValue );
|
||||
|
||||
// Setting the given objective as the active objective and all other
|
||||
@ -264,8 +269,10 @@ void AMPLSolver::SolveProblem(
|
||||
|
||||
std::string OptimisationGoal;
|
||||
|
||||
if( TheContext.contains( Solver::ObjectiveFunctionLabel ) )
|
||||
OptimisationGoal = TheContext.at( Solver::ObjectiveFunctionLabel );
|
||||
if( TheContext.contains(
|
||||
Solver::ApplicationExecutionContext::Keys::ObjectiveFunctionLabel ) )
|
||||
OptimisationGoal = TheContext.at(
|
||||
Solver::ApplicationExecutionContext::Keys::ObjectiveFunctionLabel );
|
||||
else if( !DefaultObjectiveFunction.empty() )
|
||||
OptimisationGoal = DefaultObjectiveFunction;
|
||||
else
|
||||
@ -337,7 +344,8 @@ void AMPLSolver::SolveProblem(
|
||||
// application execution context has the deployment flag set.
|
||||
|
||||
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() )
|
||||
{
|
||||
@ -350,8 +358,9 @@ void AMPLSolver::SolveProblem(
|
||||
|
||||
// The found solution can then be returned to the requesting actor or topic
|
||||
|
||||
Send( Solver::Solution(
|
||||
TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(),
|
||||
Send( Solver::Solution(
|
||||
TheContext.at(
|
||||
Solver::Solution::Keys::TimeStamp ).get< Solver::TimePointType >(),
|
||||
OptimisationGoal, ObjectiveValues, VariableValues,
|
||||
DeploymentFlagSet
|
||||
), TheRequester );
|
||||
|
@ -40,7 +40,7 @@ namespace NebulOuS
|
||||
void MetricUpdater::AddMetricSubscription(
|
||||
const MetricTopic & MetricDefinitions, const Address OptimiserController )
|
||||
{
|
||||
JSON TheMetrics = MetricDefinitions.at( MetricList );
|
||||
JSON TheMetrics = MetricDefinitions.at( MetricTopic::Keys::MetricList );
|
||||
|
||||
if( TheMetrics.is_array() )
|
||||
{
|
||||
@ -57,18 +57,14 @@ void MetricUpdater::AddMetricSubscription(
|
||||
auto [ MetricRecordPointer, MetricAdded ] = MetricValues.try_emplace(
|
||||
MetricRecord.get<std::string>(), JSON() );
|
||||
|
||||
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
|
||||
TheMetricNames.insert( MetricRecordPointer->first );
|
||||
|
||||
if( MetricAdded )
|
||||
{
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||
std::string( MetricValueRootString ) + MetricRecordPointer->first ),
|
||||
std::string( MetricValueUpdate::MetricValueRootString )
|
||||
+ MetricRecordPointer->first ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
AllMetricValuesSet = false;
|
||||
@ -85,7 +81,7 @@ void MetricUpdater::AddMetricSubscription(
|
||||
{
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
std::string( MetricValueRootString ) + TheMetric ),
|
||||
std::string( MetricValueUpdate::MetricValueRootString ) + TheMetric ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
MetricValues.erase( TheMetric );
|
||||
@ -138,14 +134,16 @@ void MetricUpdater::UpdateMetricValue(
|
||||
{
|
||||
Theron::AMQ::TopicName TheTopic
|
||||
= TheMetricTopic.AsString().erase( 0,
|
||||
NebulOuS::MetricValueRootString.size() );
|
||||
MetricValueUpdate::MetricValueRootString.size() );
|
||||
|
||||
if( MetricValues.contains( TheTopic ) )
|
||||
{
|
||||
MetricValues.at( TheTopic ) = TheMetricValue.at( NebulOuS::ValueLabel );
|
||||
MetricValues.at( TheTopic )
|
||||
= TheMetricValue.at( MetricValueUpdate::Keys::ValueLabel );
|
||||
|
||||
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(); } ))) )
|
||||
{
|
||||
Send( Solver::ApplicationExecutionContext(
|
||||
SeverityMessage.at( NebulOuS::TimePoint ).get< Solver::TimePointType >(),
|
||||
SeverityMessage.at(
|
||||
MetricValueUpdate::Keys::TimePoint ).get< Solver::TimePointType >(),
|
||||
MetricValues, true
|
||||
), TheSolverManager );
|
||||
|
||||
@ -250,17 +249,17 @@ MetricUpdater::MetricUpdater( const std::string UpdaterName,
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||
NebulOuS::MetricSubscriptions ),
|
||||
MetricTopic::AMQTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||
NebulOuS::ReconfigurationTopic ),
|
||||
ReconfigurationMessage::AMQTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||
NebulOuS::SLOViolationTopic ),
|
||||
SLOViolation::AMQTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
}
|
||||
|
||||
@ -275,24 +274,25 @@ MetricUpdater::~MetricUpdater()
|
||||
{
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
NebulOuS::MetricSubscriptions ),
|
||||
MetricTopic::AMQTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
NebulOuS::ReconfigurationTopic ),
|
||||
ReconfigurationMessage::AMQTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
NebulOuS::SLOViolationTopic ),
|
||||
SLOViolation::AMQTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
|
||||
std::ranges::for_each( std::views::keys( MetricValues ),
|
||||
[this]( const Theron::AMQ::TopicName & TheMetricTopic ){
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
std::string( MetricValueRootString ) + TheMetricTopic ),
|
||||
std::string( MetricValueUpdate::MetricValueRootString )
|
||||
+ TheMetricTopic ),
|
||||
GetSessionLayerAddress() );
|
||||
});
|
||||
}
|
||||
|
@ -68,74 +68,6 @@ using JSON = nlohmann::json; // Short form name space
|
||||
|
||||
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
|
||||
@ -178,14 +110,12 @@ private:
|
||||
|
||||
Solver::TimePointType ValidityTime;
|
||||
|
||||
// There is also a flag to indicate when all metric values have received
|
||||
// values since optimising for a application execution context defiend all
|
||||
// metrics requires that at least one value is received for each metric. This
|
||||
// condition could be tested before sending the request to find a new
|
||||
// solution, but this means testing all metrics in a linear scan for a
|
||||
// condition that will only happen initially until all metrics have been seen
|
||||
// and so it is better for the performance if there is a flag to check for
|
||||
// this condition.
|
||||
// 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.
|
||||
|
||||
bool AllMetricValuesSet;
|
||||
|
||||
@ -204,18 +134,35 @@ private:
|
||||
{
|
||||
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 )
|
||||
: JSONTopicMessage( std::string( MetricSubscriptions ) )
|
||||
: JSONTopicMessage( AMQTopic )
|
||||
{}
|
||||
|
||||
MetricTopic( const MetricTopic & Other )
|
||||
: JSONTopicMessage( Other )
|
||||
{}
|
||||
|
||||
// MetricTopic( const JSONTopicMessage & Other )
|
||||
// : JSONTopicMessage( Other )
|
||||
// {}
|
||||
|
||||
virtual ~MetricTopic() = default;
|
||||
};
|
||||
|
||||
@ -241,6 +188,25 @@ private:
|
||||
{
|
||||
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 )
|
||||
: JSONWildcardMessage( std::string( MetricValueRootString ) )
|
||||
{}
|
||||
@ -263,21 +229,48 @@ private:
|
||||
// SLO violations
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
// The SLO Violation detector publishes an event to indicate that at least
|
||||
// one of the constraints for the application deployment will be violated in
|
||||
// the predicted future, and that the search for a new solution should start.
|
||||
// This will trigger the the publication of the Solver's Application Execution
|
||||
// context message. The context message will contain the current status of the
|
||||
// metric values, and trigger a solver to find a new, optimal variable
|
||||
// assignment to be deployed to resolve the identified problem.
|
||||
|
||||
// The SLO violation detector will publish a message when a reconfiguration is
|
||||
// deamed necessary for a future time point called "Event type VI" on the wiki
|
||||
// page: https://openproject.nebulouscloud.eu/projects/nebulous-collaboration-hub/wiki/slo-severity-based-violation-detector#output-event-type-vi
|
||||
// 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.
|
||||
|
||||
class SLOViolation
|
||||
: public Theron::AMQ::JSONTopicMessage
|
||||
{
|
||||
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 )
|
||||
: JSONTopicMessage( std::string( SLOViolationTopic ) )
|
||||
: JSONTopicMessage( AMQTopic )
|
||||
{}
|
||||
|
||||
SLOViolation( const SLOViolation & Other )
|
||||
@ -311,18 +304,28 @@ private:
|
||||
|
||||
bool ReconfigurationInProgress;
|
||||
|
||||
// When the reconfiguration has been done and the Optimizer Controller
|
||||
// confirms that the application is running in a new configuration, it will
|
||||
// send a reconfiguration completed message. This message will just be a
|
||||
// JSON message.
|
||||
// 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( std::string( ReconfigurationTopic ) )
|
||||
: JSONTopicMessage( AMQTopic )
|
||||
{}
|
||||
|
||||
ReconfigurationMessage( const ReconfigurationMessage & Other )
|
||||
|
158
Solver.hpp
158
Solver.hpp
@ -79,52 +79,6 @@ public:
|
||||
// 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
|
||||
// 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
|
||||
@ -161,6 +115,48 @@ public:
|
||||
static constexpr std::string_view AMQTopic
|
||||
= "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
|
||||
// solve for, and the application's execution context as the metric map
|
||||
|
||||
@ -169,10 +165,10 @@ public:
|
||||
const MetricValueType & TheContext,
|
||||
bool DeploySolution = false )
|
||||
: JSONTopicMessage( std::string( AMQTopic ),
|
||||
{ { std::string( TimeStamp ), MicroSecondTimePoint },
|
||||
{ std::string( ObjectiveFunctionLabel ), ObjectiveFunctionID },
|
||||
{ std::string( ExecutionContext ), TheContext },
|
||||
{ std::string( DeploymentFlag ), DeploySolution }
|
||||
{ { Keys::TimeStamp, MicroSecondTimePoint },
|
||||
{ Keys::ObjectiveFunctionLabel, ObjectiveFunctionID },
|
||||
{ Keys::ExecutionContext, TheContext },
|
||||
{ Keys::DeploymentFlag, DeploySolution }
|
||||
}) {}
|
||||
|
||||
// The constructor omitting the objective function identifier is similar
|
||||
@ -183,9 +179,9 @@ public:
|
||||
const MetricValueType & TheContext,
|
||||
bool DeploySolution = false )
|
||||
: JSONTopicMessage( std::string( AMQTopic ),
|
||||
{ { std::string( TimeStamp ), MicroSecondTimePoint },
|
||||
{ std::string( ExecutionContext ), TheContext },
|
||||
{ std::string( DeploymentFlag ), DeploySolution }
|
||||
{ { Keys::TimeStamp, MicroSecondTimePoint },
|
||||
{ Keys::ExecutionContext, TheContext },
|
||||
{ Keys::DeploymentFlag, DeploySolution }
|
||||
}) {}
|
||||
|
||||
// The copy constructor simply passes the job on to the JSON Topic
|
||||
@ -206,9 +202,9 @@ public:
|
||||
virtual ~ApplicationExecutionContext() = default;
|
||||
};
|
||||
|
||||
// The handler for this message is virtual as it where the real action will
|
||||
// happen and the search for the optimal solution will hopefully lead to a
|
||||
// feasible soltuion that can be returned to the sender of the applicaton
|
||||
// The handler for this message is virtual as it is where the real action
|
||||
// will happen and the search for the optimal solution will hopefully lead
|
||||
// to a feasible soltuion that can be returned to the sender of the applicaton
|
||||
// context.
|
||||
|
||||
protected:
|
||||
@ -241,26 +237,47 @@ 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 VariableValuesType = MetricValueType;
|
||||
|
||||
static constexpr std::string_view ObjectiveValues = "ObjectiveValues";
|
||||
static constexpr std::string_view VariableValues = "VariableValues";
|
||||
|
||||
// The topic for which the message is published is defined first
|
||||
|
||||
static constexpr std::string_view AMQTopic
|
||||
= "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,
|
||||
const std::string ObjectiveFunctionID,
|
||||
const ObjectiveValuesType & TheObjectiveValues,
|
||||
const VariableValuesType & TheVariables,
|
||||
bool DeploySolution )
|
||||
: JSONTopicMessage( std::string( AMQTopic ) ,
|
||||
{ { std::string( TimeStamp ), MicroSecondTimePoint },
|
||||
{ std::string( ObjectiveFunctionLabel ), ObjectiveFunctionID },
|
||||
{ std::string( ObjectiveValues ) , TheObjectiveValues },
|
||||
{ std::string( VariableValues ), TheVariables },
|
||||
{ std::string( DeploymentFlag ), DeploySolution }
|
||||
{ { Keys::TimeStamp, MicroSecondTimePoint },
|
||||
{ Keys::ObjectiveFunctionLabel, ObjectiveFunctionID },
|
||||
{ Keys::ObjectiveValues, TheObjectiveValues },
|
||||
{ Keys::VariableValues, TheVariables },
|
||||
{ Keys::DeploymentFlag, DeploySolution }
|
||||
} )
|
||||
{}
|
||||
|
||||
@ -335,6 +352,11 @@ public:
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||
OptimisationProblem::AMQTopic
|
||||
), GetSessionLayerAddress() );
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::Subscription,
|
||||
ApplicationExecutionContext::AMQTopic
|
||||
), GetSessionLayerAddress() );
|
||||
}
|
||||
|
||||
Solver() = delete;
|
||||
@ -342,10 +364,18 @@ public:
|
||||
virtual ~Solver()
|
||||
{
|
||||
if( HasNetwork() )
|
||||
{
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
OptimisationProblem::AMQTopic
|
||||
), GetSessionLayerAddress() );
|
||||
|
||||
Send( Theron::AMQ::NetworkLayer::TopicSubscription(
|
||||
Theron::AMQ::NetworkLayer::TopicSubscription::Action::CloseSubscription,
|
||||
ApplicationExecutionContext::AMQTopic
|
||||
), GetSessionLayerAddress() );
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -174,7 +174,8 @@ private:
|
||||
const Address TheRequester )
|
||||
{
|
||||
ContextQueue.emplace(
|
||||
TheContext.at( Solver::TimeStamp ).get< Solver::TimePointType >(),
|
||||
TheContext.at( Solver::ApplicationExecutionContext::Keys::TimeStamp
|
||||
).get< Solver::TimePointType >(),
|
||||
TheContext );
|
||||
|
||||
DispatchToSolvers();
|
||||
|
Loading…
Reference in New Issue
Block a user