Skip to content

Instantly share code, notes, and snippets.

@ph1048
Created March 5, 2023 05:55
Show Gist options
  • Save ph1048/9165c9214d4d19866b355ff88ce63ad5 to your computer and use it in GitHub Desktop.
Save ph1048/9165c9214d4d19866b355ff88ce63ad5 to your computer and use it in GitHub Desktop.
package nospike
import (
"bufio"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"syscall"
"golang.org/x/sys/unix"
)
type Nospike interface {
AddSelf()
SetCpuLimit(cpuPercentage float64)
GetThrottleDelta() uint64
Dispose()
}
type nospikeInstance struct {
mountdir string
cgId string
nrPeriods uint64
nrThrottled uint64
throttledTime uint64
}
const resolution float64 = 20000
func (c *nospikeInstance) AddSelf() {
err := ioutil.WriteFile(filepath.Join(c.mountdir, c.cgId, "cgroup.procs"), []byte("0"), 0)
if err != nil {
panic(err)
}
}
func (c *nospikeInstance) SetCpuLimit(cpuPercentage float64) {
quota := resolution * cpuPercentage / 100
err := ioutil.WriteFile(filepath.Join(c.mountdir, c.cgId, "cpu.cfs_quota_us"), []byte(strconv.FormatFloat(quota, 'f', 0, 64)), 0)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(filepath.Join(c.mountdir, c.cgId, "cpu.cfs_period_us"), []byte(strconv.FormatFloat(resolution, 'f', 0, 64)), 0)
if err != nil {
panic(err)
}
}
func (c *nospikeInstance) Dispose() {
unix.Unmount(c.mountdir, unix.UMOUNT_NOFOLLOW|unix.MNT_FORCE)
os.Remove(c.mountdir)
}
func (c *nospikeInstance) GetThrottleDelta() uint64 {
var nrPeriodsOld uint64 = c.nrPeriods
var nrThrottledOld uint64 = c.nrThrottled
var throttledTimeOld uint64 = c.throttledTime
f, err := os.Open(filepath.Join(c.mountdir, c.cgId, "cpu.stat"))
if err != nil {
log.Fatal(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
var key string
var val uint64
cnt, err := fmt.Sscanf(scanner.Text(), "%s %d", &key, &val)
if err != nil || cnt != 2 {
continue
}
switch key {
case "nr_periods":
c.nrPeriods = val
case "nr_throttled":
c.nrThrottled = val
case "throttled_time":
c.throttledTime = val
}
}
if nrThrottledOld == 0 {
return 0
}
//fmt.Println("time:", c.throttledTime, c.nrThrottled, c.nrPeriods)
delta := c.throttledTime - throttledTimeOld
deltap := c.nrPeriods - nrPeriodsOld
deltat := c.nrThrottled - nrThrottledOld
_ = delta
_ = deltap
return deltat
}
func New(cgId string) (Nospike, error) {
inst := &nospikeInstance{
cgId: cgId,
}
tmpdir, err := ioutil.TempDir("", "nspmount-*")
if err != nil {
return nil, err
}
inst.mountdir = tmpdir
err = unix.Mount("cpu", tmpdir, "cgroup", 0, "cpu")
if err != nil {
inst.Dispose()
return nil, err
}
err = os.Mkdir(filepath.Join(tmpdir, cgId), 0)
if err != nil && errors.Unwrap(err) != syscall.EEXIST {
inst.Dispose()
return nil, err
}
return inst, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment