Skip to content

Commit 35c3256

Browse files
author
Steve Powell
committed
Merge 94164248-error-on-zero to master
[Completes #94164248]
2 parents 934ea74 + eec6697 commit 35c3256

File tree

3 files changed

+123
-40
lines changed

3 files changed

+123
-40
lines changed

integration/main_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ var _ = Describe("java-buildpack-memory-calculator executable", func() {
159159

160160
It("fails with an error", func() {
161161
Ω(cmdErr).Should(HaveOccurred(), "exit status")
162-
Ω(string(sErr)).Should(ContainSubstring("Cannot balance memory: Total memory exceeded by configuration: ["), "stderr")
162+
Ω(string(sErr)).Should(ContainSubstring("Cannot balance memory: Memory allocation failed for configuration: ["), "stderr")
163163
Ω(string(sOut)).Should(Equal(""), "stdout")
164164
})
165165
})

memory/allocator.go

+36-28
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ type allocator struct {
3434
originalSizes map[string]Range // unmodified after creation
3535
buckets map[string]Bucket // named buckets for allocation
3636
warnings []string // warnings if allocation found issues
37-
failure error // error if allocation failed
3837
}
3938

4039
func NewAllocator(sizes map[string]Range, heuristics map[string]float64) (*allocator, error) {
@@ -57,21 +56,20 @@ const (
5756
// Balance memory between buckets, adjusting stack units, observing
5857
// constraints, and detecting memory wastage and default proximity.
5958
func (a *allocator) Balance(memLimit MemSize) error {
60-
// Adjust stack bucket, if it exists
59+
// adjust stack bucket, if it exists
6160
stackBucket, estNumThreads := a.normaliseStack(memLimit)
6261

63-
// balance buckets
64-
a.balance(memLimit)
62+
// distribute memory among the buckets
63+
if berr := a.balance(memLimit); berr != nil {
64+
return fmt.Errorf("Memory allocation failed for configuration: %v, : %s", getSizes(a.originalSizes), berr)
65+
}
6566

66-
// Validate result and gather warnings
67+
// validate result and gather warnings
6768
a.validateAllocation(memLimit)
6869

69-
// Re-adjust stack bucket, if it exists
70+
// reset stack bucket, if it exists
7071
a.unnormaliseStack(stackBucket, estNumThreads)
7172

72-
if a.failure != nil {
73-
return fmt.Errorf("Total memory exceeded by configuration: %v", getSizes(a.originalSizes))
74-
}
7573
return nil
7674
}
7775

@@ -82,9 +80,6 @@ func (a *allocator) SetLowerBounds() {
8280
}
8381

8482
func (a *allocator) Switches(sfs switches.Funs) []string {
85-
if a.failure != nil {
86-
return nil
87-
}
8883
var strs = make([]string, 0, 10)
8984
for s, b := range a.buckets {
9085
strs = append(strs, sfs.Apply(s, b.GetSize().String())...)
@@ -93,12 +88,10 @@ func (a *allocator) Switches(sfs switches.Funs) []string {
9388
}
9489

9590
func (a *allocator) GetWarnings() []string {
96-
if a.failure != nil {
97-
return nil
98-
}
9991
return a.warnings
10092
}
10193

94+
// getSizes returns a slice of memory type range strings
10295
func getSizes(ss map[string]Range) []string {
10396
result := []string{}
10497
for n, s := range ss {
@@ -130,6 +123,7 @@ func totalWeight(bs map[string]Bucket) float64 {
130123
return w
131124
}
132125

126+
// Replace stack bucket to make it represent total memory for stacks temporarily
133127
func (a *allocator) normaliseStack(memLimit MemSize) (originalStackBucket Bucket, estNumThreads float64) {
134128
if sb, ok := a.buckets["stack"]; ok {
135129
stackMem := weightedSize(totalWeight(a.buckets), memLimit, sb)
@@ -146,6 +140,7 @@ func weightedSize(totWeight float64, memLimit MemSize, b Bucket) float64 {
146140
return (float64(memLimit) * b.Weight()) / totWeight
147141
}
148142

143+
// Replace stack bucket, and set size per thread
149144
func (a *allocator) unnormaliseStack(sb Bucket, estNum float64) {
150145
if sb == nil {
151146
return
@@ -156,18 +151,26 @@ func (a *allocator) unnormaliseStack(sb Bucket, estNum float64) {
156151
}
157152

158153
// Balance memory between buckets, observing constraints.
159-
func (a *allocator) balance(memLimit MemSize) {
154+
func (a *allocator) balance(memLimit MemSize) error {
160155
remaining := copyBucketMap(a.buckets)
161156
removed := true
162157

163158
for removed && len(remaining) != 0 {
164159
var err error
165160
memLimit, removed, err = balanceOrRemove(remaining, memLimit)
166161
if err != nil {
167-
a.failure = err
168-
return
162+
return err
169163
}
170164
}
165+
166+
// check for zero allocations
167+
for n, b := range a.buckets {
168+
if b.GetSize().String() == "0" {
169+
return fmt.Errorf("Cannot allocate memory to '%n' type", n)
170+
}
171+
}
172+
173+
return nil
171174
}
172175

173176
func (a *allocator) validateAllocation(memLimit MemSize) {
@@ -179,16 +182,8 @@ func (a *allocator) validateAllocation(memLimit MemSize) {
179182

180183
func memoryWastageWarnings(bs map[string]Bucket, memLimit MemSize) []string {
181184
warnings := []string{}
182-
if nativeBucket, ok := bs["native"]; ok && nativeBucket.Range().Floor() == MEMSIZE_ZERO {
183-
totWeight := totalWeight(bs)
184-
floatSize := NATIVE_MEMORY_WARNING_FACTOR * weightedSize(totWeight, memLimit, nativeBucket)
185-
if MemSize(math.Floor(0.5 + floatSize)).LessThan(*nativeBucket.GetSize()) {
186-
warnings = append(warnings,
187-
fmt.Sprintf(
188-
"There is more than %g times more spare native memory than the default "+
189-
"so configured Java memory may be too small or available memory may be too large",
190-
NATIVE_MEMORY_WARNING_FACTOR))
191-
}
185+
if nb, ok := bs["native"]; ok {
186+
warnings = append(warnings, nativeBucketWarning(nb, totalWeight(bs), memLimit)...)
192187
}
193188

194189
totalSize := MEMSIZE_ZERO
@@ -205,6 +200,19 @@ func memoryWastageWarnings(bs map[string]Bucket, memLimit MemSize) []string {
205200
return warnings
206201
}
207202

203+
func nativeBucketWarning(nativeBucket Bucket, totWeight float64, memLimit MemSize) []string {
204+
if nativeBucket.Range().Floor() == MEMSIZE_ZERO {
205+
floatSize := NATIVE_MEMORY_WARNING_FACTOR * weightedSize(totWeight, memLimit, nativeBucket)
206+
if MemSize(math.Floor(0.5 + floatSize)).LessThan(*nativeBucket.GetSize()) {
207+
return []string{fmt.Sprintf(
208+
"There is more than %g times more spare native memory than the default "+
209+
"so configured Java memory may be too small or available memory may be too large",
210+
NATIVE_MEMORY_WARNING_FACTOR)}
211+
}
212+
}
213+
return []string{}
214+
}
215+
208216
func closeToDefaultWarnings(bs map[string]Bucket, sizes map[string]Range, memLimit MemSize) []string {
209217
totWeight := totalWeight(bs)
210218
warnings := []string{}

memory/allocator_test.go

+86-11
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,6 @@ var _ = Describe("Allocator", func() {
5656
}
5757
})
5858

59-
JustBeforeEach(func() {
60-
a = shouldWork(memory.NewAllocator(convertToRanges(sizes), weights))
61-
})
62-
6359
Context("constructor", func() {
6460

6561
Context("with good parameters", func() {
@@ -79,6 +75,10 @@ var _ = Describe("Allocator", func() {
7975
}
8076
})
8177

78+
JustBeforeEach(func() {
79+
a = shouldWork(memory.NewAllocator(convertToRanges(sizes), weights))
80+
})
81+
8282
It("succeeds", func() {
8383
Ω(memory.GetBuckets(a)).Should(ConsistOf(
8484
"Bucket{name: stack, size: <nil>, range: 2M..2M, weight: 1}",
@@ -108,14 +108,89 @@ var _ = Describe("Allocator", func() {
108108
)) // heap, permgen, stack
109109
})
110110
})
111+
})
112+
113+
Context("balancing", func() {
114+
var (
115+
memLimit = memory.MEMSIZE_ZERO
116+
aerr error
117+
)
118+
119+
JustBeforeEach(func() {
120+
a = shouldWork(memory.NewAllocator(convertToRanges(sizes), weights))
121+
aerr = a.Balance(memLimit)
122+
})
123+
124+
Context("badly", func() {
125+
126+
JustBeforeEach(func() {
127+
Ω(aerr).Should(HaveOccurred())
128+
})
129+
130+
Context("with no memory and one bucket", func() {
131+
BeforeEach(func() {
132+
sizes = strmap{"heap": "0.."}
133+
weights = floatmap{"heap": 5.0}
134+
memLimit = memory.MEMSIZE_ZERO
135+
})
136+
It("fails", func() {})
137+
})
138+
139+
Context("with not enough memory and one bucket", func() {
140+
BeforeEach(func() {
141+
sizes = strmap{"heap": "64m.."}
142+
weights = floatmap{"heap": 5.0}
143+
memLimit = memory.NewMemSize(32 * mEGA)
144+
})
145+
It("fails", func() {})
146+
})
147+
148+
Context("with not enough memory and two buckets", func() {
149+
BeforeEach(func() {
150+
sizes = strmap{"heap": "33m..", "hope": "32m.."}
151+
weights = floatmap{"heap": 1.0, "hope": 1.0}
152+
memLimit = memory.NewMemSize(64 * mEGA)
153+
})
154+
It("fails", func() {})
155+
})
111156

112-
Context("good balancing", func() {
113-
var (
114-
memLimit = memory.MemSize(0)
115-
)
157+
Context("with just enough memory for one out of two buckets", func() {
158+
BeforeEach(func() {
159+
sizes = strmap{"heap": "38m..", "hope": ".."}
160+
weights = floatmap{"heap": 1.0, "hope": 1.0}
161+
memLimit = memory.NewMemSize(38 * mEGA)
162+
})
163+
It("fails", func() {})
164+
})
165+
})
116166

167+
Context("well", func() {
117168
JustBeforeEach(func() {
118-
Ω(a.Balance(memLimit)).ShouldNot(HaveOccurred())
169+
Ω(aerr).ShouldNot(HaveOccurred())
170+
})
171+
172+
Context("with exactly enough memory and one bucket", func() {
173+
BeforeEach(func() {
174+
sizes = strmap{"heap": "64m.."}
175+
weights = floatmap{"heap": 5.0}
176+
memLimit = memory.NewMemSize(64 * mEGA)
177+
})
178+
It("fills the bucket up", func() {
179+
Ω(memory.GetBuckets(a)).Should(ConsistOf(
180+
"Bucket{name: heap, size: 64M, range: 64M.., weight: 5}",
181+
))
182+
})
183+
})
184+
185+
Context("with no memory and no buckets", func() {
186+
BeforeEach(func() {
187+
sizes = strmap{"heap": "0.."}
188+
weights = floatmap{}
189+
memLimit = memory.MEMSIZE_ZERO
190+
})
191+
It("results in no buckets", func() {
192+
Ω(memory.GetBuckets(a)).Should(BeEmpty())
193+
})
119194
})
120195

121196
Context("with single bucket to 'balance'", func() {
@@ -133,7 +208,7 @@ var _ = Describe("Allocator", func() {
133208
})
134209
})
135210

136-
Context("with no buckets to 'balance'", func() {
211+
Context("with some memory and no buckets to 'balance'", func() {
137212

138213
BeforeEach(func() {
139214
sizes = strmap{}
@@ -146,7 +221,7 @@ var _ = Describe("Allocator", func() {
146221
})
147222
})
148223

149-
Context("with two buckets to balance", func() {
224+
Context("with some memory and two buckets to balance", func() {
150225

151226
BeforeEach(func() {
152227
sizes = strmap{"heap": "0..", "hope": "0.."}

0 commit comments

Comments
 (0)