Skip to content

Commit

Permalink
sort containers to optimize scale down
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
  • Loading branch information
ndeloof committed Feb 15, 2024
1 parent c79aabd commit de3da82
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
18 changes: 18 additions & 0 deletions pkg/compose/convergence.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
}

sort.Slice(containers, func(i, j int) bool {
// select obsolete containers first, so they get removed as we scale down
if obsolete, _ := mustRecreate(service, containers[i], recreate); obsolete {
// i is obsolete, so must be first in the list
return true
}
if obsolete, _ := mustRecreate(service, containers[j], recreate); obsolete {
// j is obsolete, so must be first in the list
return false
}

// For up-to-date containers, sort by container number to preserve low-values in container numbers
ni, erri := strconv.Atoi(containers[i].Labels[api.ContainerNumberLabel])
nj, errj := strconv.Atoi(containers[j].Labels[api.ContainerNumberLabel])
if erri == nil && errj == nil {
return ni < nj
}

// If we don't get a container number (?) just sort by creation date
return containers[i].Created < containers[j].Created
})
for i, container := range containers {
Expand Down
2 changes: 2 additions & 0 deletions pkg/e2e/fixtures/scale/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ services:
- db
db:
image: nginx:alpine
environment:
- MAYBE
front:
image: nginx:alpine
deploy:
Expand Down
72 changes: 72 additions & 0 deletions pkg/e2e/scale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,78 @@ func TestScaleWithDepsCases(t *testing.T) {
checkServiceContainer(t, res.Combined(), "scale-deps-tests-db", NO_STATE_TO_CHECK, 1)
}

func TestScaleUpAndDownPreserveContainerNumber(t *testing.T) {
const projectName = "scale-up-down-test"

c := NewCLI(t, WithEnv(
"COMPOSE_PROJECT_NAME="+projectName))

reset := func() {
c.RunDockerComposeCmd(t, "down", "--rmi", "all")
}
t.Cleanup(reset)
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2", "db")
res.Assert(t, icmd.Success)

res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
res.Assert(t, icmd.Success)
assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1\n"+projectName+"-db-2")

t.Log("scale down removes replica #2")
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=1", "db")
res.Assert(t, icmd.Success)

res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
res.Assert(t, icmd.Success)
assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1")

t.Log("scale up restores replica #2")
res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2", "db")
res.Assert(t, icmd.Success)

res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
res.Assert(t, icmd.Success)
assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1\n"+projectName+"-db-2")
}

func TestScaleDownRemovesObsolete(t *testing.T) {
const projectName = "scale-down-obsolete-test"
c := NewCLI(t, WithEnv(
"COMPOSE_PROJECT_NAME="+projectName))

reset := func() {
c.RunDockerComposeCmd(t, "down", "--rmi", "all")
}
t.Cleanup(reset)
res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "db")
res.Assert(t, icmd.Success)

res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
res.Assert(t, icmd.Success)
assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1")

cmd := c.NewDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=2", "db")
res = icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
cmd.Env = append(cmd.Env, "MAYBE=value")
})
res.Assert(t, icmd.Success)

res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
res.Assert(t, icmd.Success)
assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1\n"+projectName+"-db-2")

t.Log("scale down removes obsolete replica #1")
cmd = c.NewDockerComposeCmd(t, "--project-directory", "fixtures/scale", "up", "-d", "--scale", "db=1", "db")
res = icmd.RunCmd(cmd, func(cmd *icmd.Cmd) {
cmd.Env = append(cmd.Env, "MAYBE=value")
})
res.Assert(t, icmd.Success)

res = c.RunDockerComposeCmd(t, "ps", "--format", "{{.Name}}", "db")
res.Assert(t, icmd.Success)
assert.Equal(t, strings.TrimSpace(res.Stdout()), projectName+"-db-1")
}

func checkServiceContainer(t *testing.T, stdout, containerName, containerState string, count int) {
found := 0
lines := strings.Split(stdout, "\n")
Expand Down

0 comments on commit de3da82

Please sign in to comment.