Skip to content

Commit 81dfad9

Browse files
authored
scheduler: add downgrade strategy for empty 'aggregated' on cold koor… (koordinator-sh#2239)
Signed-off-by: clay-wangzhi <[email protected]>
1 parent 6fc4205 commit 81dfad9

File tree

2 files changed

+188
-6
lines changed

2 files changed

+188
-6
lines changed

pkg/scheduler/plugins/loadaware/helper.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,26 @@ func getTargetAggregatedUsage(nodeMetric *slov1alpha1.NodeMetric, aggregatedDura
6060
return nil
6161
}
6262

63-
// If no specific period is set, the maximum period recorded by NodeMetrics will be used by default.
63+
// If no specific period is set, the non-empty maximum period recorded by NodeMetrics will be used by default.
6464
// This is a default policy.
6565
if aggregatedDuration == nil || aggregatedDuration.Duration == 0 {
6666
var maxDuration time.Duration
67-
var maxIndex int
67+
var maxIndex int = -1
6868
for i, v := range nodeMetric.Status.NodeMetric.AggregatedNodeUsages {
69-
if v.Duration.Duration > maxDuration {
69+
if len(v.Usage[aggregationType].ResourceList) > 0 && v.Duration.Duration > maxDuration {
7070
maxDuration = v.Duration.Duration
7171
maxIndex = i
7272
}
7373
}
74-
aggregatedNodeUsage := &nodeMetric.Status.NodeMetric.AggregatedNodeUsages[maxIndex]
75-
usage := aggregatedNodeUsage.Usage[aggregationType]
76-
if len(usage.ResourceList) > 0 {
74+
75+
if maxIndex == -1 {
76+
// All values in aggregatedDuration are empty, downgrade to use the values in NodeUsage
77+
usage := nodeMetric.Status.NodeMetric.NodeUsage
78+
if len(usage.ResourceList) > 0 {
79+
return &usage
80+
}
81+
} else {
82+
usage := nodeMetric.Status.NodeMetric.AggregatedNodeUsages[maxIndex].Usage[aggregationType]
7783
return &usage
7884
}
7985
} else if aggregatedDuration != nil {
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
Copyright 2022 The Koordinator Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package loadaware
18+
19+
import (
20+
"testing"
21+
"time"
22+
23+
"github.com/stretchr/testify/assert"
24+
corev1 "k8s.io/api/core/v1"
25+
"k8s.io/apimachinery/pkg/api/resource"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
28+
"github.com/koordinator-sh/koordinator/apis/extension"
29+
slov1alpha1 "github.com/koordinator-sh/koordinator/apis/slo/v1alpha1"
30+
)
31+
32+
// Test cases description:
33+
// 1. When nodeMetric contains valid AggregatedNodeUsages and aggregatedDuration is nil, it should return the non-empty longest duration resource usage.
34+
// 2. When aggregatedDuration is not nil and matches a duration in AggregatedNodeUsages, it should return the corresponding resource usage.
35+
// 3. When nodeMetric's NodeUsage contains a valid resource list and AggregatedNodeUsages is empty, it should return the resource usage of NodeUsage.
36+
37+
func TestGetTargetAggregatedUsage(t *testing.T) {
38+
aggregationType := extension.P95
39+
tests := []struct {
40+
name string
41+
nodeMetric *slov1alpha1.NodeMetric
42+
aggregatedDuration *metav1.Duration
43+
44+
expectedResult *slov1alpha1.ResourceMap
45+
}{
46+
{
47+
name: "Valid AggregatedNodeUsages and aggregatedDuration is nil",
48+
nodeMetric: &slov1alpha1.NodeMetric{
49+
Status: slov1alpha1.NodeMetricStatus{
50+
NodeMetric: &slov1alpha1.NodeMetricInfo{
51+
AggregatedNodeUsages: []slov1alpha1.AggregatedUsage{
52+
{
53+
Duration: metav1.Duration{Duration: 5 * time.Minute},
54+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
55+
aggregationType: {
56+
ResourceList: corev1.ResourceList{
57+
corev1.ResourceCPU: resource.MustParse("30"),
58+
},
59+
},
60+
},
61+
},
62+
{
63+
Duration: metav1.Duration{Duration: 10 * time.Minute},
64+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
65+
aggregationType: {
66+
ResourceList: corev1.ResourceList{
67+
corev1.ResourceCPU: resource.MustParse("50"),
68+
},
69+
},
70+
},
71+
},
72+
{
73+
Duration: metav1.Duration{Duration: 15 * time.Minute},
74+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
75+
aggregationType: {
76+
ResourceList: nil,
77+
},
78+
},
79+
},
80+
},
81+
},
82+
},
83+
},
84+
aggregatedDuration: nil,
85+
expectedResult: &slov1alpha1.ResourceMap{
86+
ResourceList: corev1.ResourceList{
87+
corev1.ResourceCPU: resource.MustParse("50"),
88+
},
89+
},
90+
},
91+
{
92+
name: "aggregatedDuration is not nil and matches a duration",
93+
nodeMetric: &slov1alpha1.NodeMetric{
94+
Status: slov1alpha1.NodeMetricStatus{
95+
NodeMetric: &slov1alpha1.NodeMetricInfo{
96+
AggregatedNodeUsages: []slov1alpha1.AggregatedUsage{
97+
{
98+
Duration: metav1.Duration{Duration: 5 * time.Minute},
99+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
100+
aggregationType: {
101+
ResourceList: corev1.ResourceList{
102+
corev1.ResourceCPU: resource.MustParse("30"),
103+
},
104+
},
105+
},
106+
},
107+
{
108+
Duration: metav1.Duration{Duration: 10 * time.Minute},
109+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
110+
aggregationType: {
111+
ResourceList: corev1.ResourceList{
112+
corev1.ResourceCPU: resource.MustParse("50"),
113+
},
114+
},
115+
},
116+
},
117+
{
118+
Duration: metav1.Duration{Duration: 15 * time.Minute},
119+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
120+
aggregationType: {
121+
ResourceList: corev1.ResourceList{
122+
corev1.ResourceCPU: resource.MustParse("70"),
123+
},
124+
},
125+
},
126+
},
127+
},
128+
},
129+
},
130+
},
131+
aggregatedDuration: &metav1.Duration{Duration: 5 * time.Minute},
132+
expectedResult: &slov1alpha1.ResourceMap{
133+
ResourceList: corev1.ResourceList{
134+
corev1.ResourceCPU: resource.MustParse("30"),
135+
},
136+
},
137+
},
138+
{
139+
name: "NodeUsage contains a valid resource list and AggregatedNodeUsages is empty",
140+
nodeMetric: &slov1alpha1.NodeMetric{
141+
Status: slov1alpha1.NodeMetricStatus{
142+
NodeMetric: &slov1alpha1.NodeMetricInfo{
143+
NodeUsage: slov1alpha1.ResourceMap{
144+
ResourceList: corev1.ResourceList{
145+
corev1.ResourceCPU: resource.MustParse("30"),
146+
},
147+
},
148+
AggregatedNodeUsages: []slov1alpha1.AggregatedUsage{
149+
{
150+
Duration: metav1.Duration{Duration: 5 * time.Minute},
151+
Usage: map[extension.AggregationType]slov1alpha1.ResourceMap{
152+
aggregationType: {
153+
ResourceList: nil,
154+
},
155+
},
156+
},
157+
},
158+
},
159+
},
160+
},
161+
aggregatedDuration: nil,
162+
expectedResult: &slov1alpha1.ResourceMap{
163+
ResourceList: corev1.ResourceList{
164+
corev1.ResourceCPU: resource.MustParse("30"),
165+
},
166+
},
167+
},
168+
}
169+
170+
for _, tt := range tests {
171+
t.Run(tt.name, func(t *testing.T) {
172+
result := getTargetAggregatedUsage(tt.nodeMetric, tt.aggregatedDuration, aggregationType)
173+
assert.Equal(t, tt.expectedResult, result)
174+
})
175+
}
176+
}

0 commit comments

Comments
 (0)