Skip to content

Commit a761430

Browse files
authored
cgmon: fix the issue that CPU quota is 0 if it's not limited by cgroup (#61322)
close #61223
1 parent 998279f commit a761430

File tree

3 files changed

+76
-22
lines changed

3 files changed

+76
-22
lines changed

pkg/util/cgmon/BUILD.bazel

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@io_bazel_rules_go//go:def.bzl", "go_library")
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
22

33
go_library(
44
name = "cgmon",
@@ -14,3 +14,15 @@ go_library(
1414
"@org_uber_go_zap//:zap",
1515
],
1616
)
17+
18+
go_test(
19+
name = "cgmon_test",
20+
timeout = "short",
21+
srcs = ["cgmon_test.go"],
22+
embed = [":cgmon"],
23+
flaky = True,
24+
deps = [
25+
"@com_github_shirou_gopsutil_v3//mem",
26+
"@com_github_stretchr_testify//require",
27+
],
28+
)

pkg/util/cgmon/cgmon.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ var (
3838
ctx context.Context
3939
cancel context.CancelFunc
4040
wg sync.WaitGroup
41-
cfgMaxProcs int
42-
lastMaxProcs int
41+
lastCPU int
4342
lastMemoryLimit uint64
43+
44+
getCgroupCPUPeriodAndQuota = cgroup.GetCPUPeriodAndQuota
45+
getCgroupMemoryLimit = cgroup.GetMemoryLimit
4446
)
4547

4648
// StartCgroupMonitor uses to start the cgroup monitoring.
@@ -115,45 +117,44 @@ func refreshCgroupCPU() error {
115117
quota := runtime.NumCPU()
116118

117119
// Get CPU quota from cgroup.
118-
cpuPeriod, cpuQuota, err := cgroup.GetCPUPeriodAndQuota()
119-
if err != nil {
120-
return err
121-
}
122-
if cpuPeriod > 0 && cpuQuota > 0 {
120+
cpuPeriod, cpuQuota, err := getCgroupCPUPeriodAndQuota()
121+
122+
// Only use cgroup CPU quota if it is available. It's possible that the cgroup doesn't have CPU quota set.
123+
// Then in some environments, systemd will not enable the cpu controller if cpu quota is not set.
124+
if err == nil && cpuPeriod > 0 && cpuQuota > 0 {
123125
ratio := float64(cpuQuota) / float64(cpuPeriod)
124126
if ratio < float64(quota) {
125127
quota = int(math.Ceil(ratio))
126128
}
127129
}
128130

129-
if quota != lastMaxProcs {
131+
if quota != lastCPU {
130132
log.Info("set the maxprocs", zap.Int("quota", quota))
131133
metrics.MaxProcs.Set(float64(quota))
132-
lastMaxProcs = quota
133-
} else if lastMaxProcs == 0 {
134-
log.Info("set the maxprocs", zap.Int("cfgMaxProcs", cfgMaxProcs))
135-
metrics.MaxProcs.Set(float64(cfgMaxProcs))
136-
lastMaxProcs = cfgMaxProcs
134+
lastCPU = quota
137135
}
138-
return nil
136+
137+
return err
139138
}
140139

141140
func refreshCgroupMemory() error {
142-
memLimit, err := cgroup.GetMemoryLimit()
143-
if err != nil {
144-
return err
145-
}
141+
// Get the total memory limit from `procfs`
146142
vmem, err := mem.VirtualMemory()
147143
if err != nil {
148144
return err
149145
}
150-
if memLimit > vmem.Total {
151-
memLimit = vmem.Total
146+
memLimit := vmem.Total
147+
148+
// Only use cgroup memory limit if it is available.
149+
cgroupMemLimit, err := getCgroupMemoryLimit()
150+
if err == nil && cgroupMemLimit < memLimit {
151+
memLimit = cgroupMemLimit
152152
}
153+
153154
if memLimit != lastMemoryLimit {
154155
log.Info("set the memory limit", zap.Uint64("memLimit", memLimit))
155156
metrics.MemoryLimit.Set(float64(memLimit))
156157
lastMemoryLimit = memLimit
157158
}
158-
return nil
159+
return err
159160
}

pkg/util/cgmon/cgmon_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2022 PingCAP, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cgmon
16+
17+
import (
18+
"errors"
19+
"runtime"
20+
"testing"
21+
22+
"github.com/shirou/gopsutil/v3/mem"
23+
"github.com/stretchr/testify/require"
24+
)
25+
26+
func TestUploadDefaultValueWithoutCgroup(t *testing.T) {
27+
getCgroupCPUPeriodAndQuota = func() (period int64, quota int64, err error) {
28+
return 0, 0, errors.New("mock error")
29+
}
30+
getCgroupMemoryLimit = func() (uint64, error) {
31+
return 0, errors.New("mock error")
32+
}
33+
34+
require.Error(t, refreshCgroupCPU())
35+
require.Error(t, refreshCgroupMemory())
36+
37+
require.Equal(t, runtime.NumCPU(), lastCPU)
38+
vmem, err := mem.VirtualMemory()
39+
require.NoError(t, err)
40+
require.Equal(t, vmem.Total, lastMemoryLimit)
41+
}

0 commit comments

Comments
 (0)