Skip to content

Commit

Permalink
Merge pull request #379 from hibell/create-scc
Browse files Browse the repository at this point in the history
Build server and app shared class cache
  • Loading branch information
dmikusa authored Mar 8, 2023
2 parents 0c44e32 + e574427 commit 6e6246f
Show file tree
Hide file tree
Showing 6 changed files with 306 additions and 11 deletions.
28 changes: 28 additions & 0 deletions buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,34 @@ api = "0.7"
launch = false
name = "BP_LIBERTY_FEATURE_INSTALL_DISABLED"

[[metadata.configurations]]
build = true
default = "false"
description = "OpenJ9 only: Disable building the shared class cache."
launch = false
name = "BP_LIBERTY_SCC_DISABLED"

[[metadata.configurations]]
build = true
default = "100"
description = "OpenJ9 only: Size to use for the shared class cache."
launch = false
name = "BP_LIBERTY_SCC_SIZE_MB"

[[metadata.configurations]]
build = true
default = "1"
description = "OpenJ9 only: Number of iterations to cycle the server when building the shared class cache."
launch = false
name = "BP_LIBERTY_SCC_NUM_ITERATIONS"

[[metadata.configurations]]
build = true
default = "false"
description = "OpenJ9 only: Disable trimming the size of the shared class cache."
launch = false
name = "BP_LIBERTY_SCC_TRIM_SIZE_DISABLED"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:open_liberty:23.0.0.2:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-full"
Expand Down
117 changes: 117 additions & 0 deletions internal/util/jvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ package util
import (
"bufio"
"bytes"
"errors"
"fmt"
"github.com/paketo-buildpacks/libpak/bard"
"github.com/paketo-buildpacks/libpak/effect"
"io"
"os/exec"
"regexp"
"strconv"
"strings"
)

// DetectJVMName returns the value for java.vm.name.
func DetectJVMName(executor effect.Executor) (string, error) {
buf := &bytes.Buffer{}

Expand All @@ -47,5 +54,115 @@ func DetectJVMName(executor effect.Executor) (string, error) {
break
}
}

if err := scanner.Err(); err != nil {
return "", fmt.Errorf("unable to read JVM name\n%w", err)
}

return "", nil
}

type SharedClassCache struct {
Name string
Path string

Executor effect.Executor
Logger bard.Logger
}

type SharedClassCacheOptions struct {
Enabled bool
SizeMB int
NumIterations int
Trim bool
}

func (scc SharedClassCache) GetFillRatio() (float64, error) {
sccOption := fmt.Sprintf("-Xshareclasses:name=%s,cacheDir=%s,printTopLayerStats", scc.Name, scc.Path)
args := []string{sccOption, "-version"}
buf := &bytes.Buffer{}
stderrBuf := &bytes.Buffer{}
err := scc.Executor.Execute(effect.Execution{
Command: "java",
Args: args,
Stdout: buf,
Stderr: stderrBuf,
})
var exitError *exec.ExitError
if errors.As(err, &exitError) && exitError.ExitCode() != 1 {
return 0.0, fmt.Errorf("unable to get cache stats\n%w\n%s", err, stderrBuf.String())
}

r := regexp.MustCompile(`Cache is (\d+)% full`)

scanner := bufio.NewScanner(buf)
for scanner.Scan() {
line := scanner.Text()
scc.Logger.Debugf("%s\n", line)
if matches := r.FindStringSubmatch(line); matches != nil {
v, err := strconv.ParseInt(matches[1], 10, 64)
if err != nil {
return 0.0, fmt.Errorf("unable to parse [%s] to int\n%w", matches[1], err)
}
return float64(v) / 100, nil
}
}

if err := scanner.Err(); err != nil {
return 0.0, fmt.Errorf("unable to read cache ratio\n%w", err)
}

return 0.0, fmt.Errorf("unable to find cache fill ratio")
}

func (scc SharedClassCache) CreateLayer(size int) error {
scc.Logger.Bodyf("Creating SCC layer with size %dm", size)
return scc.execSccCommand("createLayer,groupAccess", fmt.Sprintf("-Xscmx%dm", size))
}

func (scc SharedClassCache) Resize(size int) error {
fillRatio, err := scc.GetFillRatio()
if err != nil {
return err
}
newSize := int(float64(size)*fillRatio + 0.5)
scc.Logger.Bodyf("Resizing cache from %dm to %dm", size, newSize)
if err := scc.Delete(); err != nil {
return err
}
if err := scc.CreateLayer(newSize); err != nil {
return fmt.Errorf("unable to create resized layer\n%w", err)
}
return nil
}

func (scc SharedClassCache) Delete() error {
err := scc.execSccCommand("destroy")
var exitError *exec.ExitError
if errors.As(err, &exitError) && exitError.ExitCode() == 1 {
return nil
}
if err != nil {
return fmt.Errorf("unable to destroy cache\n%w", err)
}
return nil
}

func (scc SharedClassCache) execSccCommand(command string, options ...string) error {
sccOption := fmt.Sprintf("-Xshareclasses:name=%s,cacheDir=%s,%s", scc.Name, scc.Path, command)
writer := io.Discard
if scc.Logger.IsDebugEnabled() {
sccOption = fmt.Sprintf("%s,verbose", sccOption)
writer = scc.Logger.InfoWriter()
}
args := []string{sccOption}
if options != nil {
args = append(args, options...)
}
return scc.Executor.Execute(effect.Execution{
Command: "java",
Args: append(args, "-version"),
Stdout: writer,
Stderr: writer,
})
}
35 changes: 34 additions & 1 deletion liberty/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/paketo-buildpacks/liberty/internal/util"
"github.com/paketo-buildpacks/libpak/bindings"
"github.com/paketo-buildpacks/libpak/sherpa"
"strconv"
"strings"

"github.com/buildpacks/libcnb"
Expand Down Expand Up @@ -215,6 +216,10 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
result.Layers = append(result.Layers, base)

if installType == openLibertyInstall || installType == websphereLibertyInstall {
sccOptions, err := getSharedClassOptions(cr, jvmName)
if err != nil {
return libcnb.BuildResult{}, fmt.Errorf("unable to get SCC options\n%w", err)
}
if err := b.buildDistributionRuntime(
profile,
version,
Expand All @@ -224,6 +229,7 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
disableFeatureInstall,
featureList,
detectedBuildSrc,
sccOptions,
dr,
dc,
&result); err != nil {
Expand All @@ -240,6 +246,32 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
return result, nil
}

func getSharedClassOptions(cr libpak.ConfigurationResolver, jvmName string) (util.SharedClassCacheOptions, error) {
if jvmName != "OpenJ9" {
return util.SharedClassCacheOptions{
Enabled: false,
}, nil
}

resolvedSize, _ := cr.Resolve("BP_LIBERTY_SCC_SIZE_MB")
size, err := strconv.Atoi(resolvedSize)
if err != nil {
return util.SharedClassCacheOptions{}, fmt.Errorf("unable to parse BP_LIBERTY_SCC_SIZE_MB\n%w", err)
}
resolvedNumIterations, _ := cr.Resolve("BP_LIBERTY_SCC_NUM_ITERATIONS")
numIterations, err := strconv.Atoi(resolvedNumIterations)
if err != nil {
return util.SharedClassCacheOptions{}, fmt.Errorf("unable to parse BP_LIBERTY_SCC_NUM_ITERATIONS\n%w", err)
}

return util.SharedClassCacheOptions{
Enabled: !cr.ResolveBool("BP_LIBERTY_SCC_DISABLED"),
SizeMB: size,
NumIterations: numIterations,
Trim: !cr.ResolveBool("BP_LIBERTY_SCC_TRIM_SIZE_DISABLED"),
}, nil
}

func (b Build) buildDistributionRuntime(
profile string,
version string,
Expand All @@ -249,6 +281,7 @@ func (b Build) buildDistributionRuntime(
disableFeatureInstall bool,
features []string,
buildSrc core.BuildSource,
sccOptions util.SharedClassCacheOptions,
dependencyResolver libpak.DependencyResolver,
cache libpak.DependencyCache,
result *libcnb.BuildResult) error {
Expand All @@ -272,7 +305,7 @@ func (b Build) buildDistributionRuntime(
return fmt.Errorf("unable to load iFixes\n%w", err)
}

distro := NewDistribution(dep, cache, installType, serverName, appPath, disableFeatureInstall, features, iFixes, b.Executor)
distro := NewDistribution(dep, cache, installType, serverName, appPath, disableFeatureInstall, features, iFixes, sccOptions, b.Executor)
distro.Logger = b.Logger

result.Layers = append(result.Layers, distro)
Expand Down
4 changes: 4 additions & 0 deletions liberty/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
{"name": "BP_LIBERTY_PROFILE", "default": "", "build": true},
{"name": "BP_LIBERTY_INSTALL_TYPE", "default": "ol", "build": true},
{"name": "BP_LIBERTY_SERVER_NAME", "default": "", "build": true},
{"name": "BP_LIBERTY_SCC_DISABLED", "default": "false", "build": true},
{"name": "BP_LIBERTY_SCC_SIZE_MB", "default": "100", "build": true},
{"name": "BP_LIBERTY_SCC_NUM_ITERATIONS", "default": "1", "build": true},
{"name": "BP_LIBERTY_SCC_TRIM_SIZE_DISABLED", "default": "false", "build": true},
},
"dependencies": []map[string]interface{}{
{"id": "open-liberty-runtime-kernel", "version": "21.0.11"},
Expand Down
Loading

0 comments on commit 6e6246f

Please sign in to comment.