aboutsummaryrefslogtreecommitdiffstats
path: root/client/go/internal/admin/jvm/application_container.go
blob: c525ea77949631fddf64e785af8cc95781bc02e8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// Author: arnej

package jvm

import (
	"fmt"
	"os"

	"github.com/vespa-engine/vespa/client/go/internal/admin/defaults"
	"github.com/vespa-engine/vespa/client/go/internal/admin/envvars"
	"github.com/vespa-engine/vespa/client/go/internal/admin/prog"
	"github.com/vespa-engine/vespa/client/go/internal/admin/trace"
	"github.com/vespa-engine/vespa/client/go/internal/util"
)

const (
	JAR_FOR_APPLICATION_CONTAINER = "container-disc-jar-with-dependencies.jar"
)

type ApplicationContainer struct {
	containerBase
}

func (a *ApplicationContainer) ArgForMain() string {
	dir := defaults.UnderVespaHome("lib/jars")
	return fmt.Sprintf("file:%s/%s", dir, JAR_FOR_APPLICATION_CONTAINER)
}

func (a *ApplicationContainer) Discriminator() string {
	cfgId := a.ConfigId()
	if cfgId != "" {
		trace.Trace("Discriminator: using md5 of", cfgId)
		return util.Md5Hex(cfgId + "\n")
	}
	svcName := a.ServiceName()
	if svcName != "" {
		trace.Trace("Discriminator: using", svcName)
		return svcName
	}
	pid := os.Getpid()
	trace.Trace("Discriminator: using md5 of", pid)
	return util.Md5Hex(fmt.Sprintf("%d", pid))
}

func (a *ApplicationContainer) addJdiscProperties() {
	cfgId := a.ConfigId()
	opts := a.jvmOpts
	opts.AddCommonJdiscProperties()
	containerParentDir := defaults.UnderVespaHome("var/jdisc_container")
	containerHomeDir := fmt.Sprintf("%s/%s", containerParentDir, a.Discriminator())
	bCacheDir := fmt.Sprintf("%s/%s", containerHomeDir, "bundlecache")
	propsFile := fmt.Sprintf("%s/%s.properties", containerHomeDir, "jdisc")
	opts.fixSpec.FixDir(containerHomeDir)
	opts.fixSpec.FixDir(bCacheDir)
	a.propsFile = propsFile
	opts.AddOption("-Djdisc.config.file=" + propsFile)
	opts.AddOption("-Djdisc.cache.path=" + bCacheDir)
	opts.AddOption("-Djdisc.logger.tag=" + cfgId)
}

func validPercentage(val int) bool {
	return val > 0 && val < 100
}

func (a *ApplicationContainer) configureMemory(qc *QrStartConfig) {
	jvm_heapsize := qc.Jvm.Heapsize                                                         // Heap size (in megabytes) for the Java VM
	jvm_minHeapsize := qc.Jvm.MinHeapsize                                                   // Min heapsize (in megabytes) for the Java VM
	jvm_stacksize := qc.Jvm.Stacksize                                                       // Stack size (in kilobytes)
	jvm_compressedClassSpaceSize := qc.Jvm.CompressedClassSpaceSize                         // CompressedOOps size in megabytes
	jvm_baseMaxDirectMemorySize := qc.Jvm.BaseMaxDirectMemorySize                           // Base value of maximum direct memory size (in megabytes)
	jvm_directMemorySizeCache := qc.Jvm.DirectMemorySizeCache                               // Amount of direct memory used for caching. (in megabytes)
	jvm_heapSizeAsPercentageOfPhysicalMemory := qc.Jvm.HeapSizeAsPercentageOfPhysicalMemory // Heap size as percentage of available RAM, overrides value above.

	if jvm_heapsize <= 0 {
		jvm_heapsize = 1536
		trace.Trace("using hardcoded value for jvm_heapsize:", jvm_heapsize)
	}
	if jvm_minHeapsize <= 0 {
		jvm_minHeapsize = jvm_heapsize
	}
	available := getAvailableMemory()
	if validPercentage(jvm_heapSizeAsPercentageOfPhysicalMemory) && available.ToMB() > 500 {
		available = adjustAvailableMemory(available)
		jvm_heapsize = available.ToMB() * jvm_heapSizeAsPercentageOfPhysicalMemory / 100
		jvm_minHeapsize = jvm_heapsize
	}
	if jvm_minHeapsize > jvm_heapsize {
		trace.Warning(fmt.Sprintf(
			"Misconfigured heap size, jvm_minHeapsize(%d) is larger than jvm_heapsize(%d). It has been capped.",
			jvm_minHeapsize, jvm_heapsize))
		jvm_minHeapsize = jvm_heapsize
	}
	if jvm_stacksize <= 0 {
		jvm_stacksize = 512
	}
	if jvm_baseMaxDirectMemorySize <= 0 {
		jvm_baseMaxDirectMemorySize = 75
	}
	if jvm_compressedClassSpaceSize < 0 {
		jvm_compressedClassSpaceSize = 32
	}
	if jvm_directMemorySizeCache < 0 {
		jvm_directMemorySizeCache = 0
	}
	maxDirectMemorySize := jvm_baseMaxDirectMemorySize + (jvm_heapsize / 8) + jvm_directMemorySizeCache
	opts := a.jvmOpts
	opts.AddOption(fmt.Sprintf("-Xms%dm", jvm_minHeapsize))
	opts.AddOption(fmt.Sprintf("-Xmx%dm", jvm_heapsize))
	opts.AddOption(fmt.Sprintf("-XX:ThreadStackSize=%d", jvm_stacksize))
	opts.AddOption(fmt.Sprintf("-XX:MaxDirectMemorySize=%dm", maxDirectMemorySize))
	opts.MaybeAddHugepages(MegaBytesOfMemory(jvm_heapsize))
	if jvm_compressedClassSpaceSize > 0 {
		opts.AddOption(fmt.Sprintf("-XX:CompressedClassSpaceSize=%dm", jvm_compressedClassSpaceSize))
	}
}

func (a *ApplicationContainer) configureGC(qc *QrStartConfig) {
	if extra := qc.Jvm.Gcopts; extra != "" {
		a.JvmOptions().AddJvmArgsFromString(extra)
	}
	if qc.Jvm.Verbosegc {
		a.JvmOptions().AddOption("-Xlog:gc")
	}
}

func (a *ApplicationContainer) configureClasspath(qc *QrStartConfig) {
	opts := a.JvmOptions()
	if cp := qc.Jdisc.ClasspathExtra; cp != "" {
		opts.classPath = append(opts.classPath, cp)
	}
}

func (a *ApplicationContainer) configureCPU(qc *QrStartConfig) {
	cnt := qc.Jvm.AvailableProcessors
	if cnt > 0 {
		trace.Trace("CpuCount: using", cnt, "from qr-start config")
	}
	a.JvmOptions().ConfigureCpuCount(cnt)
}

func (a *ApplicationContainer) configureOptions() {
	opts := a.JvmOptions()
	opts.AddOption("-Dconfig.id=" + a.ConfigId())
	if env := os.Getenv(envvars.VESPA_CONTAINER_JVMARGS); env != "" {
		opts.AddJvmArgsFromString(env)
	}
	qrStartCfg := a.getQrStartCfg()
	opts.AddOption("-Djdisc.export.packages=" + qrStartCfg.Jdisc.ExportPackages)
	opts.AddCommonXX()
	opts.AddCommonOpens()
	opts.AddCommonJdkProperties()
	a.configureCPU(qrStartCfg)
	a.configureMemory(qrStartCfg)
	a.configureGC(qrStartCfg)
	a.configureClasspath(qrStartCfg)
	a.addJdiscProperties()
	svcName := a.ServiceName()
	if svcName == "container" || svcName == "container-clustercontroller" {
		RemoveStaleZkLocks(a)
		logsDir := defaults.UnderVespaHome("logs/vespa")
		zkLogFile := fmt.Sprintf("%s/zookeeper.%s", logsDir, svcName)
		opts.AddOption("-Dzookeeper_log_file_prefix=" + zkLogFile)
	}
}

func (c *ApplicationContainer) exportExtraEnv(ps *prog.Spec) {
	if c.ConfigId() != "" {
		ps.Setenv(envvars.VESPA_CONFIG_ID, c.ConfigId())
	} else {
		util.JustExitMsg("application container requires a config id")
	}
}