@@ -159,9 +159,12 @@ func (u *updater) RunOnce(ctx context.Context) {
159159 klog .V (3 ).InfoS ("Skipping VPA object in ignored namespace" , "vpa" , klog .KObj (vpa ), "namespace" , vpa .Namespace )
160160 continue
161161 }
162- if vpa_api_util .GetUpdateMode (vpa ) != vpa_types .UpdateModeRecreate &&
163- vpa_api_util .GetUpdateMode (vpa ) != vpa_types .UpdateModeAuto && vpa_api_util .GetUpdateMode (vpa ) != vpa_types .UpdateModeInPlaceOrRecreate {
164- klog .V (3 ).InfoS ("Skipping VPA object because its mode is not \" InPlaceOrRecreate\" , \" Recreate\" or \" Auto\" " , "vpa" , klog .KObj (vpa ))
162+ updateMode := vpa_api_util .GetUpdateMode (vpa )
163+ if updateMode != vpa_types .UpdateModeRecreate &&
164+ updateMode != vpa_types .UpdateModeAuto &&
165+ updateMode != vpa_types .UpdateModeInPlaceOrRecreate &&
166+ vpa .Spec .StartupBoost == nil {
167+ klog .V (3 ).InfoS ("Skipping VPA object because its mode is not \" InPlaceOrRecreate\" , \" Recreate\" or \" Auto\" and it doesn't have startupBoost configured" , "vpa" , klog .KObj (vpa ))
165168 continue
166169 }
167170 selector , err := u .selectorFetcher .Fetch (ctx , vpa )
@@ -226,8 +229,7 @@ func (u *updater) RunOnce(ctx context.Context) {
226229 defer vpasWithInPlaceUpdatablePodsCounter .Observe ()
227230 defer vpasWithInPlaceUpdatedPodsCounter .Observe ()
228231
229- // NOTE: this loop assumes that controlledPods are filtered
230- // to contain only Pods controlled by a VPA in auto, recreate, or inPlaceOrRecreate mode
232+ cpuStartupBoostEnabled := features .Enabled (features .CPUStartupBoost )
231233 for vpa , livePods := range controlledPods {
232234 vpaSize := len (livePods )
233235 updateMode := vpa_api_util .GetUpdateMode (vpa )
@@ -238,31 +240,80 @@ func (u *updater) RunOnce(ctx context.Context) {
238240 continue
239241 }
240242
241- evictionLimiter := u .restrictionFactory .NewPodsEvictionRestriction (creatorToSingleGroupStatsMap , podToReplicaCreatorMap )
242243 inPlaceLimiter := u .restrictionFactory .NewPodsInPlaceRestriction (creatorToSingleGroupStatsMap , podToReplicaCreatorMap )
244+ podsAvailableForUpdate := make ([]* apiv1.Pod , 0 )
245+ podsToUnboost := make ([]* apiv1.Pod , 0 )
246+ withInPlaceUpdated := false
243247
244- podsForInPlace := make ([]* apiv1.Pod , 0 )
248+ if cpuStartupBoostEnabled && vpa .Spec .StartupBoost != nil {
249+ // First, handle unboosting for pods that have finished their startup period.
250+ for _ , pod := range livePods {
251+ if vpa_api_util .PodHasCPUBoostInProgress (pod ) {
252+ if vpa_api_util .IsPodReadyAndStartupBoostDurationPassed (pod , vpa ) {
253+ podsToUnboost = append (podsToUnboost , pod )
254+ }
255+ } else {
256+ podsAvailableForUpdate = append (podsAvailableForUpdate , pod )
257+ }
258+ }
259+
260+ // Perform unboosting
261+ for _ , pod := range podsToUnboost {
262+ if inPlaceLimiter .CanUnboost (pod , vpa ) {
263+ klog .V (2 ).InfoS ("Unboosting pod" , "pod" , klog .KObj (pod ))
264+ err = u .inPlaceRateLimiter .Wait (ctx )
265+ if err != nil {
266+ klog .V (0 ).InfoS ("In-place rate limiter wait failed for unboosting" , "error" , err )
267+ return
268+ }
269+ err := inPlaceLimiter .InPlaceUpdate (pod , vpa , u .eventRecorder )
270+ if err != nil {
271+ klog .V (0 ).InfoS ("Unboosting failed" , "error" , err , "pod" , klog .KObj (pod ))
272+ metrics_updater .RecordFailedInPlaceUpdate (vpaSize , "UnboostError" )
273+ } else {
274+ klog .V (2 ).InfoS ("Successfully unboosted pod" , "pod" , klog .KObj (pod ))
275+ withInPlaceUpdated = true
276+ metrics_updater .AddInPlaceUpdatedPod (vpaSize )
277+ }
278+ }
279+ }
280+ } else {
281+ // CPU Startup Boost is not enabled or configured for this VPA,
282+ // so all live pods are available for potential standard VPA updates.
283+ podsAvailableForUpdate = livePods
284+ }
285+
286+ if updateMode == vpa_types .UpdateModeOff || updateMode == vpa_types .UpdateModeInitial {
287+ continue
288+ }
289+
290+ evictionLimiter := u .restrictionFactory .NewPodsEvictionRestriction (creatorToSingleGroupStatsMap , podToReplicaCreatorMap )
245291 podsForEviction := make ([]* apiv1.Pod , 0 )
292+ podsForInPlace := make ([]* apiv1.Pod , 0 )
293+ withInPlaceUpdatable := false
294+ withEvictable := false
246295
247296 if updateMode == vpa_types .UpdateModeInPlaceOrRecreate && features .Enabled (features .InPlaceOrRecreate ) {
248- podsForInPlace = u .getPodsUpdateOrder (filterNonInPlaceUpdatablePods (livePods , inPlaceLimiter ), vpa )
297+ podsForInPlace = u .getPodsUpdateOrder (filterNonInPlaceUpdatablePods (podsAvailableForUpdate , inPlaceLimiter ), vpa )
249298 inPlaceUpdatablePodsCounter .Add (vpaSize , len (podsForInPlace ))
299+ if len (podsForInPlace ) > 0 {
300+ withInPlaceUpdatable = true
301+ }
250302 } else {
251303 // If the feature gate is not enabled but update mode is InPlaceOrRecreate, updater will always fallback to eviction.
252304 if updateMode == vpa_types .UpdateModeInPlaceOrRecreate {
253305 klog .InfoS ("Warning: feature gate is not enabled for this updateMode" , "featuregate" , features .InPlaceOrRecreate , "updateMode" , vpa_types .UpdateModeInPlaceOrRecreate )
254306 }
255- podsForEviction = u .getPodsUpdateOrder (filterNonEvictablePods (livePods , evictionLimiter ), vpa )
307+ podsForEviction = u .getPodsUpdateOrder (filterNonEvictablePods (podsAvailableForUpdate , evictionLimiter ), vpa )
256308 evictablePodsCounter .Add (vpaSize , updateMode , len (podsForEviction ))
309+ if len (podsForEviction ) > 0 {
310+ withEvictable = true
311+ }
257312 }
258313
259- withInPlaceUpdatable := false
260- withInPlaceUpdated := false
261- withEvictable := false
262314 withEvicted := false
263315
264316 for _ , pod := range podsForInPlace {
265- withInPlaceUpdatable = true
266317 decision := inPlaceLimiter .CanInPlaceUpdate (pod )
267318
268319 if decision == utils .InPlaceDeferred {
@@ -289,7 +340,6 @@ func (u *updater) RunOnce(ctx context.Context) {
289340 }
290341
291342 for _ , pod := range podsForEviction {
292- withEvictable = true
293343 if ! evictionLimiter .CanEvict (pod ) {
294344 continue
295345 }
0 commit comments