Skip to content

Writing BtrPlace extensions

Fabien Hermenier edited this page Nov 5, 2019 · 9 revisions

BtrPlace is already bundled with several Views and constraints to accomodate with common concerns. In some circumstances, the library may however lack of solutions. We discuss here how to create extensions.

Generalities

BtrPlace is designed around the bridge pattern to dissociate the user API which expresses a constraint or a view and the solver API which expresses the enforcement algorithm using the choco API. Furthermore, it is pretty sweet to be able to serialise an instance into JSON to be able to replay it for debugging purpose.

Architecture

As a result, when you develop a view or a constraint, you will have to develop 3 components:

  1. the user-side code. For a view, this exhibits a part of a cluster state. For a constraint, this will exhibit the desired state.
  2. The serialisation code to produce a JSON version of your component.
  3. the solver-side code. This consists in customizing the default Choco CSP to express your requirements

The user-side API to solve-side API mapping process

The mapping between a user-level constraint or view and the choco-level equivalent is done using the ChocoMapper that is accessible from any ChocoScheduler Parameters. Internally, the Reflection API is used to instantiate the Choco object.

The choco CSP generation process

The Choco CSP is generated incrementally from a core problem called the ReconfigurationProblem

  1. The ReconfigurationProblem is created. It models that the nodes are free to change their state using NodeTransitions. The next state for every VM is however forced. By default, their state is unchanged but constraints derived from VMTransitions can force the state-change. The basic model is in charge of accounting the number of VMs per node and scheduling the actions so that they succeed according to the element states.
  2. The ChocoViews associated to the ModelView are instantiated.
  3. The CSP code for the ChocoView is created through their inject() method. Usually the views create variables and some relations between them. The injection ordering satisfies the [declared dependencies] (http://www.btrplace.org/apidocs/org/btrplace/scheduler/choco/view/ChocoView.html#getDependencies--). Once injected, you will be able to access them from ReconfigurationProbleem.getView().
  4. The CSP code for the constraints is injected through their inject() method. At this moment, it is then safe to access any ChocoView variable.
  5. The Choco views are queried a second time using their beforeSolve() method. This enables to some code optimisation. Again the dependencies between the views are considered.

Writing a View

A view deserves the purpose of declaring a new dimension or concern for a cluster. For example, to state the resource usage for a specific component (Network, GPU, etc.) that are not present by default.

User-level API

Every view derives from ModelView.

  • It must provide an identifier
  • It must be Copyable
  • It must implements equals() and hashcode()

It is also convenient to implement a static get() method to access the view inside a model once attached (example)

Remember that a view reports the initial cluster state. Accordingly, there is no notion of interpretation about the current situation.

Choco-level API

The Choco version of a constraint implements ChocoView. It expects the user-side view as a unique constructor parameter.

  • Implement the inject() view to provide here the view specific variables and basic relations between them.
  • If the view requires variables from another view, declare the dependencies in getDependencies()
  • If some late optimisation is possible, implement beforeSolve()
  • If the view can perform some problem simplification in the repair mode, it must implements getMisplaced() to state the supposed mis-placed VMs. Be careful, if this method returns all the VMs in the problem, then the repair mode will be counter-effective as it will prevent any optimisation.
  • insertAction() may be implemented if, once a solution computed, this view may modify the reconfiguration plan with view specific events.

JSON Serialisation

Writing a Constraint

An instance may be composed of any number of satisfaction-oriented constraints and one optimisation constraint.

User-level API

Satisfaction-oriented constraints

Each constraint derives from SatConstraint but SimpleConstaint can be used to reduce the code to write.

  • Implements equals() and hashcode() to provide comparison capabilities
  • Write a constraint that manipulates the minimum number of elements. For example, while it would make sense to have a VM-host anti-affinity that can take multiple VM into parameters, the underlying semantic is still per VM.
  • Make the constraint as stateless as possible (there is a known caveat with the continuous mode). Typically, it should not be possible to change the set of managed VMs or nodes.
  • Each constraint provides a Checker that will be called to analyse a Model and state if the constraint is satisfied. If the constraint is discrete, it is enough to implement endsWith(). If the constraint is continuous, it is also required to implement every method signalling the beginning or the end of an action that may alter the constraint satisfaction.

Optimisation constraints

  • An optimisation constraint is not focusing any particular elements.
  • By design, there is no need to provide any viability checker
  • just implement at minimum equals() and hashcode() to ease comparison
  • The constraint should be stateless

Choco-level API

Generalities

  • The constructor must take the user-level constraint as a unique parameter to be instantiable by the ChocoMapper
  • The inject() method will be called with the ReconfigurationProblem that can be customized
  • The getMisplacedVMs() should be implemented to reduce the problem size in the repair mode
  • Remember the restriction mode of the constraint. If it is continuous, it must be satisfied at any moment of time, including the initial state.

About the optimisation constraint

  • Along with the regular injection, it matters to declare the objective variable that will have to be maximised or minimised using the ReconfigurationProblem#setObjective() method
  • It matters to provide a branching heuristic that will state all the decision variables. Choco considers that a problem is solved only if all the decision variables are instantiated. If some meaningful variables are not instantiated once a solution computed, the generation of the ReconfigurationPlan may fail

JSON Serialisation

Modelling tips

Generalities about Choco

The choco library is well documented and provide a wide range of documentations, from tutorials for beginners to technical information for developers.

  • Have a look to Choco documentation and tutorials before starting the modelling process
  • Avoid creating variables when un-needed as they will consume memory
  • ReconfigurationProblem contains a few helpers (make* methods) to create context-specific variables that will be properly initialised.
  • Provide meaningful variable names to help debugging. However, with tens of thousands of variable, the variable labels will use a significant amount of memory. Use ReconfigurationProblem#makeVarLabel() to create them lazily, only when the scheduler verbosity is at least 1.

Clone this wiki locally