@@ -39,6 +39,10 @@ type preScoreState struct {
39
39
IgnoredNodes sets.String
40
40
// TopologyPairToPodCounts is keyed with topologyPair, and valued with the number of matching pods.
41
41
TopologyPairToPodCounts map [topologyPair ]* int64
42
+ // TopologyNormalizingWeight is the weight we give to the counts per topology.
43
+ // This allows the pod counts of smaller topologies to not be watered down by
44
+ // bigger ones.
45
+ TopologyNormalizingWeight []float64
42
46
}
43
47
44
48
// Clone implements the mandatory Clone interface. We don't really copy the data since
@@ -51,6 +55,7 @@ func (s *preScoreState) Clone() framework.StateData {
51
55
// don't have required topologyKey(s), and initialize:
52
56
// 1) s.TopologyPairToPodCounts: keyed with both eligible topology pair and node names.
53
57
// 2) s.IgnoredNodes: the set of nodes that shouldn't be scored.
58
+ // 3) s.TopologyNormalizingWeight: The weight to be given to each constraint based on the number of values in a topology.
54
59
func (pl * PodTopologySpread ) initPreScoreState (s * preScoreState , pod * v1.Pod , filteredNodes []* v1.Node ) error {
55
60
var err error
56
61
if len (pod .Spec .TopologySpreadConstraints ) > 0 {
@@ -67,24 +72,35 @@ func (pl *PodTopologySpread) initPreScoreState(s *preScoreState, pod *v1.Pod, fi
67
72
if len (s .Constraints ) == 0 {
68
73
return nil
69
74
}
75
+ topoSize := make ([]int , len (s .Constraints ))
70
76
for _ , node := range filteredNodes {
71
77
if ! nodeLabelsMatchSpreadConstraints (node .Labels , s .Constraints ) {
72
78
// Nodes which don't have all required topologyKeys present are ignored
73
79
// when scoring later.
74
80
s .IgnoredNodes .Insert (node .Name )
75
81
continue
76
82
}
77
- for _ , constraint := range s .Constraints {
83
+ for i , constraint := range s .Constraints {
78
84
// per-node counts are calculated during Score.
79
85
if constraint .TopologyKey == v1 .LabelHostname {
80
86
continue
81
87
}
82
88
pair := topologyPair {key : constraint .TopologyKey , value : node .Labels [constraint .TopologyKey ]}
83
89
if s .TopologyPairToPodCounts [pair ] == nil {
84
90
s .TopologyPairToPodCounts [pair ] = new (int64 )
91
+ topoSize [i ]++
85
92
}
86
93
}
87
94
}
95
+
96
+ s .TopologyNormalizingWeight = make ([]float64 , len (s .Constraints ))
97
+ for i , c := range s .Constraints {
98
+ sz := topoSize [i ]
99
+ if c .TopologyKey == v1 .LabelHostname {
100
+ sz = len (filteredNodes ) - len (s .IgnoredNodes )
101
+ }
102
+ s .TopologyNormalizingWeight [i ] = topologyNormalizingWeight (sz )
103
+ }
88
104
return nil
89
105
}
90
106
@@ -174,20 +190,20 @@ func (pl *PodTopologySpread) Score(ctx context.Context, cycleState *framework.Cy
174
190
175
191
// For each present <pair>, current node gets a credit of <matchSum>.
176
192
// And we sum up <matchSum> and return it as this node's score.
177
- var score int64
178
- for _ , c := range s .Constraints {
193
+ var score float64
194
+ for i , c := range s .Constraints {
179
195
if tpVal , ok := node .Labels [c .TopologyKey ]; ok {
196
+ var cnt int64
180
197
if c .TopologyKey == v1 .LabelHostname {
181
- count := countPodsMatchSelector (nodeInfo .Pods , c .Selector , pod .Namespace )
182
- score += int64 (count )
198
+ cnt = int64 (countPodsMatchSelector (nodeInfo .Pods , c .Selector , pod .Namespace ))
183
199
} else {
184
200
pair := topologyPair {key : c .TopologyKey , value : tpVal }
185
- matchSum := * s .TopologyPairToPodCounts [pair ]
186
- score += matchSum
201
+ cnt = * s .TopologyPairToPodCounts [pair ]
187
202
}
203
+ score += float64 (cnt ) * s .TopologyNormalizingWeight [i ]
188
204
}
189
205
}
190
- return score , nil
206
+ return int64 ( score ) , nil
191
207
}
192
208
193
209
// NormalizeScore invoked after scoring all nodes.
@@ -200,41 +216,41 @@ func (pl *PodTopologySpread) NormalizeScore(ctx context.Context, cycleState *fra
200
216
return nil
201
217
}
202
218
203
- // Calculate the summed <total> score and <minScore>.
219
+ // Calculate <minScore> and <maxScore>
204
220
var minScore int64 = math .MaxInt64
205
- var total int64
221
+ var maxScore int64
206
222
for _ , score := range scores {
207
223
// it's mandatory to check if <score.Name> is present in m.IgnoredNodes
208
224
if s .IgnoredNodes .Has (score .Name ) {
209
225
continue
210
226
}
211
- total += score .Score
212
227
if score .Score < minScore {
213
228
minScore = score .Score
214
229
}
230
+ if score .Score > maxScore {
231
+ maxScore = score .Score
232
+ }
215
233
}
216
234
217
- maxMinDiff := total - minScore
218
235
for i := range scores {
219
236
nodeInfo , err := pl .sharedLister .NodeInfos ().Get (scores [i ].Name )
220
237
if err != nil {
221
238
return framework .NewStatus (framework .Error , err .Error ())
222
239
}
223
240
node := nodeInfo .Node ()
224
241
225
- if maxMinDiff == 0 {
226
- scores [i ].Score = framework . MaxNodeScore
242
+ if s . IgnoredNodes . Has ( node . Name ) {
243
+ scores [i ].Score = 0
227
244
continue
228
245
}
229
246
230
- if s . IgnoredNodes . Has ( node . Name ) {
231
- scores [i ].Score = 0
247
+ if maxScore == 0 {
248
+ scores [i ].Score = framework . MaxNodeScore
232
249
continue
233
250
}
234
251
235
- flippedScore := total - scores [i ].Score
236
- fScore := float64 (framework .MaxNodeScore ) * (float64 (flippedScore ) / float64 (maxMinDiff ))
237
- scores [i ].Score = int64 (fScore )
252
+ s := scores [i ].Score
253
+ scores [i ].Score = framework .MaxNodeScore * (maxScore + minScore - s ) / maxScore
238
254
}
239
255
return nil
240
256
}
@@ -256,3 +272,16 @@ func getPreScoreState(cycleState *framework.CycleState) (*preScoreState, error)
256
272
}
257
273
return s , nil
258
274
}
275
+
276
+ // topologyNormalizingWeight calculates the weight for the topology, based on
277
+ // the number of values that exist for a topology.
278
+ // Since <size> is at least 1 (all nodes that passed the Filters are in the
279
+ // same topology), and k8s supports 5k nodes, the result is in the interval
280
+ // <1.09, 8.52>.
281
+ //
282
+ // Note: <size> could also be zero when no nodes have the required topologies,
283
+ // however we don't care about topology weight in this case as we return a 0
284
+ // score for all nodes.
285
+ func topologyNormalizingWeight (size int ) float64 {
286
+ return math .Log (float64 (size + 2 ))
287
+ }
0 commit comments