Skip to content

Commit

Permalink
Merge pull request #83 from lunar-consultancy/main
Browse files Browse the repository at this point in the history
Make memory calculator work with cgroups v2
  • Loading branch information
Daniel Mikusa authored Aug 18, 2021
2 parents 19481b9 + 4023688 commit 43a36c4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
7 changes: 4 additions & 3 deletions cmd/helper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ func main() {
d = helper.LinkLocalDNS{Logger: l}
j = helper.JavaOpts{Logger: l}
m = helper.MemoryCalculator{
Logger: l,
MemoryLimitPath: helper.DefaultMemoryLimitPath,
MemoryInfoPath: helper.DefaultMemoryInfoPath,
Logger: l,
MemoryLimitPathV1: helper.DefaultMemoryLimitPathV1,
MemoryLimitPathV2: helper.DefaultMemoryLimitPathV2,
MemoryInfoPath: helper.DefaultMemoryInfoPath,
}
o = helper.OpenSSLCertificateLoader{CertificateLoader: cl, Logger: l}
s8 = helper.SecurityProvidersClasspath8{Logger: l}
Expand Down
48 changes: 28 additions & 20 deletions helper/memory_calculator.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,21 @@ import (
)

const (
ClassLoadFactor = 0.35
DefaultHeadroom = 0
DefaultMemoryLimitPath = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
DefaultMemoryInfoPath = "/proc/meminfo"
DefaultThreadCount = 250
MaxJVMSize = 64 * calc.Tebi
UnsetTotalMemory = int64(9_223_372_036_854_771_712)
ClassLoadFactor = 0.35
DefaultHeadroom = 0
DefaultMemoryLimitPathV1 = "/sys/fs/cgroup/memory/memory.limit_in_bytes"
DefaultMemoryLimitPathV2 = "/sys/fs/cgroup/memory.max"
DefaultMemoryInfoPath = "/proc/meminfo"
DefaultThreadCount = 250
MaxJVMSize = 64 * calc.Tebi
UnsetTotalMemory = int64(9_223_372_036_854_771_712)
)

type MemoryCalculator struct {
Logger bard.Logger
MemoryLimitPath string
MemoryInfoPath string
Logger bard.Logger
MemoryLimitPathV1 string
MemoryLimitPathV2 string
MemoryInfoPath string
}

func (m MemoryCalculator) Execute() (map[string]string, error) {
Expand Down Expand Up @@ -106,16 +108,9 @@ func (m MemoryCalculator) Execute() (map[string]string, error) {
}
}

totalMemory := UnsetTotalMemory

if b, err := ioutil.ReadFile(m.MemoryLimitPath); err != nil && !os.IsNotExist(err) {
m.Logger.Infof("WARNING: Unable to read %s: %s", m.MemoryLimitPath, err)
} else if err == nil {
if size, err := calc.ParseSize(strings.TrimSpace(string(b))); err != nil {
m.Logger.Infof("WARNING: Unable to convert memory limit %q from path %q as int: %s", strings.TrimSpace(string(b)), m.MemoryLimitPath, err)
} else {
totalMemory = size.Value
}
totalMemory := m.getMemoryLimitFromPath(m.MemoryLimitPathV1)
if totalMemory == UnsetTotalMemory {
totalMemory = m.getMemoryLimitFromPath(m.MemoryLimitPathV2)
}

if totalMemory == UnsetTotalMemory {
Expand Down Expand Up @@ -176,6 +171,19 @@ func (m MemoryCalculator) Execute() (map[string]string, error) {
return map[string]string{"JAVA_TOOL_OPTIONS": strings.Join(values, " ")}, nil
}

func (m MemoryCalculator) getMemoryLimitFromPath(memoryLimitPath string) int64 {
if b, err := ioutil.ReadFile(memoryLimitPath); err != nil && !os.IsNotExist(err) {
m.Logger.Infof("WARNING: Unable to read %s: %s", memoryLimitPath, err)
} else if err == nil {
if size, err := calc.ParseSize(strings.TrimSpace(string(b))); err != nil {
m.Logger.Infof("WARNING: Unable to convert memory limit %q from path %q as int: %s", strings.TrimSpace(string(b)), memoryLimitPath, err)
} else {
return size.Value
}
}
return UnsetTotalMemory
}

func parseMemInfo(s string) (int64, error) {
pattern := `MemAvailable:\s*(\d+)(.*)`
rp := regexp.MustCompile(pattern)
Expand Down
50 changes: 35 additions & 15 deletions helper/memory_calculator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ func testMemoryCalculator(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

applicationPath string
memoryLimitPath string
memoryInfoPath string
m helper.MemoryCalculator
applicationPath string
memoryLimitPathV1 string
memoryLimitPathV2 string
memoryInfoPath string
m helper.MemoryCalculator
)

it.Before(func() {
Expand All @@ -45,24 +46,35 @@ func testMemoryCalculator(t *testing.T, context spec.G, it spec.S) {
applicationPath, err = ioutil.TempDir("", "memory-calculator-application")
Expect(err).NotTo(HaveOccurred())

limit, err := ioutil.TempFile("", "memory-calculator-memory-limit")
limitV1, err := ioutil.TempFile("", "memory-calculator-memory-limit-v1")
Expect(err).NotTo(HaveOccurred())
Expect(limit.Close()).To(Succeed())
Expect(os.RemoveAll(limit.Name())).To(Succeed())
memoryLimitPath = limit.Name()
Expect(limitV1.Close()).To(Succeed())
Expect(os.RemoveAll(limitV1.Name())).To(Succeed())
memoryLimitPathV1 = limitV1.Name()

limitV2, err := ioutil.TempFile("", "memory-calculator-memory-limit-v2")
Expect(err).NotTo(HaveOccurred())
Expect(limitV2.Close()).To(Succeed())
Expect(os.RemoveAll(limitV2.Name())).To(Succeed())
memoryLimitPathV2 = limitV2.Name()

info, err := ioutil.TempFile("", "memory-calculator-memory-info")
Expect(err).NotTo(HaveOccurred())
Expect(info.Close()).To(Succeed())
Expect(os.RemoveAll(info.Name())).To(Succeed())
memoryInfoPath = info.Name()

m = helper.MemoryCalculator{MemoryLimitPath: memoryLimitPath, MemoryInfoPath: memoryInfoPath}
m = helper.MemoryCalculator{
MemoryLimitPathV1: memoryLimitPathV1,
MemoryLimitPathV2: memoryLimitPathV2,
MemoryInfoPath: memoryInfoPath,
}
})

it.After(func() {
Expect(os.RemoveAll(applicationPath)).To(Succeed())
Expect(os.RemoveAll(memoryLimitPath)).To(Succeed())
Expect(os.RemoveAll(memoryLimitPathV1)).To(Succeed())
Expect(os.RemoveAll(memoryLimitPathV2)).To(Succeed())
})

it("returns error if $BPI_APPLICATION_PATH is not set", func() {
Expand Down Expand Up @@ -191,7 +203,7 @@ func testMemoryCalculator(t *testing.T, context spec.G, it spec.S) {
Buffers: 112396 kB
`

Expect(ioutil.WriteFile(memoryLimitPath, strconv.AppendInt([]byte{}, helper.UnsetTotalMemory, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryLimitPathV1, strconv.AppendInt([]byte{}, helper.UnsetTotalMemory, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryInfoPath, []byte(s), 0755)).To(Succeed())

Expect(m.Execute()).To(Equal(map[string]string{
Expand All @@ -207,7 +219,7 @@ func testMemoryCalculator(t *testing.T, context spec.G, it spec.S) {
Buffers: 112396 kB
`

Expect(ioutil.WriteFile(memoryLimitPath, strconv.AppendInt([]byte{}, helper.UnsetTotalMemory, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryLimitPathV1, strconv.AppendInt([]byte{}, helper.UnsetTotalMemory, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryInfoPath, []byte(s), 0755)).To(Succeed())

Expect(m.Execute()).To(Equal(map[string]string{
Expand All @@ -216,29 +228,37 @@ func testMemoryCalculator(t *testing.T, context spec.G, it spec.S) {
})

it("limits total memory to 1G if unable to determine total memory", func() {
Expect(ioutil.WriteFile(memoryLimitPath, strconv.AppendInt([]byte{}, helper.UnsetTotalMemory, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryLimitPathV1, strconv.AppendInt([]byte{}, helper.UnsetTotalMemory, 10), 0755)).To(Succeed())

Expect(m.Execute()).To(Equal(map[string]string{
"JAVA_TOOL_OPTIONS": "-XX:MaxDirectMemorySize=10M -Xmx522705K -XX:MaxMetaspaceSize=13870K -XX:ReservedCodeCacheSize=240M -Xss1M",
}))
})

it("limits total memory to 64T", func() {
Expect(ioutil.WriteFile(memoryLimitPath, strconv.AppendInt([]byte{}, helper.MaxJVMSize+1, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryLimitPathV1, strconv.AppendInt([]byte{}, helper.MaxJVMSize+1, 10), 0755)).To(Succeed())

Expect(m.Execute()).To(Equal(map[string]string{
"JAVA_TOOL_OPTIONS": "-XX:MaxDirectMemorySize=10M -Xmx68718950865K -XX:MaxMetaspaceSize=13870K -XX:ReservedCodeCacheSize=240M -Xss1M",
}))
})

it("limits total memory to container size if set", func() {
Expect(ioutil.WriteFile(memoryLimitPath, strconv.AppendInt([]byte{}, 10*calc.Gibi, 10), 0755)).To(Succeed())
Expect(ioutil.WriteFile(memoryLimitPathV1, strconv.AppendInt([]byte{}, 10*calc.Gibi, 10), 0755)).To(Succeed())

Expect(m.Execute()).To(Equal(map[string]string{
"JAVA_TOOL_OPTIONS": "-XX:MaxDirectMemorySize=10M -Xmx9959889K -XX:MaxMetaspaceSize=13870K -XX:ReservedCodeCacheSize=240M -Xss1M",
}))
})

it("limits total memory to container size if V2 set", func() {
Expect(ioutil.WriteFile(memoryLimitPathV2, strconv.AppendInt([]byte{}, 11*calc.Gibi, 10), 0755)).To(Succeed())

Expect(m.Execute()).To(Equal(map[string]string{
"JAVA_TOOL_OPTIONS": "-XX:MaxDirectMemorySize=10M -Xmx11008465K -XX:MaxMetaspaceSize=13870K -XX:ReservedCodeCacheSize=240M -Xss1M",
}))
})

context("$JAVA_TOOL_OPTIONS", func() {
it.Before(func() {
Expect(os.Setenv("JAVA_TOOL_OPTIONS", "test-java-tool-options")).To(Succeed())
Expand Down

0 comments on commit 43a36c4

Please sign in to comment.