Files
2026-07-13 13:17:40 +08:00

111 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Application-level autoscaling policy for video processing pipeline.
Scaling strategy:
- VideoAnalyzer: scales based on its load (error_ratio = requests / capacity)
- VideoEncoder: scales based on its load, with floor = VideoAnalyzer replicas
- MultiDecoder: 0.5x VideoEncoder replicas
Error ratio formula:
error_ratio = total_ongoing_requests / (target_per_replica × current_replicas)
- error_ratio > 1.0 → over capacity → scale up
- error_ratio < 1.0 → under capacity → scale down
- error_ratio = 1.0 → at capacity → no change
"""
import math
from typing import Dict, Tuple
from ray.serve._private.common import DeploymentID
from ray.serve.config import AutoscalingContext
def _get_error_ratio(ctx: AutoscalingContext) -> float:
"""
Calculate error ratio: how much over/under target capacity we are.
Returns 1.0 when idle to maintain current replicas.
"""
target_per_replica = ctx.config.target_ongoing_requests or 1
total_requests = ctx.total_num_requests
current_replicas = ctx.current_num_replicas
if total_requests == 0:
return 1.0 # Idle: maintain current replicas
total_capacity = target_per_replica * current_replicas
return total_requests / total_capacity
def _scale_by_error_ratio(ctx: AutoscalingContext, floor: int = 0) -> int:
"""
Calculate target replicas based on error ratio.
Args:
ctx: Deployment autoscaling context
floor: Minimum replicas (in addition to capacity_adjusted_min)
Returns:
Target replica count, clamped to min/max limits
"""
error_ratio = _get_error_ratio(ctx)
# Scale current replicas by error ratio
target = int(math.ceil(ctx.current_num_replicas * error_ratio))
# Apply floor (e.g., encoder should have at least as many as analyzer)
target = max(target, floor)
# Clamp to configured limits
return max(
ctx.capacity_adjusted_min_replicas,
min(ctx.capacity_adjusted_max_replicas, target),
)
def _find_deployment(
contexts: Dict[DeploymentID, AutoscalingContext],
name: str,
) -> Tuple[DeploymentID, AutoscalingContext]:
"""Find deployment by name."""
for dep_id, ctx in contexts.items():
if dep_id.name == name:
return dep_id, ctx
raise KeyError(f"Deployment '{name}' not found")
def coordinated_scaling_policy(
contexts: Dict[DeploymentID, AutoscalingContext],
) -> Tuple[Dict[DeploymentID, int], Dict]:
"""
Coordinated scaling for video processing pipeline.
Scaling rules:
VideoAnalyzer: scale by its own load
VideoEncoder: scale by its own load, floor = analyzer replicas
MultiDecoder: 0.5x encoder replicas
"""
decisions: Dict[DeploymentID, int] = {}
# 1. VideoAnalyzer: scale by load
analyzer_id, analyzer_ctx = _find_deployment(contexts, "VideoAnalyzer")
analyzer_replicas = _scale_by_error_ratio(analyzer_ctx)
decisions[analyzer_id] = analyzer_replicas
# 2. VideoEncoder: scale by load, but at least as many as analyzer
encoder_id, encoder_ctx = _find_deployment(contexts, "VideoEncoder")
encoder_replicas = _scale_by_error_ratio(encoder_ctx, floor=analyzer_replicas)
decisions[encoder_id] = encoder_replicas
# 3. MultiDecoder: 0.5x encoder replicas
decoder_id, decoder_ctx = _find_deployment(contexts, "MultiDecoder")
decoder_replicas = max(1, math.ceil(encoder_replicas / 2))
decoder_replicas = max(
decoder_ctx.capacity_adjusted_min_replicas,
min(decoder_ctx.capacity_adjusted_max_replicas, decoder_replicas),
)
decisions[decoder_id] = decoder_replicas
return decisions, {}