Repository navigation
Commit 6caa33e
authored
Add 'balancedweighted' VM allocation algorithm (#14109)
* Add a DAO method to count VMs per host
Counts VMs occupying each host in a zone, pod or cluster, optionally only
those that changed state recently. One query for the whole scope rather
than one per host.
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Track a moving average of real host utilisation
StatsCollector already polls CPU and memory utilisation for every host,
but it keeps only the newest sample and nothing uses it for placement.
- fold those samples into an exponentially weighted moving average
- weight by elapsed time, so a missed poll decays correctly instead of
over-weighting the previous value
- report nothing usable until a host has been sampled, so callers can
fall back to allocation figures
Two settings: host.load.sample.interval and host.load.half.life.
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Add 'balancedweighted' VM allocation algorithm
Opt-in via vm.allocation.algorithm. Existing algorithms and the default
are untouched.
Allocated capacity alone is a poor ranking under heavy overprovisioning:
it is measured against a total already multiplied by the overprovisioning
factor, so a host under real strain still reports a low percentage and
keeps being chosen. Anything allocation cannot see - VMs the scheduler
has lost track of, guests using more than they asked for - is invisible.
Ranks on a blend, lower is better:
| term | source |
|---------------------|-------------------------------------------|
| CPU allocated | op_host_capacity, over the inflated total |
| CPU utilisation | moving average of real usage |
| memory allocated | op_host_capacity, over the inflated total |
| memory utilisation | moving average of real usage |
| VM count | VMs on the host |
| recent starts | VMs started within the last few minutes |
A dominant resource term is added on top so a host nearly out of any one
resource does not rank well on a good average. Hosts measurably too busy
are held back, unless that would leave nowhere to deploy.
Selection is random among the best few rather than strictly ordered:
capacity is only charged once a VM starts, so concurrent deployments all
read the same figures and strict ordering makes them agree on one host.
All weights and thresholds are settings, most cluster scoped.
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Fix three defects found in review of the weighted scoring
Allocated fraction was measured against the wrong total. op_host_capacity
stores totals raw and overprovisioning is applied when they are read, so
dividing by the stored total made the fraction reach 1 at the host's
physical size. On a cluster overcommitted 10 times every host clamped to
1, killing both the allocation term and the dominant resource term - on
exactly the clusters this algorithm is for.
- apply the cluster ratio to the denominator
- drop hosts missing a CPU or memory capacity row instead of scoring the
missing resource as untouched, which made them rank first
Utilisation thresholds could be bypassed. Held-back hosts were appended
before the random spread was applied, so the spread could shuffle a busy
host into the lead. With 1 healthy host and a spread of 3, two thirds of
deployments picked an over-threshold host.
- spread over healthy hosts only, before anything else is appended
A host with no load samples was treated as idle. It was exempt from the
thresholds and its dominant resource term fell back to allocation, so a
host with broken stats outranked every measured host and collected the
deployments.
- rank hosts we cannot measure behind every host we can
- when nothing can be measured, ranking falls back to allocation as before
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Address the rest of the weighted scoring review
Utilisation average
- a host whose agent stops reporting kept vouching for itself forever:
StatsCollector hands back the previous entry when a poll fails, and
that unchanged reading was folded again every minute. Detect the
repeat, and expire an average that stops being updated
- sample on a scheduled executor catching Throwable, not a Timer, which
dies permanently and silently on one escaping error
- only collect when an algorithm that reads the figures is selected
- read the half life once per sample rather than inside the map update
- document what getCpuUtilization means per hypervisor: it is what this
assumes on KVM, a reservation figure on VMware, and scaled by core
count on XenServer
Scoring
- a negative weight would rank the most loaded host first; floor at
zero and say so
- zeroing all six terms no longer discards the dominant resource term
- read weights once per ranking instead of once per host
Queries
- one query per ranking instead of two, returning both counts
- count Stopping VMs, which still hold their host
- correct the doc: every host in scope is returned, including empty ones
Tests
- cover rank() end to end, which is where the defects were: the
capacity denominator, the thresholds, the spread and the ordering of
measured against unmeasured hosts
- the distribution simulation drew different random streams per arm, so
the arms saw different workloads. Fix the workload up front and add
an allocation-only-plus-spread control, which shows the scoring and
not the spread is what evens out real load
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Address review feedback on the weighted allocation algorithm
vm_instance.update_time is a TIMESTAMP column, so there is no date that
reliably means "never" - anything past 2038 is out of range. Counting
with no cut-off used Long.MAX_VALUE, which is roughly year 292 million.
The query now leaves the timestamp out entirely in that case rather than
binding an impossible one.
Also catch Exception rather than Throwable in the new query, and put the
config key array one entry per line.
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Hold back hosts with a backlog of VMs still starting
The symptom this addresses is a burst of deployments all landing on one
host and then sitting in Starting for minutes instead of seconds, while
that host works through a queue of starts its storage and agent command
path cannot absorb.
Nothing in the algorithm saw that. Capacity is not charged until a VM
reaches Starting, which is after the allocator has returned, and the
utilisation average is a sample interval and a half life behind, so a
burst is over before either notices. The recent-start term is the
closest thing to a guard and it cannot do the job: it counts VMs whose
state changed within a window, so a VM stuck in Starting for ten minutes
stops counting after five - the signal decays fastest on the hosts that
are worst off - and its contribution is capped at 0.111 of the score, so
a host with 200 VMs stuck in Starting ranks the same as one with 50.
Count the VMs actually in Starting instead, and use it as a gate rather
than a weight. It does not expire while the condition persists, it is
read fresh on every ranking rather than sampled, and a gate cannot be
outvoted by the other terms. It costs nothing: the per-host VM count
query already filters on vm.state, so it is one more aggregate in a
query that was already being made.
Held-back hosts are still never dropped, only moved down the list, so
this cannot turn a deployment that would have succeeded into an
InsufficientServerCapacity failure. Within the held-back group the order
is by starting count, so if every host is backed up the least backed up
one is tried first.
Also fix a hole in the selection spread. It was applied to the healthy
list only, so whenever no host was eligible that list was empty, the
shuffle was a no-op, and the held-back hosts were handed out in strict
score order - concurrent deployments agreeing on one host in exactly the
conditions that caused the pile-up. The spread now applies to whichever
tier leads, which also removes the special-cased branch it was hiding
in. The "short of capacity" warning stays narrow: hosts merely being
unmeasured, as they all are on a freshly started management server, is
not a capacity problem and must not warn on every deployment.
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Rank each candidate host once, however many tags it carries
listAllUpAndEnabledNonHAHosts joins host_tags to filter out hosts whose
tags are rules, and the join carries no DISTINCT, so a host comes back
once per non-rule tag it holds. That path is taken whenever a VM has no
host tag to filter the candidate list by, which in practice means system
VMs: a virtual router deploying onto a twelve host cluster where three
hosts carry a second tag is handed fifteen candidates.
An allocator that walks the list and takes the first host that fits is
indifferent to a repeat, which is why this has gone unnoticed. The
weighted ranking is not. Scores are keyed by host, so a repeat changes no
score, but the selection spread shuffles the leading few entries and the
caller takes whichever ends up first - so a host holding two of the three
slots draws two thirds of the deployments rather than one third.
Collapse the repeats in the DAO, where the join creates them and every
caller benefits, and again in the scorer, so ranking cannot be skewed by
whatever a future caller hands it.
Signed-off-by: Brad House <bhouse@nexthop.ai>
* Let WeightedHostScorer implement HostScoringWeights
The four shared weights are then inherited like the scorer's own keys
instead of being qualified with the interface name. The scorer's own
description suffix is renamed so it no longer hides the interface's.
Signed-off-by: Brad House <bhouse@nexthop.ai>
---------
Signed-off-by: Brad House <bhouse@nexthop.ai>1 parent 7ccc48c commit 6caa33e
17 files changed
Lines changed: 2055 additions & 4 deletions
File tree
- api/src/main/java/com/cloud
- deploy
- host
- engine/schema/src
- main/java/com/cloud
- host/dao
- vm/dao
- test/java/com/cloud/host/dao
- server/src
- main
- java/com/cloud/agent/manager/allocator/impl
- resources/META-INF/cloudstack/server-allocator
- test/java/com/cloud/agent/manager/allocator/impl
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
66 | 69 | | |
67 | 70 | | |
68 | 71 | | |
69 | | - | |
| 72 | + | |
70 | 73 | | |
71 | 74 | | |
72 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
73 | | - | |
| 73 | + | |
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
935 | 936 | | |
936 | 937 | | |
937 | 938 | | |
938 | | - | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
939 | 947 | | |
940 | 948 | | |
941 | 949 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
131 | 132 | | |
132 | 133 | | |
133 | 134 | | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
134 | 147 | | |
135 | 148 | | |
136 | 149 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
| 49 | + | |
48 | 50 | | |
49 | 51 | | |
50 | 52 | | |
| |||
156 | 158 | | |
157 | 159 | | |
158 | 160 | | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
159 | 176 | | |
160 | 177 | | |
161 | 178 | | |
| |||
801 | 818 | | |
802 | 819 | | |
803 | 820 | | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
804 | 860 | | |
805 | 861 | | |
806 | 862 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
| 42 | + | |
42 | 43 | | |
43 | 44 | | |
44 | 45 | | |
| |||
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
63 | 90 | | |
64 | 91 | | |
65 | 92 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
95 | 96 | | |
96 | 97 | | |
97 | 98 | | |
| 99 | + | |
| 100 | + | |
98 | 101 | | |
99 | 102 | | |
100 | 103 | | |
| |||
209 | 212 | | |
210 | 213 | | |
211 | 214 | | |
| 215 | + | |
| 216 | + | |
212 | 217 | | |
213 | 218 | | |
214 | 219 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
0 commit comments