@@ -209,6 +209,77 @@ def assert_autoscaling_action(
209209 )
210210
211211
212+ @pytest .fixture
213+ def autoscaler_max_upscaling_delta_setup ():
214+ resource_manager = MagicMock (
215+ spec = ResourceManager , get_budget = MagicMock (return_value = None )
216+ )
217+
218+ actor_pool = MagicMock (
219+ spec = _ActorPool ,
220+ min_size = MagicMock (return_value = 5 ),
221+ max_size = MagicMock (return_value = 20 ),
222+ current_size = MagicMock (return_value = 10 ),
223+ get_current_size = MagicMock (return_value = 10 ),
224+ num_pending_actors = MagicMock (return_value = 0 ),
225+ get_pool_util = MagicMock (return_value = 2.0 ),
226+ )
227+
228+ op = MagicMock (
229+ spec = InternalQueueOperatorMixin ,
230+ completed = MagicMock (return_value = False ),
231+ _inputs_complete = False ,
232+ )
233+ op_state = MagicMock (
234+ spec = OpState ,
235+ total_enqueued_input_blocks = MagicMock (return_value = 1 ),
236+ )
237+ op_state ._scheduling_status = MagicMock (under_resource_limits = True )
238+ return resource_manager , actor_pool , op , op_state
239+
240+ def test_actor_pool_scaling_respects_small_max_upscaling_delta (
241+ autoscaler_max_upscaling_delta_setup ,
242+ ):
243+ resource_manager , actor_pool , op , op_state = autoscaler_max_upscaling_delta_setup
244+ autoscaler = DefaultActorAutoscaler (
245+ topology = MagicMock (),
246+ resource_manager = resource_manager ,
247+ config = AutoscalingConfig (
248+ actor_pool_util_upscaling_threshold = 1.0 ,
249+ actor_pool_util_downscaling_threshold = 0.5 ,
250+ actor_pool_max_upscaling_delta = 3 ,
251+ ),
252+ )
253+ request = autoscaler ._derive_target_scaling_config (
254+ actor_pool = actor_pool ,
255+ op = op ,
256+ op_state = op_state ,
257+ )
258+ assert request .delta == 3
259+ assert "max_upscaling_delta=3" in request .reason
260+
261+ def test_actor_pool_scaling_respects_large_max_upscaling_delta (
262+ autoscaler_max_upscaling_delta_setup ,
263+ ):
264+ resource_manager , actor_pool , op , op_state = autoscaler_max_upscaling_delta_setup
265+ autoscaler = DefaultActorAutoscaler (
266+ topology = MagicMock (),
267+ resource_manager = resource_manager ,
268+ config = AutoscalingConfig (
269+ actor_pool_util_upscaling_threshold = 1.0 ,
270+ actor_pool_util_downscaling_threshold = 0.5 ,
271+ actor_pool_max_upscaling_delta = 100 ,
272+ ),
273+ )
274+ request = autoscaler ._derive_target_scaling_config (
275+ actor_pool = actor_pool ,
276+ op = op ,
277+ op_state = op_state ,
278+ )
279+ assert request .delta == 10
280+ assert "max_upscaling_delta=10" in request .reason
281+
282+
212283def test_cluster_scaling ():
213284 """Test `_try_scale_up_cluster` in `DefaultAutoscaler`"""
214285 op1 = MagicMock (
@@ -417,6 +488,62 @@ def __call__(self, row):
417488 assert expected_message not in wanr_log_args_str
418489
419490
491+ @pytest .fixture
492+ def autoscaler_config_mocks ():
493+ resource_manager = MagicMock (spec = ResourceManager )
494+ topology = MagicMock ()
495+ topology .items = MagicMock (return_value = [])
496+ return resource_manager , topology
497+
498+
499+ def test_autoscaling_config_validation_zero_delta (autoscaler_config_mocks ):
500+ resource_manager , topology = autoscaler_config_mocks
501+
502+ with pytest .raises (
503+ ValueError , match = "actor_pool_max_upscaling_delta must be positive"
504+ ):
505+ DefaultActorAutoscaler (
506+ topology = topology ,
507+ resource_manager = resource_manager ,
508+ config = AutoscalingConfig (
509+ actor_pool_util_upscaling_threshold = 1.0 ,
510+ actor_pool_util_downscaling_threshold = 0.5 ,
511+ actor_pool_max_upscaling_delta = 0 ,
512+ ),
513+ )
514+
515+ def test_autoscaling_config_validation_negative_delta (autoscaler_config_mocks ):
516+ resource_manager , topology = autoscaler_config_mocks
517+
518+ with pytest .raises (
519+ ValueError , match = "actor_pool_max_upscaling_delta must be positive"
520+ ):
521+ DefaultActorAutoscaler (
522+ topology = topology ,
523+ resource_manager = resource_manager ,
524+ config = AutoscalingConfig (
525+ actor_pool_util_upscaling_threshold = 1.0 ,
526+ actor_pool_util_downscaling_threshold = 0.5 ,
527+ actor_pool_max_upscaling_delta = - 1 ,
528+ ),
529+ )
530+
531+
532+ def test_autoscaling_config_validation_positive_delta (autoscaler_config_mocks ):
533+ resource_manager , topology = autoscaler_config_mocks
534+
535+ autoscaler = DefaultActorAutoscaler (
536+ topology = topology ,
537+ resource_manager = resource_manager ,
538+ config = AutoscalingConfig (
539+ actor_pool_util_upscaling_threshold = 1.0 ,
540+ actor_pool_util_downscaling_threshold = 0.5 ,
541+ actor_pool_max_upscaling_delta = 5 ,
542+ ),
543+ )
544+ assert autoscaler ._actor_pool_max_upscaling_delta == 5
545+
546+
420547if __name__ == "__main__" :
421548 import sys
422549
0 commit comments