Skip to content

Commit

Permalink
add option to round up CPU quota
Browse files Browse the repository at this point in the history
  • Loading branch information
wallee94 committed Oct 12, 2023
1 parent e83e959 commit f5ff8d2
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 11 deletions.
5 changes: 4 additions & 1 deletion internal/runtime/cpu_quota_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (

// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process
// to a valid GOMAXPROCS value.
func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) {
func CPUQuotaToGOMAXPROCS(minValue int, roundUpQuota bool) (int, CPUQuotaStatus, error) {
cgroups, err := newQueryer()
if err != nil {
return -1, CPUQuotaUndefined, err
Expand All @@ -44,6 +44,9 @@ func CPUQuotaToGOMAXPROCS(minValue int) (int, CPUQuotaStatus, error) {
}

maxProcs := int(math.Floor(quota))
if roundUpQuota {
maxProcs = int(math.Ceil(quota))
}

Check warning on line 49 in internal/runtime/cpu_quota_linux.go

View check run for this annotation

Codecov / codecov/patch

internal/runtime/cpu_quota_linux.go#L47-L49

Added lines #L47 - L49 were not covered by tests
if minValue > 0 && maxProcs < minValue {
return minValue, CPUQuotaMinUsed, nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/runtime/cpu_quota_unsupported.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ package runtime
// CPUQuotaToGOMAXPROCS converts the CPU quota applied to the calling process
// to a valid GOMAXPROCS value. This is Linux-specific and not supported in the
// current OS.
func CPUQuotaToGOMAXPROCS(_ int) (int, CPUQuotaStatus, error) {
func CPUQuotaToGOMAXPROCS(_ int, _ bool) (int, CPUQuotaStatus, error) {
return -1, CPUQuotaUndefined, nil
}
12 changes: 10 additions & 2 deletions maxprocs/maxprocs.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ func currentMaxProcs() int {

type config struct {
printf func(string, ...interface{})
procs func(int) (int, iruntime.CPUQuotaStatus, error)
procs func(int, bool) (int, iruntime.CPUQuotaStatus, error)
minGOMAXPROCS int
roundUpQuota bool
}

func (c *config) log(fmt string, args ...interface{}) {
Expand Down Expand Up @@ -71,6 +72,13 @@ func Min(n int) Option {
})
}

// RoundUpQuota controls whether the CPU quota should be rounded up instead of down.
func RoundUpQuota(v bool) Option {
return optionFunc(func(cfg *config) {
cfg.roundUpQuota = v
})
}

type optionFunc func(*config)

func (of optionFunc) apply(cfg *config) { of(cfg) }
Expand Down Expand Up @@ -102,7 +110,7 @@ func Set(opts ...Option) (func(), error) {
return undoNoop, nil
}

maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS)
maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS, cfg.roundUpQuota)
if err != nil {
return undoNoop, err
}
Expand Down
36 changes: 29 additions & 7 deletions maxprocs/maxprocs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func testLogger() (*bytes.Buffer, Option) {
return buf, Logger(printf)
}

func stubProcs(f func(int) (int, iruntime.CPUQuotaStatus, error)) Option {
func stubProcs(f func(int, bool) (int, iruntime.CPUQuotaStatus, error)) Option {
return optionFunc(func(cfg *config) {
cfg.procs = f
})
Expand Down Expand Up @@ -96,7 +96,7 @@ func TestSet(t *testing.T) {
})

t.Run("ErrorReadingQuota", func(t *testing.T) {
opt := stubProcs(func(int) (int, iruntime.CPUQuotaStatus, error) {
opt := stubProcs(func(int, bool) (int, iruntime.CPUQuotaStatus, error) {
return 0, iruntime.CPUQuotaUndefined, errors.New("failed")
})
prev := currentMaxProcs()
Expand All @@ -109,7 +109,7 @@ func TestSet(t *testing.T) {

t.Run("QuotaUndefined", func(t *testing.T) {
buf, logOpt := testLogger()
quotaOpt := stubProcs(func(int) (int, iruntime.CPUQuotaStatus, error) {
quotaOpt := stubProcs(func(int, bool) (int, iruntime.CPUQuotaStatus, error) {
return 0, iruntime.CPUQuotaUndefined, nil
})
prev := currentMaxProcs()
Expand All @@ -122,7 +122,7 @@ func TestSet(t *testing.T) {

t.Run("QuotaUndefined return maxProcs=7", func(t *testing.T) {
buf, logOpt := testLogger()
quotaOpt := stubProcs(func(int) (int, iruntime.CPUQuotaStatus, error) {
quotaOpt := stubProcs(func(int, bool) (int, iruntime.CPUQuotaStatus, error) {
return 7, iruntime.CPUQuotaUndefined, nil
})
prev := currentMaxProcs()
Expand All @@ -135,7 +135,7 @@ func TestSet(t *testing.T) {

t.Run("QuotaTooSmall", func(t *testing.T) {
buf, logOpt := testLogger()
quotaOpt := stubProcs(func(min int) (int, iruntime.CPUQuotaStatus, error) {
quotaOpt := stubProcs(func(min int, roundUpQuota bool) (int, iruntime.CPUQuotaStatus, error) {
return min, iruntime.CPUQuotaMinUsed, nil
})
undo, err := Set(logOpt, quotaOpt, Min(5))
Expand All @@ -147,7 +147,7 @@ func TestSet(t *testing.T) {

t.Run("Min unused", func(t *testing.T) {
buf, logOpt := testLogger()
quotaOpt := stubProcs(func(min int) (int, iruntime.CPUQuotaStatus, error) {
quotaOpt := stubProcs(func(min int, roundUpQuota bool) (int, iruntime.CPUQuotaStatus, error) {
return min, iruntime.CPUQuotaMinUsed, nil
})
// Min(-1) should be ignored.
Expand All @@ -159,7 +159,7 @@ func TestSet(t *testing.T) {
})

t.Run("QuotaUsed", func(t *testing.T) {
opt := stubProcs(func(min int) (int, iruntime.CPUQuotaStatus, error) {
opt := stubProcs(func(min int, roundUpQuota bool) (int, iruntime.CPUQuotaStatus, error) {
assert.Equal(t, 1, min, "Default minimum value should be 1")
return 42, iruntime.CPUQuotaUsed, nil
})
Expand All @@ -168,6 +168,28 @@ func TestSet(t *testing.T) {
require.NoError(t, err, "Set failed")
assert.Equal(t, 42, currentMaxProcs(), "should change GOMAXPROCS to match quota")
})

t.Run("RoundUpQuotaSetToTrue", func(t *testing.T) {
opt := stubProcs(func(min int, roundUpQuota bool) (int, iruntime.CPUQuotaStatus, error) {
assert.Equal(t, true, roundUpQuota, "roundUpQuota should be true")
return 43, iruntime.CPUQuotaUsed, nil
})
undo, err := Set(opt, RoundUpQuota(true))
defer undo()
require.NoError(t, err, "Set failed")
assert.Equal(t, 43, currentMaxProcs(), "should change GOMAXPROCS to match rounded up quota")
})

t.Run("RoundUpQuotaSetToFalse", func(t *testing.T) {
opt := stubProcs(func(min int, roundUpQuota bool) (int, iruntime.CPUQuotaStatus, error) {
assert.Equal(t, false, roundUpQuota, "roundUpQuota should be false")
return 42, iruntime.CPUQuotaUsed, nil
})
undo, err := Set(opt, RoundUpQuota(false))
defer undo()
require.NoError(t, err, "Set failed")
assert.Equal(t, 42, currentMaxProcs(), "should change GOMAXPROCS to match rounded up quota")
})
}

func TestMain(m *testing.M) {
Expand Down

0 comments on commit f5ff8d2

Please sign in to comment.