Skip to content

Commit 959f581

Browse files
authoredOct 16, 2024··
Read cgroups for resource tuner limits (#1632)
1 parent cfc38de commit 959f581

File tree

8 files changed

+350
-34
lines changed

8 files changed

+350
-34
lines changed
 

‎contrib/resourcetuner/cgroups.go

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
4+
//
5+
// Copyright (c) 2020 Uber Technologies, Inc.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
25+
//go:build linux
26+
27+
package resourcetuner
28+
29+
import (
30+
"errors"
31+
"fmt"
32+
"io/fs"
33+
"os"
34+
"strconv"
35+
"strings"
36+
"time"
37+
38+
"github.com/containerd/cgroups/v3/cgroup2"
39+
"github.com/containerd/cgroups/v3/cgroup2/stats"
40+
)
41+
42+
func newCGroupInfo() cGroupInfo {
43+
return &cGroupInfoImpl{}
44+
}
45+
46+
type cGroupInfoImpl struct {
47+
lastCGroupMemStat *stats.MemoryStat
48+
cgroupCpuCalc cgroupCpuCalc
49+
}
50+
51+
func (p *cGroupInfoImpl) Update() (bool, error) {
52+
err := p.updateCGroupStats()
53+
// Stop updates if not in a container. No need to return the error and log it.
54+
if !errors.Is(err, fs.ErrNotExist) {
55+
return false, nil
56+
} else if err != nil {
57+
return true, err
58+
}
59+
return true, nil
60+
}
61+
62+
func (p *cGroupInfoImpl) GetLastMemUsage() float64 {
63+
if p.lastCGroupMemStat != nil {
64+
return float64(p.lastCGroupMemStat.Usage) / float64(p.lastCGroupMemStat.UsageLimit)
65+
}
66+
return 0
67+
}
68+
69+
func (p *cGroupInfoImpl) GetLastCPUUsage() float64 {
70+
return p.cgroupCpuCalc.lastCalculatedPercent
71+
}
72+
73+
func (p *cGroupInfoImpl) updateCGroupStats() error {
74+
control, err := cgroup2.Load("/")
75+
if err != nil {
76+
return fmt.Errorf("failed to get cgroup mem stats %v", err)
77+
}
78+
metrics, err := control.Stat()
79+
if err != nil {
80+
return fmt.Errorf("failed to get cgroup mem stats %v", err)
81+
}
82+
// Only update if a limit has been set
83+
if metrics.Memory.UsageLimit != 0 {
84+
p.lastCGroupMemStat = metrics.Memory
85+
}
86+
87+
err = p.cgroupCpuCalc.updateCpuUsage(metrics)
88+
if err != nil {
89+
return fmt.Errorf("failed to get cgroup cpu usage %v", err)
90+
}
91+
return nil
92+
}
93+
94+
type cgroupCpuCalc struct {
95+
lastRefresh time.Time
96+
lastCpuUsage uint64
97+
lastCalculatedPercent float64
98+
}
99+
100+
func (p *cgroupCpuCalc) updateCpuUsage(metrics *stats.Metrics) error {
101+
// Read CPU quota and period from cpu.max
102+
cpuQuota, cpuPeriod, err := readCpuMax("/sys/fs/cgroup/cpu.max")
103+
// We might simply be in a container with an unset cpu.max in which case we don't want to error
104+
if err == nil {
105+
// CPU usage calculation based on delta
106+
currentCpuUsage := metrics.CPU.UsageUsec
107+
now := time.Now()
108+
109+
if p.lastCpuUsage == 0 || p.lastRefresh.IsZero() {
110+
p.lastCpuUsage = currentCpuUsage
111+
p.lastRefresh = now
112+
return nil
113+
}
114+
115+
// Time passed between this and last check
116+
timeDelta := now.Sub(p.lastRefresh).Microseconds() // Convert to microseconds
117+
118+
// Calculate CPU usage percentage based on the delta
119+
cpuUsageDelta := float64(currentCpuUsage - p.lastCpuUsage)
120+
121+
if cpuQuota > 0 {
122+
p.lastCalculatedPercent = cpuUsageDelta * float64(cpuPeriod) / float64(cpuQuota*timeDelta)
123+
}
124+
125+
// Update for next call
126+
p.lastCpuUsage = currentCpuUsage
127+
p.lastRefresh = now
128+
}
129+
130+
return nil
131+
}
132+
133+
// readCpuMax reads the cpu.max file to get the CPU quota and period
134+
func readCpuMax(path string) (quota int64, period int64, err error) {
135+
data, err := os.ReadFile(path)
136+
if err != nil {
137+
return 0, 0, err
138+
}
139+
parts := strings.Fields(string(data))
140+
if len(parts) != 2 {
141+
return 0, 0, errors.New("invalid format in cpu.max")
142+
}
143+
144+
// Parse the quota (first value)
145+
if parts[0] == "max" {
146+
quota = 0 // Unlimited quota
147+
} else {
148+
quota, err = strconv.ParseInt(parts[0], 10, 64)
149+
if err != nil {
150+
return 0, 0, err
151+
}
152+
}
153+
154+
// Parse the period (second value)
155+
period, err = strconv.ParseInt(parts[1], 10, 64)
156+
if err != nil {
157+
return 0, 0, err
158+
}
159+
160+
return quota, period, nil
161+
}
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// The MIT License
2+
//
3+
// Copyright (c) 2020 Temporal Technologies Inc. All rights reserved.
4+
//
5+
// Copyright (c) 2020 Uber Technologies, Inc.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
25+
//go:build !linux
26+
27+
package resourcetuner
28+
29+
import "errors"
30+
31+
func newCGroupInfo() cGroupInfo {
32+
return &cGroupInfoImpl{}
33+
}
34+
35+
type cGroupInfoImpl struct {
36+
}
37+
38+
func (p *cGroupInfoImpl) Update() (bool, error) {
39+
return false, errors.New("cgroup is not supported on this platform")
40+
}
41+
42+
func (p *cGroupInfoImpl) GetLastMemUsage() float64 {
43+
return 0
44+
}
45+
46+
func (p *cGroupInfoImpl) GetLastCPUUsage() float64 {
47+
return 0
48+
}

‎contrib/resourcetuner/go.mod

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,34 @@ go 1.21
55
toolchain go1.22.5
66

77
require (
8-
github.com/shirou/gopsutil/v4 v4.24.6
8+
github.com/containerd/cgroups/v3 v3.0.3
9+
github.com/shirou/gopsutil/v4 v4.24.8
910
github.com/stretchr/testify v1.9.0
1011
go.einride.tech/pid v0.1.3
11-
go.temporal.io/sdk v1.28.1
12+
go.temporal.io/sdk v1.29.1
1213
)
1314

1415
require (
16+
github.com/cilium/ebpf v0.11.0 // indirect
17+
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
1518
github.com/davecgh/go-spew v1.1.1 // indirect
1619
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
1720
github.com/go-ole/go-ole v1.2.6 // indirect
21+
github.com/godbus/dbus/v5 v5.0.4 // indirect
1822
github.com/gogo/protobuf v1.3.2 // indirect
1923
github.com/golang/mock v1.6.0 // indirect
2024
github.com/google/uuid v1.6.0 // indirect
2125
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
2226
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
2327
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
2428
github.com/nexus-rpc/sdk-go v0.0.10 // indirect
29+
github.com/opencontainers/runtime-spec v1.0.2 // indirect
2530
github.com/pborman/uuid v1.2.1 // indirect
2631
github.com/pmezard/go-difflib v1.0.0 // indirect
2732
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
2833
github.com/robfig/cron v1.2.0 // indirect
2934
github.com/shoenig/go-m1cpu v0.1.6 // indirect
35+
github.com/sirupsen/logrus v1.9.0 // indirect
3036
github.com/stretchr/objx v0.5.2 // indirect
3137
github.com/tklauser/go-sysconf v0.3.12 // indirect
3238
github.com/tklauser/numcpus v0.6.1 // indirect

‎contrib/resourcetuner/go.sum

+19-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
22
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
33
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
44
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
5+
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
6+
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
57
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
68
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
9+
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
10+
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
11+
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
12+
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
713
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
814
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
915
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -13,11 +19,15 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
1319
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
1420
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
1521
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
22+
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
23+
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
1624
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
1725
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
1826
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
1927
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
2028
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
29+
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
30+
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
2131
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
2232
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
2333
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -54,6 +64,8 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
5464
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
5565
github.com/nexus-rpc/sdk-go v0.0.10 h1:7jEPUlsghxoD4OJ2H8YbFJ1t4wbxsUef7yZgBfyY3uA=
5666
github.com/nexus-rpc/sdk-go v0.0.10/go.mod h1:TpfkM2Cw0Rlk9drGkoiSMpFqflKTiQLWUNyKJjF8mKQ=
67+
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
68+
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
5769
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
5870
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
5971
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@@ -67,13 +79,15 @@ github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
6779
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
6880
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
6981
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
70-
github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64=
71-
github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
82+
github.com/shirou/gopsutil/v4 v4.24.8 h1:pVQjIenQkIhqO81mwTaXjTzOMT7d3TZkf43PlVFHENI=
83+
github.com/shirou/gopsutil/v4 v4.24.8/go.mod h1:wE0OrJtj4dG+hYkxqDH3QiBICdKSf04/npcvLLc/oRg=
7284
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
7385
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
7486
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
7587
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
7688
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
89+
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
90+
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
7791
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7892
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
7993
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
@@ -99,6 +113,8 @@ go.temporal.io/api v1.39.0 h1:pbhcfvNDB7mllb8lIBqPcg+m6LMG/IhTpdiFxe+0mYk=
99113
go.temporal.io/api v1.39.0/go.mod h1:1WwYUMo6lao8yl0371xWUm13paHExN5ATYT/B7QtFis=
100114
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
101115
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
116+
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
117+
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
102118
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
103119
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
104120
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -145,6 +161,7 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
145161
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
146162
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
147163
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
164+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
148165
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
149166
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
150167
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=

‎contrib/resourcetuner/resourcetuner.go

+78-17
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,30 @@ package resourcetuner
2525
import (
2626
"context"
2727
"errors"
28+
"runtime"
2829
"sync"
2930
"time"
3031

3132
"github.com/shirou/gopsutil/v4/cpu"
3233
"github.com/shirou/gopsutil/v4/mem"
3334
"go.einride.tech/pid"
35+
"go.temporal.io/sdk/log"
3436
"go.temporal.io/sdk/worker"
3537
)
3638

3739
type ResourceBasedTunerOptions struct {
40+
// TargetMem is the target overall system memory usage as value 0 and 1 that the controller will
41+
// attempt to maintain. Must be set nonzero.
3842
TargetMem float64
43+
// TargetCpu is the target overall system CPU usage as value 0 and 1 that the controller will
44+
// attempt to maintain. Must be set nonzero.
3945
TargetCpu float64
46+
// Passed to ResourceBasedSlotSupplierOptions.RampThrottle for activities.
47+
// If not set, the default value is 50ms.
48+
ActivityRampThrottle time.Duration
49+
// Passed to ResourceBasedSlotSupplierOptions.RampThrottle for workflows.
50+
// If not set, the default value is 0ms.
51+
WorkflowRampThrottle time.Duration
4052
}
4153

4254
// NewResourceBasedTuner creates a WorkerTuner that dynamically adjusts the number of slots based
@@ -50,10 +62,19 @@ func NewResourceBasedTuner(opts ResourceBasedTunerOptions) (worker.WorkerTuner,
5062
controller := NewResourceController(options)
5163
wfSS := &ResourceBasedSlotSupplier{controller: controller,
5264
options: defaultWorkflowResourceBasedSlotSupplierOptions()}
65+
if opts.WorkflowRampThrottle != 0 {
66+
wfSS.options.RampThrottle = opts.WorkflowRampThrottle
67+
}
5368
actSS := &ResourceBasedSlotSupplier{controller: controller,
5469
options: defaultActivityResourceBasedSlotSupplierOptions()}
70+
if opts.ActivityRampThrottle != 0 {
71+
actSS.options.RampThrottle = opts.ActivityRampThrottle
72+
}
5573
laSS := &ResourceBasedSlotSupplier{controller: controller,
5674
options: defaultActivityResourceBasedSlotSupplierOptions()}
75+
if opts.ActivityRampThrottle != 0 {
76+
laSS.options.RampThrottle = opts.ActivityRampThrottle
77+
}
5778
nexusSS := &ResourceBasedSlotSupplier{controller: controller,
5879
options: defaultWorkflowResourceBasedSlotSupplierOptions()}
5980
compositeTuner, err := worker.NewCompositeTuner(worker.CompositeTunerOptions{
@@ -163,7 +184,7 @@ func (r *ResourceBasedSlotSupplier) TryReserveSlot(info worker.SlotReservationIn
163184
numIssued := info.NumIssuedSlots()
164185
if numIssued < r.options.MinSlots || (numIssued < r.options.MaxSlots &&
165186
time.Since(r.lastSlotIssuedAt) > r.options.RampThrottle) {
166-
decision, err := r.controller.pidDecision()
187+
decision, err := r.controller.pidDecision(info.Logger())
167188
if err != nil {
168189
info.Logger().Error("Error calculating resource usage", "error", err)
169190
return nil
@@ -188,10 +209,14 @@ func (r *ResourceBasedSlotSupplier) MaxSlots() int {
188209
type SystemInfoSupplier interface {
189210
// GetMemoryUsage returns the current system memory usage as a fraction of total memory between
190211
// 0 and 1.
191-
GetMemoryUsage() (float64, error)
212+
GetMemoryUsage(infoContext *SystemInfoContext) (float64, error)
192213
// GetCpuUsage returns the current system CPU usage as a fraction of total CPU usage between 0
193214
// and 1.
194-
GetCpuUsage() (float64, error)
215+
GetCpuUsage(infoContext *SystemInfoContext) (float64, error)
216+
}
217+
218+
type SystemInfoContext struct {
219+
Logger log.Logger
195220
}
196221

197222
// ResourceControllerOptions contains configurable parameters for a ResourceController.
@@ -262,7 +287,9 @@ type ResourceController struct {
262287
func NewResourceController(options ResourceControllerOptions) *ResourceController {
263288
var infoSupplier SystemInfoSupplier
264289
if options.InfoSupplier == nil {
265-
infoSupplier = &psUtilSystemInfoSupplier{}
290+
infoSupplier = &psUtilSystemInfoSupplier{
291+
cGroupInfo: newCGroupInfo(),
292+
}
266293
} else {
267294
infoSupplier = options.InfoSupplier
268295
}
@@ -286,23 +313,22 @@ func NewResourceController(options ResourceControllerOptions) *ResourceControlle
286313
}
287314
}
288315

289-
func (rc *ResourceController) pidDecision() (bool, error) {
316+
func (rc *ResourceController) pidDecision(logger log.Logger) (bool, error) {
290317
rc.mu.Lock()
291318
defer rc.mu.Unlock()
292319

293-
memUsage, err := rc.infoSupplier.GetMemoryUsage()
320+
memUsage, err := rc.infoSupplier.GetMemoryUsage(&SystemInfoContext{Logger: logger})
294321
if err != nil {
295322
return false, err
296323
}
297-
cpuUsage, err := rc.infoSupplier.GetCpuUsage()
324+
cpuUsage, err := rc.infoSupplier.GetCpuUsage(&SystemInfoContext{Logger: logger})
298325
if err != nil {
299326
return false, err
300327
}
301328
if memUsage >= rc.options.MemTargetPercent {
302329
// Never allow going over the memory target
303330
return false, nil
304331
}
305-
//fmt.Printf("mem: %f, cpu: %f\n", memUsage, cpuUsage)
306332
elapsedTime := time.Since(rc.lastRefresh)
307333
// This shouldn't be possible with real implementations, but if the elapsed time is 0 the
308334
// PID controller can produce NaNs.
@@ -326,27 +352,54 @@ func (rc *ResourceController) pidDecision() (bool, error) {
326352
}
327353

328354
type psUtilSystemInfoSupplier struct {
329-
mu sync.Mutex
355+
logger log.Logger
356+
mu sync.Mutex
357+
lastRefresh time.Time
358+
330359
lastMemStat *mem.VirtualMemoryStat
331360
lastCpuUsage float64
332-
lastRefresh time.Time
361+
362+
stopTryingToGetCGroupInfo bool
363+
cGroupInfo cGroupInfo
333364
}
334365

335-
func (p *psUtilSystemInfoSupplier) GetMemoryUsage() (float64, error) {
336-
if err := p.maybeRefresh(); err != nil {
366+
type cGroupInfo interface {
367+
// Update requests an update of the cgroup stats. This is a no-op if not in a cgroup. Returns
368+
// true if cgroup stats should continue to be updated, false if not in a cgroup or the returned
369+
// error is considered unrecoverable.
370+
Update() (bool, error)
371+
// GetLastMemUsage returns last known memory usage as a fraction of the cgroup limit. 0 if not
372+
// in a cgroup or limit is not set.
373+
GetLastMemUsage() float64
374+
// GetLastCPUUsage returns last known CPU usage as a fraction of the cgroup limit. 0 if not in a
375+
// cgroup or limit is not set.
376+
GetLastCPUUsage() float64
377+
}
378+
379+
func (p *psUtilSystemInfoSupplier) GetMemoryUsage(infoContext *SystemInfoContext) (float64, error) {
380+
if err := p.maybeRefresh(infoContext); err != nil {
337381
return 0, err
338382
}
383+
lastCGroupMem := p.cGroupInfo.GetLastMemUsage()
384+
if lastCGroupMem != 0 {
385+
return lastCGroupMem, nil
386+
}
339387
return p.lastMemStat.UsedPercent / 100, nil
340388
}
341389

342-
func (p *psUtilSystemInfoSupplier) GetCpuUsage() (float64, error) {
343-
if err := p.maybeRefresh(); err != nil {
390+
func (p *psUtilSystemInfoSupplier) GetCpuUsage(infoContext *SystemInfoContext) (float64, error) {
391+
if err := p.maybeRefresh(infoContext); err != nil {
344392
return 0, err
345393
}
394+
395+
lastCGroupCPU := p.cGroupInfo.GetLastCPUUsage()
396+
if lastCGroupCPU != 0 {
397+
return lastCGroupCPU, nil
398+
}
346399
return p.lastCpuUsage / 100, nil
347400
}
348401

349-
func (p *psUtilSystemInfoSupplier) maybeRefresh() error {
402+
func (p *psUtilSystemInfoSupplier) maybeRefresh(infoContext *SystemInfoContext) error {
350403
if time.Since(p.lastRefresh) < 100*time.Millisecond {
351404
return nil
352405
}
@@ -360,16 +413,24 @@ func (p *psUtilSystemInfoSupplier) maybeRefresh() error {
360413
defer cancelFn()
361414
memStat, err := mem.VirtualMemoryWithContext(ctx)
362415
if err != nil {
363-
println("Refresh error: ", err)
364416
return err
365417
}
366418
cpuUsage, err := cpu.PercentWithContext(ctx, 0, false)
367419
if err != nil {
368-
println("Refresh error: ", err)
369420
return err
370421
}
422+
371423
p.lastMemStat = memStat
372424
p.lastCpuUsage = cpuUsage[0]
425+
426+
if runtime.GOOS == "linux" && !p.stopTryingToGetCGroupInfo {
427+
continueUpdates, err := p.cGroupInfo.Update()
428+
if err != nil {
429+
infoContext.Logger.Warn("Failed to get cgroup stats", "error", err)
430+
}
431+
p.stopTryingToGetCGroupInfo = !continueUpdates
432+
}
433+
373434
p.lastRefresh = time.Now()
374435
return nil
375436
}

‎contrib/resourcetuner/resourcetuner_test.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,24 @@ import (
2626
"testing"
2727

2828
"github.com/stretchr/testify/assert"
29+
"go.temporal.io/sdk/internal/log"
2930
)
3031

3132
type FakeSystemInfoSupplier struct {
3233
memUse float64
3334
cpuUse float64
3435
}
3536

36-
func (f FakeSystemInfoSupplier) GetMemoryUsage() (float64, error) {
37+
func (f FakeSystemInfoSupplier) GetMemoryUsage(_ *SystemInfoContext) (float64, error) {
3738
return f.memUse, nil
3839
}
3940

40-
func (f FakeSystemInfoSupplier) GetCpuUsage() (float64, error) {
41+
func (f FakeSystemInfoSupplier) GetCpuUsage(_ *SystemInfoContext) (float64, error) {
4142
return f.cpuUse, nil
4243
}
4344

4445
func TestPidDecisions(t *testing.T) {
46+
logger := &log.NoopLogger{}
4547
fakeSupplier := &FakeSystemInfoSupplier{memUse: 0.5, cpuUse: 0.5}
4648
rcOpts := DefaultResourceControllerOptions()
4749
rcOpts.MemTargetPercent = 0.8
@@ -50,7 +52,7 @@ func TestPidDecisions(t *testing.T) {
5052
rc := NewResourceController(rcOpts)
5153

5254
for i := 0; i < 10; i++ {
53-
decision, err := rc.pidDecision()
55+
decision, err := rc.pidDecision(logger)
5456
assert.NoError(t, err)
5557
assert.True(t, decision)
5658

@@ -61,23 +63,23 @@ func TestPidDecisions(t *testing.T) {
6163
fakeSupplier.memUse = 0.8
6264
fakeSupplier.cpuUse = 0.9
6365
for i := 0; i < 10; i++ {
64-
decision, err := rc.pidDecision()
66+
decision, err := rc.pidDecision(logger)
6567
assert.NoError(t, err)
6668
assert.False(t, decision)
6769
}
6870

6971
fakeSupplier.memUse = 0.7
7072
fakeSupplier.cpuUse = 0.9
7173
for i := 0; i < 10; i++ {
72-
decision, err := rc.pidDecision()
74+
decision, err := rc.pidDecision(logger)
7375
assert.NoError(t, err)
7476
assert.False(t, decision)
7577
}
7678

7779
fakeSupplier.memUse = 0.7
7880
fakeSupplier.cpuUse = 0.7
7981
for i := 0; i < 10; i++ {
80-
decision, err := rc.pidDecision()
82+
decision, err := rc.pidDecision(logger)
8183
assert.NoError(t, err)
8284
assert.True(t, decision)
8385
}

‎test/go.mod

+9-3
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,37 @@ require (
1616
go.opentelemetry.io/otel/sdk v1.28.0
1717
go.opentelemetry.io/otel/trace v1.28.0
1818
go.temporal.io/api v1.39.0
19-
go.temporal.io/sdk v1.28.1
19+
go.temporal.io/sdk v1.29.1
2020
go.temporal.io/sdk/contrib/opentelemetry v0.1.0
2121
go.temporal.io/sdk/contrib/opentracing v0.0.0-00010101000000-000000000000
2222
go.temporal.io/sdk/contrib/resourcetuner v0.1.0
2323
go.temporal.io/sdk/contrib/tally v0.1.0
24-
go.uber.org/goleak v1.1.11
24+
go.uber.org/goleak v1.1.12
2525
google.golang.org/grpc v1.66.0
2626
google.golang.org/protobuf v1.34.2
2727
)
2828

2929
require (
30+
github.com/cilium/ebpf v0.11.0 // indirect
31+
github.com/containerd/cgroups/v3 v3.0.3 // indirect
32+
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
3033
github.com/davecgh/go-spew v1.1.1 // indirect
3134
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
3235
github.com/go-logr/logr v1.4.2 // indirect
3336
github.com/go-logr/stdr v1.2.2 // indirect
3437
github.com/go-ole/go-ole v1.2.6 // indirect
38+
github.com/godbus/dbus/v5 v5.0.4 // indirect
3539
github.com/gogo/protobuf v1.3.2 // indirect
3640
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
3741
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
3842
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
43+
github.com/opencontainers/runtime-spec v1.0.2 // indirect
3944
github.com/pmezard/go-difflib v1.0.0 // indirect
4045
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
4146
github.com/robfig/cron v1.2.0 // indirect
42-
github.com/shirou/gopsutil/v4 v4.24.6 // indirect
47+
github.com/shirou/gopsutil/v4 v4.24.8 // indirect
4348
github.com/shoenig/go-m1cpu v0.1.6 // indirect
49+
github.com/sirupsen/logrus v1.9.0 // indirect
4450
github.com/stretchr/objx v0.5.2 // indirect
4551
github.com/tklauser/go-sysconf v0.3.12 // indirect
4652
github.com/tklauser/numcpus v0.6.1 // indirect

‎test/go.sum

+19-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
1313
github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI=
1414
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
1515
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
16+
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
17+
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
1618
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
1719
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
20+
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
21+
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
22+
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
23+
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
1824
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1925
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2026
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -24,6 +30,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
2430
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
2531
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
2632
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
33+
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
34+
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
2735
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
2836
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
2937
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -38,6 +46,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
3846
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
3947
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
4048
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
49+
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
50+
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
4151
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
4252
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
4353
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -104,6 +114,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
104114
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
105115
github.com/nexus-rpc/sdk-go v0.0.10 h1:7jEPUlsghxoD4OJ2H8YbFJ1t4wbxsUef7yZgBfyY3uA=
106116
github.com/nexus-rpc/sdk-go v0.0.10/go.mod h1:TpfkM2Cw0Rlk9drGkoiSMpFqflKTiQLWUNyKJjF8mKQ=
117+
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
118+
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
107119
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
108120
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
109121
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
@@ -135,15 +147,17 @@ github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
135147
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
136148
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
137149
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
138-
github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64=
139-
github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
150+
github.com/shirou/gopsutil/v4 v4.24.8 h1:pVQjIenQkIhqO81mwTaXjTzOMT7d3TZkf43PlVFHENI=
151+
github.com/shirou/gopsutil/v4 v4.24.8/go.mod h1:wE0OrJtj4dG+hYkxqDH3QiBICdKSf04/npcvLLc/oRg=
140152
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
141153
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
142154
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
143155
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
144156
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
145157
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
146158
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
159+
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
160+
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
147161
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
148162
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
149163
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
@@ -185,8 +199,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
185199
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
186200
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
187201
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
188-
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
189-
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
202+
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
203+
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
190204
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
191205
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
192206
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -250,6 +264,7 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
250264
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
251265
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
252266
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
267+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
253268
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
254269
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
255270
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=

0 commit comments

Comments
 (0)
Please sign in to comment.