Skip to content

Commit 957d379

Browse files
committed
scheduler: fixed a bug where the bandwidth of reserved cores were not taken into account
1 parent a55a0fd commit 957d379

File tree

7 files changed

+74
-41
lines changed

7 files changed

+74
-41
lines changed

.changelog/26768.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
scheduler: fixed a bug where the bandwidth of reserved cores were not taken into account
3+
```

client/client.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3323,8 +3323,8 @@ func (c *Client) setGaugeForDiskStats(hStats *hoststats.HostStats, baseLabels []
33233323
// setGaugeForAllocationStats proxies metrics for allocation specific statistics
33243324
func (c *Client) setGaugeForAllocationStats(baseLabels []metrics.Label) {
33253325
node := c.GetConfig().Node
3326-
total := node.NodeResources
3327-
res := node.ReservedResources
3326+
3327+
available := node.Comparable()
33283328
allocated := c.getAllocatedResources(node)
33293329

33303330
// Emit allocated
@@ -3342,20 +3342,15 @@ func (c *Client) setGaugeForAllocationStats(baseLabels []metrics.Label) {
33423342
}
33433343

33443344
// Emit unallocated
3345-
unallocatedMem := total.Memory.MemoryMB - res.Memory.MemoryMB - allocated.Flattened.Memory.MemoryMB
3346-
unallocatedDisk := total.Disk.DiskMB - res.Disk.DiskMB - allocated.Shared.DiskMB
3347-
3348-
// The UsableCompute function call already subtracts and accounts for any
3349-
// reserved CPU within the client configuration. Therefore, we do not need
3350-
// to subtract that here.
3351-
unallocatedCpu := int64(total.Processors.Topology.UsableCompute()) - allocated.Flattened.Cpu.CpuShares
3345+
unallocatedMem := available.Flattened.Memory.MemoryMB - allocated.Flattened.Memory.MemoryMB
3346+
unallocatedDisk := available.Shared.DiskMB - allocated.Shared.DiskMB
3347+
unallocatedCpu := available.Flattened.Cpu.CpuShares - allocated.Flattened.Cpu.CpuShares
33523348

33533349
metrics.SetGaugeWithLabels([]string{"client", "unallocated", "memory"}, float32(unallocatedMem), baseLabels)
33543350
metrics.SetGaugeWithLabels([]string{"client", "unallocated", "disk"}, float32(unallocatedDisk), baseLabels)
33553351
metrics.SetGaugeWithLabels([]string{"client", "unallocated", "cpu"}, float32(unallocatedCpu), baseLabels)
33563352

3357-
totalComparable := total.Comparable()
3358-
for _, n := range totalComparable.Flattened.Networks {
3353+
for _, n := range available.Flattened.Networks {
33593354
// Determined the used resources
33603355
var usedMbits int
33613356
totalIdx := allocated.Flattened.Networks.NetIndex(n)

client/lib/numalib/topology.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,17 @@ func (st *Topology) NumECores() int {
282282
return total
283283
}
284284

285-
// UsableCores returns the number of logical cores usable by the Nomad client
285+
// AllCores returns the set of logical cores detected. The UsableCores will
286+
// be equal to or less than this value.
287+
func (st *Topology) AllCores() *idset.Set[hw.CoreID] {
288+
result := idset.Empty[hw.CoreID]()
289+
for _, cpu := range st.Cores {
290+
result.Insert(cpu.ID)
291+
}
292+
return result
293+
}
294+
295+
// UsableCores returns the set of logical cores usable by the Nomad client
286296
// for running tasks. Nomad must subtract off any reserved cores (reserved.cores)
287297
// and/or must mask the cpuset to the one set in config (config.reservable_cores).
288298
func (st *Topology) UsableCores() *idset.Set[hw.CoreID] {

nomad/structs/funcs.go

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,8 @@ func AllocsFit(node *Node, allocs []*Allocation, netIdx *NetworkIndex, checkDevi
189189
return false, "cores", used, nil
190190
}
191191

192-
// Check that the node resources (after subtracting reserved) are a
193-
// super set of those that are being allocated
194-
available := node.NodeResources.Comparable()
195-
available.Subtract(node.ReservedResources.Comparable())
192+
// Check that the node resources are a super set of those that are being allocated
193+
available := node.Comparable()
196194
if superset, dimension := available.Superset(used); !superset {
197195
return false, dimension, used, nil
198196
}
@@ -232,20 +230,12 @@ func AllocsFit(node *Node, allocs []*Allocation, netIdx *NetworkIndex, checkDevi
232230
}
233231

234232
func computeFreePercentage(node *Node, util *ComparableResources) (freePctCpu, freePctRam float64) {
235-
reserved := node.ReservedResources.Comparable()
236-
res := node.NodeResources.Comparable()
237-
238233
// Determine the node availability
239-
nodeCpu := float64(res.Flattened.Cpu.CpuShares)
240-
nodeMem := float64(res.Flattened.Memory.MemoryMB)
241-
if reserved != nil {
242-
nodeCpu -= float64(reserved.Flattened.Cpu.CpuShares)
243-
nodeMem -= float64(reserved.Flattened.Memory.MemoryMB)
244-
}
234+
available := node.Comparable()
245235

246236
// Compute the free percentage
247-
freePctCpu = 1 - (float64(util.Flattened.Cpu.CpuShares) / nodeCpu)
248-
freePctRam = 1 - (float64(util.Flattened.Memory.MemoryMB) / nodeMem)
237+
freePctCpu = 1 - (float64(util.Flattened.Cpu.CpuShares) / float64(available.Flattened.Cpu.CpuShares))
238+
freePctRam = 1 - (float64(util.Flattened.Memory.MemoryMB) / float64(available.Flattened.Memory.MemoryMB))
249239
return freePctCpu, freePctRam
250240
}
251241

nomad/structs/funcs_test.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,16 @@ func node2k() *Node {
106106
ID: 1,
107107
Grade: numalib.Performance,
108108
BaseSpeed: 1000,
109+
}, {
110+
ID: 2,
111+
Grade: numalib.Performance,
112+
BaseSpeed: 1000,
113+
Disable: true,
114+
}, {
115+
ID: 3,
116+
Grade: numalib.Performance,
117+
BaseSpeed: 1000,
118+
Disable: true,
109119
}},
110120
OverrideWitholdCompute: 1000, // set by client reserved field
111121
},
@@ -137,7 +147,8 @@ func node2k() *Node {
137147
},
138148
ReservedResources: &NodeReservedResources{
139149
Cpu: NodeReservedCpuResources{
140-
CpuShares: 1000,
150+
CpuShares: 1000,
151+
ReservedCpuCores: []uint16{2, 3},
141152
},
142153
Memory: NodeReservedMemoryResources{
143154
MemoryMB: 1024,
@@ -201,9 +212,10 @@ func TestAllocsFit(t *testing.T) {
201212
must.Eq(t, 1024, used.Flattened.Memory.MemoryMB)
202213

203214
// Should not fit second allocation
204-
fit, _, used, err = AllocsFit(n, []*Allocation{a1, a1}, nil, false)
215+
fit, dim, used, err = AllocsFit(n, []*Allocation{a1, a1}, nil, false)
205216
must.NoError(t, err)
206217
must.False(t, fit)
218+
must.Eq(t, "cpu", dim)
207219
must.Eq(t, 2000, used.Flattened.Cpu.CpuShares)
208220
must.Eq(t, 2048, used.Flattened.Memory.MemoryMB)
209221

@@ -649,8 +661,23 @@ func TestScoreFitBinPack(t *testing.T) {
649661
Cores: []numalib.Core{{
650662
ID: 0,
651663
Grade: numalib.Performance,
652-
BaseSpeed: 4096,
664+
BaseSpeed: 2048,
665+
}, {
666+
ID: 1,
667+
Grade: numalib.Performance,
668+
BaseSpeed: 2048,
669+
}, {
670+
ID: 2,
671+
Grade: numalib.Performance,
672+
BaseSpeed: 2048,
673+
Disable: true,
674+
}, {
675+
ID: 3,
676+
Grade: numalib.Performance,
677+
BaseSpeed: 2048,
678+
Disable: true,
653679
}},
680+
OverrideWitholdCompute: 2048, // set by client reserved field
654681
},
655682
},
656683
Memory: NodeMemoryResources{
@@ -661,7 +688,8 @@ func TestScoreFitBinPack(t *testing.T) {
661688
node.NodeResources.Compatibility()
662689
node.ReservedResources = &NodeReservedResources{
663690
Cpu: NodeReservedCpuResources{
664-
CpuShares: 2048,
691+
CpuShares: 2048,
692+
ReservedCpuCores: []uint16{2, 3},
665693
},
666694
Memory: NodeReservedMemoryResources{
667695
MemoryMB: 4096,

nomad/structs/structs.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,19 @@ func (n *Node) Canonicalize() {
22472247
}
22482248
}
22492249

2250+
// Comparable returns a comparable version of the node's resources available for scheduling.
2251+
// This conversion can be lossy so care must be taken when using it.
2252+
func (n *Node) Comparable() *ComparableResources {
2253+
if n == nil {
2254+
return nil
2255+
}
2256+
2257+
resources := n.NodeResources.Comparable()
2258+
resources.Subtract(n.ReservedResources.Comparable())
2259+
2260+
return resources
2261+
}
2262+
22502263
func (n *Node) Copy() *Node {
22512264
if n == nil {
22522265
return nil
@@ -3244,16 +3257,16 @@ func (n *NodeResources) Comparable() *ComparableResources {
32443257
return nil
32453258
}
32463259

3247-
usableCores := n.Processors.Topology.UsableCores().Slice()
3248-
reservableCores := helper.ConvertSlice(usableCores, func(id hw.CoreID) uint16 {
3260+
allCores := n.Processors.Topology.AllCores().Slice()
3261+
cores := helper.ConvertSlice(allCores, func(id hw.CoreID) uint16 {
32493262
return uint16(id)
32503263
})
32513264

32523265
c := &ComparableResources{
32533266
Flattened: AllocatedTaskResources{
32543267
Cpu: AllocatedCpuResources{
32553268
CpuShares: int64(n.Processors.Topology.TotalCompute()),
3256-
ReservedCores: reservableCores,
3269+
ReservedCores: cores,
32573270
},
32583271
Memory: AllocatedMemoryResources{
32593272
MemoryMB: n.Memory.MemoryMB,

scheduler/feasible/preemption.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,7 @@ func (p *Preemptor) Copy() *Preemptor {
162162

163163
// SetNode sets the node
164164
func (p *Preemptor) SetNode(node *structs.Node) {
165-
nodeRemainingResources := node.NodeResources.Comparable()
166-
167-
// Subtract the reserved resources of the node
168-
if c := node.ReservedResources.Comparable(); c != nil {
169-
nodeRemainingResources.Subtract(c)
170-
}
171-
p.nodeRemainingResources = nodeRemainingResources
165+
p.nodeRemainingResources = node.Comparable()
172166
}
173167

174168
// SetCandidates initializes the candidate set from which preemptions are chosen

0 commit comments

Comments
 (0)