55-record (controller_state , {
66 stage_module ,
77 stage_pid ,
8- min_workers = 1 ,
9- max_workers = 20 ,
10- scale_up_threshold = 10 ,
11- scale_down_threshold = 2 ,
12- check_interval = 5000 % 5s
8+ min_workers = 10 ,
9+ max_workers = 200 ,
10+ scale_up_threshold = 100 ,
11+ scale_down_threshold = 10 ,
12+ check_interval = 1000 , % 1s
13+ consecutive_hot = 0 ,
14+ consecutive_cold = 0 ,
15+ last_action_ms = 0 ,
16+ action_cooldown_ms = 2000 ,
17+ min_samples_to_act = 1
1318}).
1419
1520spawn_controller (StageModule , StagePid ) ->
1621 State = # controller_state {stage_module = StageModule , stage_pid = StagePid },
1722 Pid = spawn (? MODULE , loop , [State ]),
18- ControllerName = list_to_atom (atom_to_list (StageModule ) ++ " _controler " ),
23+ ControllerName = list_to_atom (atom_to_list (StageModule ) ++ " _controller " ),
1924 register (ControllerName , Pid ),
2025 Pid .
2126
@@ -42,25 +47,60 @@ check_stage_balance(State) ->
4247
4348 case is_process_alive (StagePid ) of
4449 true ->
45- { message_queue_len , QLen } = process_info ( StagePid , message_queue_len ),
50+ MaxWorkerQLen = max_worker_queue ( StageModule ),
4651 NumWorkers = stage_behaviour :count_workers (StageModule ),
4752
48- io :format (" [+][~p ][~p ] - Analisando métricas: QLen =~p , Workers=~p , Up=~p , Down=~p~n " ,
49- [calendar :local_time (), self (), QLen , NumWorkers , Up , Down ]),
53+ io :format (" [+][~p ][~p ] - Métricas: MaxWorkerQLen =~p , Workers=~p , Up=~p , Down=~p~n " ,
54+ [calendar :local_time (), self (), MaxWorkerQLen , NumWorkers , Up , Down ]),
5055
51- if
52- QLen > Up andalso NumWorkers < Max ->
53- io :format (" [+][~p ][~p ] - Aumento workers para ~p . QLen: ~p , Workers: ~p~n " , [calendar :local_time (), self (), StageModule , QLen , NumWorkers ]),
54- stage_behaviour :add_worker (StageModule , self ());
55-
56- QLen < Down andalso NumWorkers > Min ->
57- io :format (" [-][~p ][~p ] - Diminuindo workers para ~p . QLen: ~p , Workers: ~p~n " , [calendar :local_time (), self (), StageModule , QLen , NumWorkers ]),
58- stage_behaviour :remove_worker (StageModule , any );
59- true ->
60- io :format (" [+][~p ][~p ] - Workers estáveis para ~p . QLen: ~p , Workers: ~p~n " , [calendar :local_time (), self (), StageModule , QLen , NumWorkers ])
61- end ;
56+ NewState1 =
57+ if MaxWorkerQLen > Up -> incr_hot (reset_cold (State ));
58+ MaxWorkerQLen < Down -> incr_cold (reset_hot (State ));
59+ true -> reset_hot (reset_cold (State ))
60+ end ,
61+ NewState2 = maybe_scale (StageModule , NumWorkers , Min , Max , NewState1 ),
62+ NewState2 ;
6263 false ->
6364 io :format (" [-][~p ][~p ] - Stage ~p não está mais vivo~n " , [calendar :local_time (), self (), StageModule ])
6465 end ,
6566 State .
67+
68+ max_worker_queue (StageModule ) ->
69+ StageKey = list_to_atom (atom_to_list (StageModule ) ++ " _workers" ),
70+ case ets :lookup (erlda_workers_table , StageKey ) of
71+ [{StageKey , Workers }] when is_list (Workers ), Workers =/= [] ->
72+ lists :max ([queue_len (W ) || W <- Workers ]);
73+ _ -> 0
74+ end .
75+
76+ queue_len (Pid ) when is_pid (Pid ) ->
77+ case process_info (Pid , message_queue_len ) of
78+ {message_queue_len , N } -> N ;
79+ _ -> 0
80+ end .
81+
82+ now_ms () -> erlang :monotonic_time (millisecond ).
83+
84+ cooldown_over (# controller_state { last_action_ms = T , action_cooldown_ms = C }) ->
85+ now_ms () - T >= C .
86+
87+ incr_hot (S = # controller_state { consecutive_hot = H }) -> S # controller_state { consecutive_hot = H + 1 }.
88+ incr_cold (S = # controller_state { consecutive_cold = C }) -> S # controller_state { consecutive_cold = C + 1 }.
89+ reset_hot (S ) -> S # controller_state { consecutive_hot = 0 }.
90+ reset_cold (S ) -> S # controller_state { consecutive_cold = 0 }.
91+
92+ maybe_scale (StageModule , NumWorkers , Min , Max , S = # controller_state { consecutive_hot = H , consecutive_cold = C , min_samples_to_act = K }) ->
93+ CanAct = cooldown_over (S ),
94+ case true of
95+ _ when H >= K andalso NumWorkers < Max andalso CanAct ->
96+ io :format (" [+][~p ][~p ] - Scale UP ~p (consecutive_hot=~p )~n " , [calendar :local_time (), self (), StageModule , H ]),
97+ stage_behaviour :add_worker (StageModule ),
98+ S # controller_state { consecutive_hot = 0 , last_action_ms = now_ms () };
99+ _ when C >= K andalso NumWorkers > Min andalso CanAct ->
100+ io :format (" [-][~p ][~p ] - Scale DOWN ~p (consecutive_cold=~p )~n " , [calendar :local_time (), self (), StageModule , C ]),
101+ stage_behaviour :remove_worker (StageModule , any ),
102+ S # controller_state { consecutive_cold = 0 , last_action_ms = now_ms () };
103+ _ ->
104+ S
105+ end .
66106
0 commit comments