mirror of
https://github.com/actions-runner-controller/actions-runner-controller.git
synced 2026-09-30 06:11:37 +02:00
Set per-controller reconcile concurrency defaults and remove --default-max-concurrent-reconciles (#4670)
This commit is contained in:
@@ -10,54 +10,60 @@ import (
|
||||
// Options is the optional configuration for the controllers, which can be
|
||||
// set via command-line flags or environment variables.
|
||||
type Options struct {
|
||||
// DefaultMaxConcurrentReconciles is the maximum number of concurrent Reconciles
|
||||
// applied to every controller that does not have its own value set below.
|
||||
DefaultMaxConcurrentReconciles int
|
||||
|
||||
// AutoscalingRunnerSetMaxConcurrentReconciles is the maximum number of concurrent Reconciles
|
||||
// which can be run by the AutoscalingRunnerSetController. Zero means DefaultMaxConcurrentReconciles.
|
||||
// which can be run by the AutoscalingRunnerSetController. Values of zero or less are replaced
|
||||
// by the shipped default in Resolve.
|
||||
AutoscalingRunnerSetMaxConcurrentReconciles int
|
||||
|
||||
// AutoscalingListenerMaxConcurrentReconciles is the maximum number of concurrent Reconciles
|
||||
// which can be run by the AutoscalingListenerController. Zero means DefaultMaxConcurrentReconciles.
|
||||
// which can be run by the AutoscalingListenerController. Values of zero or less are replaced
|
||||
// by the shipped default in Resolve.
|
||||
AutoscalingListenerMaxConcurrentReconciles int
|
||||
|
||||
// EphemeralRunnerSetMaxConcurrentReconciles is the maximum number of concurrent Reconciles
|
||||
// which can be run by the EphemeralRunnerSetController. Zero means DefaultMaxConcurrentReconciles.
|
||||
// which can be run by the EphemeralRunnerSetController. Values of zero or less are replaced
|
||||
// by the shipped default in Resolve.
|
||||
EphemeralRunnerSetMaxConcurrentReconciles int
|
||||
|
||||
// EphemeralRunnerMaxConcurrentReconciles is the maximum number of concurrent Reconciles
|
||||
// which can be run by the EphemeralRunnerController. Zero means DefaultMaxConcurrentReconciles.
|
||||
// which can be run by the EphemeralRunnerController. Values of zero or less are replaced
|
||||
// by the shipped default in Resolve.
|
||||
EphemeralRunnerMaxConcurrentReconciles int
|
||||
}
|
||||
|
||||
// OptionsWithDefault returns the default options.
|
||||
// This is here to maintain the options and their default values in one place,
|
||||
// rather than having to correlate those in multiple places.
|
||||
//
|
||||
// The EphemeralRunner controller gets a higher value than the rest because it
|
||||
// is the only one of the four that routinely has many distinct objects to
|
||||
// reconcile at once: one per runner. controller-runtime serialises reconciles
|
||||
// per object, so extra workers on the other three controllers only pay off
|
||||
// when many runner scale sets exist.
|
||||
func OptionsWithDefault() Options {
|
||||
return Options{
|
||||
DefaultMaxConcurrentReconciles: 1,
|
||||
AutoscalingRunnerSetMaxConcurrentReconciles: 2,
|
||||
AutoscalingListenerMaxConcurrentReconciles: 2,
|
||||
EphemeralRunnerSetMaxConcurrentReconciles: 2,
|
||||
EphemeralRunnerMaxConcurrentReconciles: 4,
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve returns a copy of the options where a DefaultMaxConcurrentReconciles
|
||||
// of zero or less is replaced by 1, and every per-controller
|
||||
// MaxConcurrentReconciles that is left at zero is replaced by
|
||||
// DefaultMaxConcurrentReconciles.
|
||||
// Resolve returns a copy of the options where every MaxConcurrentReconciles of
|
||||
// zero or less is replaced by that controller's shipped default, so a
|
||||
// non-positive value can never reach controller-runtime.
|
||||
func (o Options) Resolve() Options {
|
||||
if o.DefaultMaxConcurrentReconciles <= 0 {
|
||||
o.DefaultMaxConcurrentReconciles = 1
|
||||
}
|
||||
orDefault := func(n int) int {
|
||||
defaults := OptionsWithDefault()
|
||||
orDefault := func(n, def int) int {
|
||||
if n > 0 {
|
||||
return n
|
||||
}
|
||||
return o.DefaultMaxConcurrentReconciles
|
||||
return def
|
||||
}
|
||||
o.AutoscalingRunnerSetMaxConcurrentReconciles = orDefault(o.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
o.AutoscalingListenerMaxConcurrentReconciles = orDefault(o.AutoscalingListenerMaxConcurrentReconciles)
|
||||
o.EphemeralRunnerSetMaxConcurrentReconciles = orDefault(o.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
o.EphemeralRunnerMaxConcurrentReconciles = orDefault(o.EphemeralRunnerMaxConcurrentReconciles)
|
||||
o.AutoscalingRunnerSetMaxConcurrentReconciles = orDefault(o.AutoscalingRunnerSetMaxConcurrentReconciles, defaults.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
o.AutoscalingListenerMaxConcurrentReconciles = orDefault(o.AutoscalingListenerMaxConcurrentReconciles, defaults.AutoscalingListenerMaxConcurrentReconciles)
|
||||
o.EphemeralRunnerSetMaxConcurrentReconciles = orDefault(o.EphemeralRunnerSetMaxConcurrentReconciles, defaults.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
o.EphemeralRunnerMaxConcurrentReconciles = orDefault(o.EphemeralRunnerMaxConcurrentReconciles, defaults.EphemeralRunnerMaxConcurrentReconciles)
|
||||
return o
|
||||
}
|
||||
|
||||
|
||||
@@ -9,35 +9,54 @@ import (
|
||||
func TestOptionsResolve(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("unset per-controller values fall back to the default", func(t *testing.T) {
|
||||
t.Run("the shipped defaults are used as-is", func(t *testing.T) {
|
||||
got := OptionsWithDefault().Resolve()
|
||||
assert.Equal(t, 1, got.DefaultMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.AutoscalingListenerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.EphemeralRunnerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 2, got.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 2, got.AutoscalingListenerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 2, got.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 4, got.EphemeralRunnerMaxConcurrentReconciles)
|
||||
})
|
||||
|
||||
t.Run("a default of zero or less is replaced by 1", func(t *testing.T) {
|
||||
t.Run("the shipped defaults survive a second resolve", func(t *testing.T) {
|
||||
once := OptionsWithDefault().Resolve()
|
||||
assert.Equal(t, once, once.Resolve())
|
||||
})
|
||||
|
||||
t.Run("explicit values win over the defaults", func(t *testing.T) {
|
||||
opts := Options{
|
||||
AutoscalingRunnerSetMaxConcurrentReconciles: 3,
|
||||
AutoscalingListenerMaxConcurrentReconciles: 5,
|
||||
EphemeralRunnerSetMaxConcurrentReconciles: 6,
|
||||
EphemeralRunnerMaxConcurrentReconciles: 20,
|
||||
}
|
||||
got := opts.Resolve()
|
||||
assert.Equal(t, 3, got.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 5, got.AutoscalingListenerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 6, got.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 20, got.EphemeralRunnerMaxConcurrentReconciles)
|
||||
})
|
||||
|
||||
t.Run("a value of zero or less is replaced by that controller's default", func(t *testing.T) {
|
||||
for _, n := range []int{0, -1} {
|
||||
opts := Options{DefaultMaxConcurrentReconciles: n}
|
||||
opts := Options{
|
||||
AutoscalingRunnerSetMaxConcurrentReconciles: n,
|
||||
AutoscalingListenerMaxConcurrentReconciles: n,
|
||||
EphemeralRunnerSetMaxConcurrentReconciles: n,
|
||||
EphemeralRunnerMaxConcurrentReconciles: n,
|
||||
}
|
||||
got := opts.Resolve()
|
||||
assert.Equal(t, 1, got.DefaultMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.AutoscalingListenerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 1, got.EphemeralRunnerMaxConcurrentReconciles)
|
||||
assert.Equal(t, OptionsWithDefault(), got, "a non-positive value must never reach controller-runtime")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("a changed default applies to every unset controller", func(t *testing.T) {
|
||||
t.Run("a single non-positive value does not disturb the others", func(t *testing.T) {
|
||||
opts := OptionsWithDefault()
|
||||
opts.DefaultMaxConcurrentReconciles = 4
|
||||
opts.EphemeralRunnerMaxConcurrentReconciles = 20
|
||||
opts.AutoscalingListenerMaxConcurrentReconciles = 7
|
||||
opts.EphemeralRunnerMaxConcurrentReconciles = 0
|
||||
got := opts.Resolve()
|
||||
assert.Equal(t, 4, got.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 4, got.AutoscalingListenerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 4, got.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 20, got.EphemeralRunnerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 7, got.AutoscalingListenerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 4, got.EphemeralRunnerMaxConcurrentReconciles)
|
||||
assert.Equal(t, 2, got.AutoscalingRunnerSetMaxConcurrentReconciles)
|
||||
assert.Equal(t, 2, got.EphemeralRunnerSetMaxConcurrentReconciles)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user