1+ /* 
2+ Copyright 2024 The Aibrix Team. 
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  algorithm
18+ 
19+ import  (
20+ 	"testing" 
21+ 
22+ 	"github.com/vllm-project/aibrix/pkg/controller/podautoscaler/common" 
23+ )
24+ 
25+ func  TestApaScalingAlgorithm_ComputeTargetReplicas (t  * testing.T ) {
26+ 	algorithm  :=  & ApaScalingAlgorithm {}
27+ 
28+ 	tests  :=  []struct  {
29+ 		name             string 
30+ 		currentPodCount  float64 
31+ 		context          * common.MockScalingContext 
32+ 		expected         int32 
33+ 		description      string 
34+ 	}{
35+ 		{
36+ 			name :            "no_scaling_within_tolerance" ,
37+ 			currentPodCount : 3.0 ,
38+ 			context : & common.MockScalingContext {
39+ 				TargetValue :              50.0 ,
40+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
41+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
42+ 				MaxScaleUpRate :           2.0 ,   // Can scale up by 100% 
43+ 				MaxScaleDownRate :         2.0 ,   // Can scale down by 50% 
44+ 				CurrentUsePerPod :         50.0 ,  // Exactly at target 
45+ 				MinReplicas :              1 ,
46+ 				MaxReplicas :              10 ,
47+ 			},
48+ 			expected :    3 ,
49+ 			description : "Should not scale when current use per pod equals target value" ,
50+ 		},
51+ 		{
52+ 			name :            "no_scaling_within_up_tolerance" ,
53+ 			currentPodCount : 3.0 ,
54+ 			context : & common.MockScalingContext {
55+ 				TargetValue :              50.0 ,
56+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
57+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
58+ 				MaxScaleUpRate :           2.0 ,
59+ 				MaxScaleDownRate :         2.0 ,
60+ 				CurrentUsePerPod :         54.0 ,  // 54/50 = 1.08, within 1+0.1=1.1 tolerance 
61+ 				MinReplicas :              1 ,
62+ 				MaxReplicas :              10 ,
63+ 			},
64+ 			expected :    3 ,
65+ 			description : "Should not scale when within up tolerance (8% above target)" ,
66+ 		},
67+ 		{
68+ 			name :            "no_scaling_within_down_tolerance" ,
69+ 			currentPodCount : 3.0 ,
70+ 			context : & common.MockScalingContext {
71+ 				TargetValue :              50.0 ,
72+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
73+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
74+ 				MaxScaleUpRate :           2.0 ,
75+ 				MaxScaleDownRate :         2.0 ,
76+ 				CurrentUsePerPod :         42.0 ,  // 42/50 = 0.84, within 1-0.2=0.8 tolerance 
77+ 				MinReplicas :              1 ,
78+ 				MaxReplicas :              10 ,
79+ 			},
80+ 			expected :    3 ,
81+ 			description : "Should not scale when within down tolerance (16% below target)" ,
82+ 		},
83+ 		{
84+ 			name :            "scale_up_beyond_tolerance" ,
85+ 			currentPodCount : 3.0 ,
86+ 			context : & common.MockScalingContext {
87+ 				TargetValue :              50.0 ,
88+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
89+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
90+ 				MaxScaleUpRate :           2.0 ,   // Can scale up by 100% 
91+ 				MaxScaleDownRate :         2.0 ,
92+ 				CurrentUsePerPod :         60.0 ,  // 60/50 = 1.2, exceeds 1+0.1=1.1 tolerance 
93+ 				MinReplicas :              1 ,
94+ 				MaxReplicas :              10 ,
95+ 			},
96+ 			expected :    4 ,  // ceil(3 * (60/50)) = ceil(3.6) = 4 
97+ 			description : "Should scale up when current use exceeds up tolerance threshold" ,
98+ 		},
99+ 		{
100+ 			name :            "scale_down_beyond_tolerance" ,
101+ 			currentPodCount : 5.0 ,
102+ 			context : & common.MockScalingContext {
103+ 				TargetValue :              50.0 ,
104+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
105+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
106+ 				MaxScaleUpRate :           2.0 ,
107+ 				MaxScaleDownRate :         2.0 ,   // Can scale down by 50% 
108+ 				CurrentUsePerPod :         30.0 ,  // 30/50 = 0.6, below 1-0.2=0.8 tolerance 
109+ 				MinReplicas :              1 ,
110+ 				MaxReplicas :              10 ,
111+ 			},
112+ 			expected :    3 ,  // ceil(5 * (30/50)) = ceil(3.0) = 3 
113+ 			description : "Should scale down when current use falls below down tolerance threshold" ,
114+ 		},
115+ 		{
116+ 			name :            "scale_up_limited_by_max_scale_up_rate" ,
117+ 			currentPodCount : 3.0 ,
118+ 			context : & common.MockScalingContext {
119+ 				TargetValue :              50.0 ,
120+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
121+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
122+ 				MaxScaleUpRate :           1.5 ,   // Can only scale up by 50% 
123+ 				MaxScaleDownRate :         2.0 ,
124+ 				CurrentUsePerPod :         100.0 , // 100/50 = 2.0, would need 6 pods but limited 
125+ 				MinReplicas :              1 ,
126+ 				MaxReplicas :              10 ,
127+ 			},
128+ 			expected :    5 ,  // ceil(1.5 * 3) = ceil(4.5) = 5 (limited by max scale up rate) 
129+ 			description : "Should be limited by max scale up rate" ,
130+ 		},
131+ 		{
132+ 			name :            "scale_down_limited_by_max_scale_down_rate" ,
133+ 			currentPodCount : 6.0 ,
134+ 			context : & common.MockScalingContext {
135+ 				TargetValue :              50.0 ,
136+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
137+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
138+ 				MaxScaleUpRate :           2.0 ,
139+ 				MaxScaleDownRate :         3.0 ,   // Can scale down to 1/3 = 33% of current (6/3=2 minimum) 
140+ 				CurrentUsePerPod :         10.0 ,  // 10/50 = 0.2, would need 1.2 pods but limited 
141+ 				MinReplicas :              1 ,
142+ 				MaxReplicas :              10 ,
143+ 			},
144+ 			expected :    2 ,  // floor(6/3) = floor(2) = 2 
145+ 			description : "Should be limited by max scale down rate" ,
146+ 		},
147+ 		{
148+ 			name :            "extreme_scale_up_case" ,
149+ 			currentPodCount : 2.0 ,
150+ 			context : & common.MockScalingContext {
151+ 				TargetValue :              10.0 ,
152+ 				UpFluctuationTolerance :   0.1 ,   // 10% tolerance 
153+ 				DownFluctuationTolerance : 0.2 ,   // 20% tolerance 
154+ 				MaxScaleUpRate :           3.0 ,   // Can scale up by 200% 
155+ 				MaxScaleDownRate :         2.0 ,
156+ 				CurrentUsePerPod :         50.0 ,  // 50/10 = 5.0, would need 10 pods 
157+ 				MinReplicas :              1 ,
158+ 				MaxReplicas :              20 ,
159+ 			},
160+ 			expected :    6 ,  // ceil(3.0 * 2) = 6 (limited by max scale up rate) 
161+ 			description : "Should handle extreme scale up scenarios with rate limiting" ,
162+ 		},
163+ 	}
164+ 
165+ 	for  _ , tt  :=  range  tests  {
166+ 		t .Run (tt .name , func (t  * testing.T ) {
167+ 			result  :=  algorithm .ComputeTargetReplicas (tt .currentPodCount , tt .context )
168+ 			if  result  !=  tt .expected  {
169+ 				t .Errorf ("ComputeTargetReplicas() = %d, expected %d. %s" , result , tt .expected , tt .description )
170+ 				t .Logf ("Current pod count: %.1f" , tt .currentPodCount )
171+ 				t .Logf ("Current use per pod: %.1f" , tt .context .CurrentUsePerPod )
172+ 				t .Logf ("Target value: %.1f" , tt .context .TargetValue )
173+ 				t .Logf ("Up tolerance: %.3f" , tt .context .UpFluctuationTolerance )
174+ 				t .Logf ("Down tolerance: %.3f" , tt .context .DownFluctuationTolerance )
175+ 				t .Logf ("Ratio (current/expected): %.6f" , tt .context .CurrentUsePerPod / tt .context .TargetValue )
176+ 				t .Logf ("Up threshold: %.6f" , 1 + tt .context .UpFluctuationTolerance )
177+ 				t .Logf ("Down threshold: %.6f" , 1 - tt .context .DownFluctuationTolerance )
178+ 			}
179+ 		})
180+ 	}
181+ }
0 commit comments