Skip to content

Commit f743dbb

Browse files
authored
Merge pull request juju#23105 from SimonRichardson/rollout-pods
juju#23105 When reconciling the units during scaling, we previously always scaled from top down - so if you had 0,1,2 and scaled down by one at a time, it would do 0,1 and then 0. This is normally fine, but it doesn't allow you to rollout the oldest unit (or pod in this instance). For instance, if you had new constraints (think affinities) then you'd have to scale to zero, thus removing all pods. This is very unfortunate. Instead, if we scale from bottom up, we can then roll in new units (pods) with new constraints, allowing for a more fluid system. This was spotted when testing juju#23076 and wanting to scale back down. ## QA steps ```sh $ juju bootstrap microk8s test $ juju add-model foo $ juju deploy prometheus-k8s prom -n 3 $ juju remove-unit prom --num-units=2 ``` Removes 0, 1. ## Links **Jira card:** [JUJU-10258](https://warthogs.atlassian.net/browse/JUJU-10258) [JUJU-10258]: https://warthogs.atlassian.net/browse/JUJU-10258?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2 parents d08eba8 + 2489510 commit f743dbb

4 files changed

Lines changed: 440 additions & 23 deletions

File tree

apiserver/restrict_caasmodel.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ var commonModelFacadeNames = set.NewStrings(
7272
"StorageProvisioner",
7373
"StringsWatcher",
7474
"Subnets",
75+
"Tracer",
7576
"Undertaker",
7677
"Uniter",
7778
"Upgrader",

apiserver/restrict_caasmodel_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func (s *RestrictCAASModelSuite) SetUpSuite(c *tc.C) {
3030

3131
func (s *RestrictCAASModelSuite) TestAllowed(c *tc.C) {
3232
s.assertMethod(c, "CAASApplication", 1, "UnitIntroduction")
33+
s.assertMethod(c, "Tracer", 1, "GetControllerTracingConfig")
3334
}
3435

3536
func (s *RestrictCAASModelSuite) TestSubnetsAllowed(c *tc.C) {

internal/worker/caasapplicationprovisioner/ops.go

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"io"
1111
"reflect"
12+
"sort"
1213
"strings"
1314
"time"
1415

@@ -634,12 +635,12 @@ func reconcileDeadUnitScale(
634635
}
635636

636637
desiredScale := ps.ScaleTarget
637-
unitsToRemove := 0
638+
threshold := unitRemovalThreshold(unitNamesAndLives, desiredScale)
638639

640+
unitsToRemove := 0
639641
var deadUnits []coreunit.Name
640642
for unitName, unitLife := range unitNamesAndLives {
641-
if unitName.Number() < desiredScale {
642-
// This is a unit we want to keep.
643+
if unitName.Number() >= threshold {
643644
continue
644645
}
645646
unitsToRemove++
@@ -676,6 +677,9 @@ func reconcileDeadUnitScale(
676677
return tryAgain
677678
}
678679

680+
sort.Slice(deadUnits, func(i, j int) bool {
681+
return deadUnits[i].Number() < deadUnits[j].Number()
682+
})
679683
for _, deadUnit := range deadUnits {
680684
logger.Infof(ctx, "removing dead unit %s", deadUnit)
681685
if err := facade.RemoveUnit(ctx, string(deadUnit)); err != nil && !errors.Is(err, errors.NotFound) {
@@ -729,15 +733,7 @@ func ensureScale(
729733
return err
730734
}
731735

732-
unitScale := 0
733-
for unitName := range units {
734-
nextUnitNumber := unitName.Number() + 1
735-
if nextUnitNumber > unitScale {
736-
unitScale = nextUnitNumber
737-
}
738-
}
739-
740-
if ps.ScaleTarget >= unitScale {
736+
if ps.ScaleTarget >= len(units) {
741737
storageUniqueID := appUUID.String()[:6]
742738
err := ensureScaleWithFsAttachments(
743739
ctx,
@@ -773,10 +769,10 @@ func ensureScale(
773769
return nil
774770
}
775771

772+
threshold := unitRemovalThreshold(units, ps.ScaleTarget)
776773
var unitsToDestroy []string
777774
for unitName, unitLife := range units {
778-
if unitName.Number() < ps.ScaleTarget {
779-
// This is a unit we want to keep.
775+
if unitName.Number() >= threshold {
780776
continue
781777
}
782778
if unitLife == life.Alive {
@@ -802,6 +798,28 @@ func getStorageUniqueID(appUUID coreapplication.UUID) string {
802798
return appUUID.String()[:6]
803799
}
804800

801+
// unitRemovalThreshold returns the lowest ordinal that should be kept when
802+
// scaling down to targetScale units. Unit ordinals lower than the returned
803+
// threshold are removed, keeping the highest targetScale ordinals. The
804+
// threshold is derived from the sorted ordinal set rather than assuming
805+
// ordinals are contiguous (0..N-1), so it remains correct when ordinals are
806+
// sparse (e.g. 0, 2, 4).
807+
func unitRemovalThreshold(units map[coreunit.Name]life.Value, targetScale int) int {
808+
ordinals := make([]int, 0, len(units))
809+
for unitName := range units {
810+
ordinals = append(ordinals, unitName.Number())
811+
}
812+
sort.Ints(ordinals)
813+
814+
if targetScale >= len(ordinals) {
815+
return 0
816+
}
817+
if targetScale <= 0 {
818+
return ordinals[len(ordinals)-1] + 1
819+
}
820+
return ordinals[len(ordinals)-targetScale]
821+
}
822+
805823
func setOperatorStatus(
806824
ctx context.Context,
807825
appName string, s status.Status, reason string, data map[string]any,

0 commit comments

Comments
 (0)