Skip to content

Commit c8b6d5e

Browse files
author
mani
committed
[YUNIKORN-3142] Calculate Preemptable Resource for leaf Queue with same resource types
1 parent 6ab2b7f commit c8b6d5e

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

pkg/scheduler/objects/queue.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,12 @@ func (sq *Queue) GetMaxResource() *resources.Resource {
12971297
return sq.internalGetMax(limit)
12981298
}
12991299

1300+
func (sq *Queue) CloneMaxResource() *resources.Resource {
1301+
sq.RLock()
1302+
defer sq.RUnlock()
1303+
return sq.maxResource.Clone()
1304+
}
1305+
13001306
// GetFairMaxResource computes the fair max resources for a given queue.
13011307
// Starting with the root, descend down to the target queue allowing children to override Resource values .
13021308
// If the root includes an explicit 0 value for a Resource, do not include it in the accumulator and treat it as missing.

pkg/scheduler/objects/quota_change_preemptor.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818

1919
package objects
2020

21+
import (
22+
"math"
23+
24+
"github.com/apache/yunikorn-core/pkg/common/resources"
25+
)
26+
2127
type QuotaChangePreemptionContext struct {
2228
queue *Queue
2329
}
@@ -48,3 +54,26 @@ func (qcp *QuotaChangePreemptionContext) tryPreemption() {
4854
// quota change preemption has really evicted victims, so mark the flag
4955
qcp.queue.MarkTriggerredQuotaChangePreemption()
5056
}
57+
58+
// GetPreemptableResources Get the preemptable resources for the queue
59+
// Subtracting the usage from the max resource gives the preemptable resources.
60+
// It could contain both positive and negative values. Only negative values are preemptable.
61+
func (qcp *QuotaChangePreemptionContext) GetPreemptableResources() *resources.Resource {
62+
maxRes := qcp.queue.CloneMaxResource()
63+
used := resources.SubOnlyExisting(qcp.queue.GetAllocatedResource(), qcp.queue.GetPreemptingResource())
64+
if maxRes.IsEmpty() || used.IsEmpty() {
65+
return nil
66+
}
67+
actual := resources.SubOnlyExisting(maxRes, used)
68+
preemptableResource := resources.NewResource()
69+
// Keep only the resource type which needs to be preempted
70+
for k, v := range actual.Resources {
71+
if v < 0 {
72+
preemptableResource.Resources[k] = resources.Quantity(math.Abs(float64(v)))
73+
}
74+
}
75+
if preemptableResource.IsEmpty() {
76+
return nil
77+
}
78+
return preemptableResource
79+
}

pkg/scheduler/objects/quota_change_preemptor_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,35 @@ func TestQuotaChangeCheckPreconditions(t *testing.T) {
9999
})
100100
}
101101
}
102+
103+
func TestQuotaChangeGetPreemptableResource(t *testing.T) {
104+
leaf, err := NewConfiguredQueue(configs.QueueConfig{
105+
Name: "leaf",
106+
}, nil, false)
107+
assert.NilError(t, err)
108+
109+
testCases := []struct {
110+
name string
111+
queue *Queue
112+
maxResource *resources.Resource
113+
usedResource *resources.Resource
114+
preemptable *resources.Resource
115+
}{
116+
{"nil max and nil used", leaf, nil, nil, nil},
117+
{"nil max", leaf, nil, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), nil},
118+
{"nil used", leaf, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), nil, nil},
119+
{"used below max", leaf, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500}), nil},
120+
{"used above max", leaf, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
121+
{"used above max in specific res type", leaf, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000, "cpu": 10}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
122+
{"used above max and below max in specific res type", leaf, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000, "cpu": 10}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1500, "cpu": 10}), resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 500})},
123+
{"used res type but max undefined", leaf, resources.NewResourceFromMap(map[string]resources.Quantity{"memory": 1000}), resources.NewResourceFromMap(map[string]resources.Quantity{"cpu": 150}), nil},
124+
}
125+
for _, tc := range testCases {
126+
t.Run(tc.name, func(t *testing.T) {
127+
tc.queue.maxResource = tc.maxResource
128+
tc.queue.allocatedResource = tc.usedResource
129+
preemptor := NewQuotaChangePreemptor(tc.queue)
130+
assert.Equal(t, resources.Equals(preemptor.GetPreemptableResources(), tc.preemptable), true)
131+
})
132+
}
133+
}

0 commit comments

Comments
 (0)