Skip to content
This repository has been archived by the owner on Mar 31, 2023. It is now read-only.

Constraints

David Gross edited this page Jul 24, 2015 · 19 revisions

TBD: it would be best not to use 'target' and 'host' and 'VM'/'virtual machine' interchangeably if they mean the same thing; choose one term and use it consistently, or use distinct terms only when you mean to refer to distinct things. The API uses all three terms, and it's hard for me to distinguish whether or not they really refer to three different things or to different names for the same thing.

One way to match hosts to tasks is by means of host constraints. For example, your task may demand that it is the only task to be running on the host, or that it run on a host of a particular type, or that a certain set of tasks each runs in a different zone or on a different host.

You can decide whether to treat any of these constraints as a hard constraint (a simple boolean thumbs-up or thumbs-down) or as a soft constraint (a more nuanced measure of acceptability).

You can use any of a set of constraints that are built-in to Fenzo, and you may also implement your own constraints if none of these suit your needs.

Evaluating a Constraint

TBD: is this something the reader will ever do, or is evaluation always done internally by the scheduler?

To evaluate whether a task is appropriate to be assigned to a host, according to the criteria of a particular constraint evaluator, call that evaluator’s evaluate() function. You pass that function three parameters:

parameter description
taskRequest a TaskRequest object that describes the task to be assigned
targetVM a VirtualMachineCurrentState object that describes the host
taskTrackerState a TaskTrackerState object that describes the current state of task assignments in the system at large

The function returns a Result object. This object has two methods that you can use to obtain the result of the constraint evaluation:

method description
isSuccessful() returns true if the host satisfies the constraint, or false otherwise
getFailureReason() returns a string that describes why the host did not satisfy the constraint (or an empty string of the host did satisfy the constraint)

Making a Constraint Soft

A ConstraintEvaluator by default implements a hard constraint, that is, one that either is or is not successful, with no middle ground. However, you may want to include a constraint in a fitness calculation (see Scheduling Optimization Plugins), in which case you want the evaluation of the constraint expressed as a value on the 0.0–1.0 fitness scale.

In many cases, this conversion will merely map a constraint failure to 0.0 and a constraint success to 1.0 without any values in-between, but in some cases, such as with the Balanced Host Attribute Constraint described below, when you convert the constraint into a soft constraint this increases its precision considerably.

TBD: Why does BalancedHostAttrConstraint implement its own asSoftConstraint() class method while for other constraints you have to reach outside the class to AsSoftConstraint.asSoftConstraint() for this functionality? Why don't all ConstraintEvaluators have overridable asSoftConstraint methods built-in?

Fenzo’ Built-In Constraint Options

The constraint options that are built-in to Fenzo are the following:

  • Constraints that operate on single tasks:
    • Exclusive Host Constraint: ensures that when a task is assigned to a host, it will be the only task assigned to that host
    • Host Attribute Value Constraint: matches a task with a host that has a particular value for a particular host attribute
  • Constraints that operate on groups of tasks:
    • Unique Host Attribute Constraint: schedules a set of tasks so that each task ends up on a host that has a different value for some host attribute that you specify
    • Balanced Host Attribute Constraint: schedules a set of tasks so that they are distributed evenly among types of hosts, where the type is determined by the value of a particular host attribute

TBD: is stream locality accomplished with one of the above constraints or do you do it in some other way?

Constraints that Operate on Single Tasks

Two built-in constraints operate on individual tasks (though the first of these, as a side effect, may end up constraining every other task).

Exclusive Host Constraint

This constraint ensures that when a task is assigned to a host, it will be the only task assigned to that host.

When this constraint evaluates a prospective host for a task, if any other task is either running on or assigned to that host, the constraint is not met. Only if the host has no tasks running on it and no tasks assigned to it is this constraint met.

Furthermore, this constraint does something no other constraint does: If a task with an exclusive host constraint is assigned to or running on a host, Fenzo will block any other tasks from being assigned to that host.

TBD: is this a feature that you can build in to your custom constraints, or is it a one-off?

Host Attribute Value Constraint

This constraint matches a task with a host that has a particular value for a particular host attribute.

You pass the constructor for this constraint object two values: the name of the host attribute you want to match, and a function that, given a task ID, will return the value of this host attribute that marks a host as being appropriate for that task.

When this constraint evaluates a host to see if it is appropriate for a task, it calls that function and then checks the value it returns against the value of the attribute on the host. If they match, the constraint is met; otherwise (or if the host does not have such an attribute) the constraint is not met.

Constraints that Operate on Groups of Tasks

Two built-in constraints operate on sets of “co-tasks” that you want to assign in a coordinated fashion.

To construct these constraints, you must pass the constructor a function that, when given a single task identifier, will return a Set of the task identifiers of all of the co-tasks of that single task. This function is called the coTasksGetter in the constructor parameters list.

Unique Host Attribute Constraint

This constraint schedules a set of tasks so that each task ends up on a host that has a different value for some host attribute that you specify (if you do not specify a host attribute, this constraint uses the host name as the host attribute).

When this constraint evaluates a host to see if it is appropriate for a task, it checks to see whether any of the task’s co-tasks have already been assigned to a host with the same value for the specified host attribute. If so (or if the host does not have such an attribute), the constraint fails; if not, it succeeds.

You could use this constraint, for example, to assign each task in a group of co-tasks to a different zone. You would do this by choosing the ZoneAttribute as your specified host attribute when you instantiate this constraint. Note that if there are fewer zones than there are tasks you want to assign, this constraint will eventually evaluate a task as being unsuitable for every host and so that task will not be assigned at all.

If that behavior is undesirable, you probably want a Balanced Host Attribute Constraint instead.

Balanced Host Attribute Constraint:

This constraint schedules a set of tasks so that they are distributed evenly among types of hosts, where the type is determined by the value of a particular host attribute.

You decide which host attribute Fenzo will use to categorize hosts into groups when you construct the constraint.

TBD: describe 'expectedValues' constructor param (why is it needed?)

The evaluate() method of this constraint class looks at the current distribution of co-tasks among hosts to see how “balanced” they are. A distribution is perfectly balanced if every co-task is on a host with a different type (meaning it has a different value for the host attribute you chose when you constructed the Balanced Host Attribute Constraint). A distribution may still be balanced if some co-tasks are on hosts with the same type, if there is no way to distribute the tasks in a way that maintains perfect balance. But this constraint attempts to minimize the difference between the number of co-tasks running on the host type with the most co-tasks running on it and the number of co-tasks running on the host type with the least co-tasks on it.

The following table will help to illustrate this. It illustrates three scenarios, showing the initial distribution of tasks among host types and what that distribution would be if a new task were added to a “blue” host:

host group Scenario 1 Scenario 2 Scenario 3
before after before after before after
balance: balanced balanced balanced unbalanced unbalanced unbalanced
evaluation: success failure success
red 1 1 1 1 3 3
green 1 1 1 1 1 1
blue 1 2 2 3 2 3

The third scenario requires some explanation. Although the end state after adding another task to a host of the “blue” type is unbalanced, and although it would be more balanced to add the task to the “green” group (indeed, this would throw the system back into balance), adding the task to the “blue” group does not make the system as a whole any more unbalanced than it was already. That is to say, the difference between the number of co-tasks on the host with the most such tasks and the number on the host with the least does not change, so the proposed assignment does not further unbalance the system, and so this constraint is not violated.

Note, however, that if you convert this constraint into a soft constraint, it becomes more intelligent in situations like these. As a soft constraint, the Balanced Host Attribute Constraint weighs how close a host group would be to having the average number of tasks if a new task were assigned to it. The actual fitness calculation is approximately this:

avg = ⌈(total number of tasks assigned to all groups + 1 for the new task)÷(total number of groups)⌉
fitness = [(avg)−(number of tasks currently assigned to the prospective group)]÷(avg)
(or 0.0 if the number of tasks currently assigned to the prospective group exceeds avg)

In Scenario 3 above, this calculation would result in the following fitness values, with the “green” group showing the highest fitness for the new task:

host group tasks currently assigned fitness
red 3 (3−3)÷3 = 0.0
green 1 (3−1)÷3 ≈ 0.67
blue 2 (3−2)÷3 ≈ 0.33
avg = ⌈(3+1+2+1)÷3⌉ = 3

Creating Your Own Constraint Variety

To create your own constraint variety, create an object that implements the ConstraintEvaluator interface and write its evaluate() method to evaluate your chosen constraint.

This method takes three attributes: a description of the task to be assigned, a description of a potential host for the task, and the current status of tasks and task assignments in the system at large. If you need more information than this, you may need to pass it in when your constraint object is constructed or obtain it in some other fashion.

Write your evaluation function to return a Result object that indicates whether the constraint was met or not.

If you want your constraint simply to map its hard success/failure to a soft 1.0/0.0 when it is converted into a soft constraint, the existing AsSoftConstraint converter will do this automatically. Otherwise, you will also need to create some additional function capable of returning a VMTaskFitnessCalculator (a soft constraint) that performs a more nuanced fitness calculation that matches your constraint.

Making the Task Scheduler Aware of Your Constraints

TBD: how do you make the scheduler aware of your constraints?