mirror of
https://github.com/NousResearch/atropos.git
synced 2026-04-26 17:13:09 +00:00
feat: implement Elastic Environment Orchestrator (DEO) for environment microservices
This commit is contained in:
parent
c20c85256e
commit
5a1ea7d3cb
8 changed files with 478 additions and 0 deletions
67
atroposlib/orchestration/controller.py
Normal file
67
atroposlib/orchestration/controller.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import logging
|
||||
import math
|
||||
from typing import Optional, Dict, Any, List
|
||||
from .metrics import WorkloadMetrics
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ScalingController:
|
||||
"""
|
||||
Decides the "Desired Actor Count" based on workload metrics.
|
||||
Uses a dampened calculation with hysteresis to avoid flapping.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
min_actors: int = 1,
|
||||
max_actors: int = 20,
|
||||
target_pressure: float = 1.0,
|
||||
scaling_threshold: float = 0.2, # ±20%
|
||||
cooldown_seconds: int = 60,
|
||||
max_step_change: int = 4
|
||||
):
|
||||
self.min_actors = min_actors
|
||||
self.max_actors = max_actors
|
||||
self.target_pressure = target_pressure
|
||||
self.scaling_threshold = scaling_threshold
|
||||
self.cooldown_seconds = cooldown_seconds
|
||||
self.max_step_change = max_step_change
|
||||
|
||||
self.last_action_timestamp = 0
|
||||
self.current_desired = min_actors
|
||||
|
||||
def calculate_desired(self, metrics: WorkloadMetrics, current_actors: int) -> int:
|
||||
"""
|
||||
Decides the next target for the number of environment actors.
|
||||
"""
|
||||
now = metrics.timestamp
|
||||
pressure = metrics.rollout_pressure
|
||||
|
||||
# 1. Check cooldown
|
||||
if now - self.last_action_timestamp < self.cooldown_seconds:
|
||||
return self.current_desired
|
||||
|
||||
# 2. Sensitivity check (Hysteresis)
|
||||
# If work is roughly satisfying target, don't change anything.
|
||||
if abs(pressure - self.target_pressure) < self.scaling_threshold:
|
||||
return self.current_desired
|
||||
|
||||
# 3. Calculate target
|
||||
# Target = Current * (Current_Pressure / Ideal_Pressure)
|
||||
# This is a dampened proportional controller.
|
||||
raw_target = math.ceil(current_actors * (pressure / self.target_pressure))
|
||||
|
||||
# 4. Apply step constraints (Rate Limiting)
|
||||
# Don't add/remove more than max_step_change in a single move.
|
||||
diff = raw_target - current_actors
|
||||
if abs(diff) > self.max_step_change:
|
||||
raw_target = current_actors + (self.max_step_change if diff > 0 else -self.max_step_change)
|
||||
|
||||
# 5. Apply world bounds
|
||||
final_target = max(self.min_actors, min(self.max_actors, raw_target))
|
||||
|
||||
if final_target != current_actors:
|
||||
self.last_action_timestamp = now
|
||||
self.current_desired = final_target
|
||||
logger.info(f"Controller DECISION: Scale {current_actors} -> {final_target} (Pressure: {pressure:.2f})")
|
||||
|
||||
return final_target
|
||||
Loading…
Add table
Add a link
Reference in a new issue