From 3c3cdee781106dd97c5afaa34cf04bc055beca22 Mon Sep 17 00:00:00 2001 From: michaelawyu Date: Fri, 2 Jun 2023 08:14:55 +0800 Subject: [PATCH 1/2] Added framework score --- pkg/scheduler/framework/score.go | 65 +++++ pkg/scheduler/framework/score_test.go | 357 ++++++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 pkg/scheduler/framework/score.go create mode 100644 pkg/scheduler/framework/score_test.go diff --git a/pkg/scheduler/framework/score.go b/pkg/scheduler/framework/score.go new file mode 100644 index 000000000..7c4a902d5 --- /dev/null +++ b/pkg/scheduler/framework/score.go @@ -0,0 +1,65 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package framework + +import ( + fleetv1 "go.goms.io/fleet/apis/v1" +) + +// ClusterScore is the scores the scheduler assigns to a cluster. +type ClusterScore struct { + // TopologySpreadScore determines how much a binding would satisfy the topology spread + // constraints specified by the user. + TopologySpreadScore int + // AffinityScore determines how much a binding would satisfy the affinity terms + // specified by the user. + AffinityScore int + // PriorityScore determines how much a binding would satisfy the priority terms + // specified by the user. + PriorityScore int +} + +// Add adds a ClusterScore to another ClusterScore. +func (s1 *ClusterScore) Add(s2 ClusterScore) { + s1.TopologySpreadScore += s2.TopologySpreadScore + s1.AffinityScore += s2.AffinityScore + s1.PriorityScore += s2.PriorityScore +} + +// Less returns true if a ClusterScore is less than another. +func (s1 *ClusterScore) Less(s2 *ClusterScore) bool { + if s1.TopologySpreadScore != s2.TopologySpreadScore { + return s1.TopologySpreadScore < s2.TopologySpreadScore + } + + if s1.AffinityScore != s2.AffinityScore { + return s1.AffinityScore < s2.AffinityScore + } + + return s1.PriorityScore < s2.PriorityScore +} + +// ScoredCluster is a cluster with a score. +type ScoredCluster struct { + Cluster *fleetv1.MemberCluster + Score *ClusterScore +} + +// ScoredClusters is a list of ScoredClusters; this type implements the sort.Interface. +type ScoredClusters []*ScoredCluster + +// Len returns the length of a ScoredClusters; it implemented sort.Interface.Len(). +func (sc ScoredClusters) Len() int { return len(sc) } + +// Less returns true if a ScoredCluster is of a lower score than another; it implemented sort.Interface.Less(). +func (sc ScoredClusters) Less(i, j int) bool { + return sc[i].Score.Less(sc[j].Score) +} + +// Swap swaps two ScoredClusters in the list; it implemented sort.Interface.Swap(). +func (sc ScoredClusters) Swap(i, j int) { + sc[i], sc[j] = sc[j], sc[i] +} diff --git a/pkg/scheduler/framework/score_test.go b/pkg/scheduler/framework/score_test.go new file mode 100644 index 000000000..218c8b258 --- /dev/null +++ b/pkg/scheduler/framework/score_test.go @@ -0,0 +1,357 @@ +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package framework + +import ( + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + + fleetv1 "go.goms.io/fleet/apis/v1" +) + +func TestClusterScoreAdd(t *testing.T) { + s1 := &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 0, + PriorityScore: 0, + } + + s2 := &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 5, + PriorityScore: 80, + } + + s1.Add(*s2) + want := &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 5, + PriorityScore: 80, + } + if !cmp.Equal(s1, want) { + t.Fatalf("Add() = %v, want %v", s1, want) + } +} + +func TestClusterScoreLess(t *testing.T) { + testCases := []struct { + name string + s1 *ClusterScore + s2 *ClusterScore + want bool + }{ + { + name: "s1 is less than s2 in topology spread score", + s1: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + s2: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 30, + }, + want: true, + }, + { + name: "s1 is less than s2 in affinity score", + s1: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + s2: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 30, + }, + want: true, + }, + { + name: "s1 is less than s2 in priority score", + s1: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + s2: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 30, + }, + want: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if tc.s1.Less(tc.s2) != tc.want { + t.Fatalf("Less(%v, %v) = %t, want %t", tc.s1, tc.s2, !tc.want, tc.want) + } + + if tc.s2.Less(tc.s1) != !tc.want { + t.Fatalf("Less(%v, %v) = %t, want %t", tc.s2, tc.s1, tc.want, !tc.want) + } + }) + } +} + +func TestClusterScoreEqual(t *testing.T) { + s1 := &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 0, + PriorityScore: 0, + } + + s2 := &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 0, + PriorityScore: 0, + } + + if s1.Less(s2) || s2.Less(s1) { + t.Fatalf("Less(%v, %v) = %v, Less(%v, %v) = %v, want both to be false", s1, s2, s1.Less(s2), s2, s1, s2.Less(s1)) + } +} + +func TestScoredClustersSort(t *testing.T) { + clusterA := &fleetv1.MemberCluster{} + clusterB := &fleetv1.MemberCluster{} + clusterC := &fleetv1.MemberCluster{} + clusterD := &fleetv1.MemberCluster{} + + testCases := []struct { + name string + scs ScoredClusters + want ScoredClusters + }{ + { + name: "sort asc values", + scs: ScoredClusters{ + { + Cluster: clusterA, + Score: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterB, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterC, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 20, + }, + }, + { + Cluster: clusterD, + Score: &ClusterScore{ + TopologySpreadScore: 2, + AffinityScore: 30, + PriorityScore: 40, + }, + }, + }, + want: ScoredClusters{ + { + Cluster: clusterD, + Score: &ClusterScore{ + TopologySpreadScore: 2, + AffinityScore: 30, + PriorityScore: 40, + }, + }, + { + Cluster: clusterC, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 20, + }, + }, + { + Cluster: clusterB, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterA, + Score: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + }, + }, + { + name: "sort desc values", + scs: ScoredClusters{ + { + Cluster: clusterD, + Score: &ClusterScore{ + TopologySpreadScore: 2, + AffinityScore: 30, + PriorityScore: 40, + }, + }, + { + Cluster: clusterC, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 20, + }, + }, + { + Cluster: clusterB, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterA, + Score: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + }, + want: ScoredClusters{ + { + Cluster: clusterD, + Score: &ClusterScore{ + TopologySpreadScore: 2, + AffinityScore: 30, + PriorityScore: 40, + }, + }, + { + Cluster: clusterC, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 20, + }, + }, + { + Cluster: clusterB, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterA, + Score: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + }, + }, + { + name: "sort values in random", + scs: ScoredClusters{ + { + Cluster: clusterC, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 20, + }, + }, + { + Cluster: clusterD, + Score: &ClusterScore{ + TopologySpreadScore: 2, + AffinityScore: 30, + PriorityScore: 40, + }, + }, + { + Cluster: clusterA, + Score: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterB, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + }, + want: ScoredClusters{ + { + Cluster: clusterD, + Score: &ClusterScore{ + TopologySpreadScore: 2, + AffinityScore: 30, + PriorityScore: 40, + }, + }, + { + Cluster: clusterC, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 20, + PriorityScore: 20, + }, + }, + { + Cluster: clusterB, + Score: &ClusterScore{ + TopologySpreadScore: 1, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + { + Cluster: clusterA, + Score: &ClusterScore{ + TopologySpreadScore: 0, + AffinityScore: 10, + PriorityScore: 20, + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + sort.Sort(sort.Reverse(tc.scs)) + if !cmp.Equal(tc.scs, tc.want) { + t.Fatalf("Sort() = %v, want %v", tc.scs, tc.want) + } + }) + } +} From 15d614d587b0de791a73e0c8d5e0caaff7b104a6 Mon Sep 17 00:00:00 2001 From: michaelawyu Date: Tue, 6 Jun 2023 20:12:10 +0800 Subject: [PATCH 2/2] Minor fixes --- pkg/scheduler/framework/score.go | 10 +----- pkg/scheduler/framework/score_test.go | 47 --------------------------- 2 files changed, 1 insertion(+), 56 deletions(-) diff --git a/pkg/scheduler/framework/score.go b/pkg/scheduler/framework/score.go index 7c4a902d5..b8b4b5996 100644 --- a/pkg/scheduler/framework/score.go +++ b/pkg/scheduler/framework/score.go @@ -17,16 +17,12 @@ type ClusterScore struct { // AffinityScore determines how much a binding would satisfy the affinity terms // specified by the user. AffinityScore int - // PriorityScore determines how much a binding would satisfy the priority terms - // specified by the user. - PriorityScore int } // Add adds a ClusterScore to another ClusterScore. func (s1 *ClusterScore) Add(s2 ClusterScore) { s1.TopologySpreadScore += s2.TopologySpreadScore s1.AffinityScore += s2.AffinityScore - s1.PriorityScore += s2.PriorityScore } // Less returns true if a ClusterScore is less than another. @@ -35,11 +31,7 @@ func (s1 *ClusterScore) Less(s2 *ClusterScore) bool { return s1.TopologySpreadScore < s2.TopologySpreadScore } - if s1.AffinityScore != s2.AffinityScore { - return s1.AffinityScore < s2.AffinityScore - } - - return s1.PriorityScore < s2.PriorityScore + return s1.AffinityScore < s2.AffinityScore } // ScoredCluster is a cluster with a score. diff --git a/pkg/scheduler/framework/score_test.go b/pkg/scheduler/framework/score_test.go index 218c8b258..ead61139c 100644 --- a/pkg/scheduler/framework/score_test.go +++ b/pkg/scheduler/framework/score_test.go @@ -18,20 +18,17 @@ func TestClusterScoreAdd(t *testing.T) { s1 := &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 0, - PriorityScore: 0, } s2 := &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 5, - PriorityScore: 80, } s1.Add(*s2) want := &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 5, - PriorityScore: 80, } if !cmp.Equal(s1, want) { t.Fatalf("Add() = %v, want %v", s1, want) @@ -50,12 +47,10 @@ func TestClusterScoreLess(t *testing.T) { s1: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, s2: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 30, }, want: true, }, @@ -64,26 +59,10 @@ func TestClusterScoreLess(t *testing.T) { s1: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, s2: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 30, - }, - want: true, - }, - { - name: "s1 is less than s2 in priority score", - s1: &ClusterScore{ - TopologySpreadScore: 1, - AffinityScore: 10, - PriorityScore: 20, - }, - s2: &ClusterScore{ - TopologySpreadScore: 1, - AffinityScore: 10, - PriorityScore: 30, }, want: true, }, @@ -106,13 +85,11 @@ func TestClusterScoreEqual(t *testing.T) { s1 := &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 0, - PriorityScore: 0, } s2 := &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 0, - PriorityScore: 0, } if s1.Less(s2) || s2.Less(s1) { @@ -139,7 +116,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -147,7 +123,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -155,7 +130,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 20, }, }, { @@ -163,7 +137,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 2, AffinityScore: 30, - PriorityScore: 40, }, }, }, @@ -173,7 +146,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 2, AffinityScore: 30, - PriorityScore: 40, }, }, { @@ -181,7 +153,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 20, }, }, { @@ -189,7 +160,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -197,7 +167,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, }, }, @@ -210,7 +179,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 2, AffinityScore: 30, - PriorityScore: 40, }, }, { @@ -218,7 +186,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 20, }, }, { @@ -226,7 +193,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -234,7 +200,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, }, }, @@ -244,7 +209,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 2, AffinityScore: 30, - PriorityScore: 40, }, }, { @@ -252,7 +216,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 20, }, }, { @@ -260,7 +223,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -268,7 +230,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, }, }, @@ -281,7 +242,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 20, }, }, { @@ -289,7 +249,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 2, AffinityScore: 30, - PriorityScore: 40, }, }, { @@ -297,7 +256,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -305,7 +263,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, }, }, @@ -315,7 +272,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 2, AffinityScore: 30, - PriorityScore: 40, }, }, { @@ -323,7 +279,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 20, - PriorityScore: 20, }, }, { @@ -331,7 +286,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 1, AffinityScore: 10, - PriorityScore: 20, }, }, { @@ -339,7 +293,6 @@ func TestScoredClustersSort(t *testing.T) { Score: &ClusterScore{ TopologySpreadScore: 0, AffinityScore: 10, - PriorityScore: 20, }, }, },