@@ -17,6 +17,9 @@ limitations under the License.
17
17
package scheduling
18
18
19
19
import (
20
+ "math/rand/v2"
21
+
22
+ "sigs.k8s.io/controller-runtime/pkg/log"
20
23
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling/types"
21
24
)
22
25
@@ -30,41 +33,62 @@ type Scorer interface {
30
33
ScoreTargets (ctx * types.Context , pods []* types.PodMetrics , req * types.LLMRequest ) ([]PodScore , error )
31
34
}
32
35
33
- // sessionAffinity is a routing scorer that routes subsequent
34
- // requests in a session to the same pod as the first request in the
35
- // session was sent to, by giving that pod the specified weight and assigning
36
- // zero score to the rest of the targets
37
- type SessionAffinityScorer struct {
38
- weight float64
39
- datastore Datastore
36
+ // Scorer is the interface that scorers must implement
37
+ type ScorerMng struct {
38
+ scorers []Scorer
40
39
}
41
40
42
- func NewSessionAffinityScorer (weight float64 , datastore Datastore ) Scorer {
43
- return SessionAffinityScorer {
44
- weight : weight ,
45
- datastore : datastore ,
41
+ func NewScorerMng () * ScorerMng {
42
+ return & ScorerMng {
43
+ scorers : make ([]Scorer , 0 ),
46
44
}
47
45
}
48
46
49
- // ScoreTargets does the actual scoring of the target pods by the session affinity.
50
- func (s SessionAffinityScorer ) ScoreTargets (ctx * types.Context , pods []* types.PodMetrics , req * types.LLMRequest ) ([]PodScore , error ) {
51
- scoredPods := make ([]PodScore , len (pods ))
52
- selectedPodFullName := ""
47
+ func (sm * ScorerMng ) addScorer (scorer Scorer ) {
48
+ sm .scorers = append (sm .scorers , scorer )
49
+ }
50
+
51
+ func (sm * ScorerMng ) scoreTargets (ctx * types.Context , pods []* types.PodMetrics , req * types.LLMRequest ) (* types.PodMetrics , error ) {
52
+ logger := log .FromContext (ctx )
53
+
54
+ podsTotalScore := make (map [* types.PodMetrics ]float64 )
55
+
56
+ // initialize zero score for all pods
57
+ for _ , pod := range pods {
58
+ podsTotalScore [pod ] = 0.0
59
+ }
60
+
61
+ // add scores from all scorers
62
+ for _ , scorer := range sm .scorers {
63
+ scoredPods , err := scorer .ScoreTargets (ctx , pods , req )
64
+ if err != nil {
65
+ logger .Info (">>> In scoreTargets, score targets returned error" , "error" , err )
66
+ return nil , err
67
+ }
53
68
54
- if req .SessionID != "" {
55
- selectedPod := s .datastore .GetPodForSession (req .SessionID )
56
- if selectedPod != nil {
57
- selectedPodFullName = selectedPod .NamespacedName .String ()
69
+ for _ , scoredPod := range scoredPods {
70
+ podsTotalScore [scoredPod .Pod ] += scoredPod .Score
58
71
}
59
72
}
60
73
61
- // session is not defined - no score for all pods
62
- for i , pod := range pods {
63
- if selectedPodFullName == pod .NamespacedName .String () {
64
- scoredPods [i ].Score = s .weight
74
+ // select pod with maximum score, if more than one with the max score - use random pods from the list
75
+ var highestScoreTargets []* types.PodMetrics
76
+ maxScore := - 1.0
77
+
78
+ for pod , score := range podsTotalScore {
79
+ if score > maxScore {
80
+ maxScore = score
81
+ highestScoreTargets = []* types.PodMetrics {pod }
82
+ } else if score == maxScore {
83
+ highestScoreTargets = append (highestScoreTargets , pod )
65
84
}
66
- scoredPods [i ].Pod = pod
67
85
}
68
86
69
- return scoredPods , nil
87
+ // single pod with max score
88
+ if len (highestScoreTargets ) == 1 {
89
+ return highestScoreTargets [0 ], nil
90
+ }
91
+
92
+ // select random pod from list of pods with max score
93
+ return highestScoreTargets [rand .IntN (len (highestScoreTargets ))], nil
70
94
}
0 commit comments