178 lines
5.7 KiB
Python
178 lines
5.7 KiB
Python
# __begin_scheduled_batch_processing_policy__
|
|
from datetime import datetime
|
|
from typing import Any, Dict
|
|
from ray.serve.config import AutoscalingContext
|
|
|
|
|
|
def scheduled_batch_processing_policy(
|
|
ctx: AutoscalingContext,
|
|
) -> tuple[int, Dict[str, Any]]:
|
|
current_time = datetime.now()
|
|
current_hour = current_time.hour
|
|
# Scale up during business hours (9 AM - 5 PM)
|
|
if 9 <= current_hour < 17:
|
|
return 2, {"reason": "Business hours"}
|
|
# Scale up for evening batch processing (6 PM - 8 PM)
|
|
elif 18 <= current_hour < 20:
|
|
return 4, {"reason": "Evening batch processing"}
|
|
# Minimal scaling during off-peak hours
|
|
else:
|
|
return 1, {"reason": "Off-peak hours"}
|
|
|
|
|
|
# __end_scheduled_batch_processing_policy__
|
|
|
|
|
|
# __begin_custom_metrics_autoscaling_policy__
|
|
from typing import Any, Dict
|
|
from ray.serve.config import AutoscalingContext
|
|
|
|
|
|
def custom_metrics_autoscaling_policy(
|
|
ctx: AutoscalingContext,
|
|
) -> tuple[int, Dict[str, Any]]:
|
|
cpu_usage_metric = ctx.aggregated_metrics.get("cpu_usage", {})
|
|
memory_usage_metric = ctx.aggregated_metrics.get("memory_usage", {})
|
|
max_cpu_usage = list(cpu_usage_metric.values())[-1] if cpu_usage_metric else 0
|
|
max_memory_usage = (
|
|
list(memory_usage_metric.values())[-1] if memory_usage_metric else 0
|
|
)
|
|
|
|
if max_cpu_usage > 80 or max_memory_usage > 85:
|
|
return min(ctx.capacity_adjusted_max_replicas, ctx.current_num_replicas + 1), {}
|
|
elif max_cpu_usage < 30 and max_memory_usage < 40:
|
|
return max(ctx.capacity_adjusted_min_replicas, ctx.current_num_replicas - 1), {}
|
|
else:
|
|
return ctx.current_num_replicas, {}
|
|
|
|
|
|
# __end_custom_metrics_autoscaling_policy__
|
|
|
|
|
|
# __begin_application_level_autoscaling_policy__
|
|
from typing import Dict, Tuple
|
|
from ray.serve.config import AutoscalingContext
|
|
|
|
from ray.serve._private.common import DeploymentID
|
|
from ray.serve.config import AutoscalingContext
|
|
|
|
|
|
def coordinated_scaling_policy(
|
|
contexts: Dict[DeploymentID, AutoscalingContext]
|
|
) -> Tuple[Dict[DeploymentID, int], Dict]:
|
|
"""Scale deployments based on coordinated load balancing."""
|
|
decisions = {}
|
|
|
|
# Example: Scale a preprocessing deployment
|
|
preprocessing_id = [d for d in contexts if d.name == "Preprocessor"][0]
|
|
preprocessing_ctx = contexts[preprocessing_id]
|
|
|
|
# Scale based on queue depth
|
|
preprocessing_replicas = max(
|
|
preprocessing_ctx.capacity_adjusted_min_replicas,
|
|
min(
|
|
preprocessing_ctx.capacity_adjusted_max_replicas,
|
|
preprocessing_ctx.total_num_requests // 10,
|
|
),
|
|
)
|
|
decisions[preprocessing_id] = preprocessing_replicas
|
|
|
|
# Example: Scale a model deployment proportionally
|
|
model_id = [d for d in contexts if d.name == "Model"][0]
|
|
model_ctx = contexts[model_id]
|
|
|
|
# Scale model to handle preprocessing output
|
|
# Assuming model takes 2x longer than preprocessing
|
|
model_replicas = max(
|
|
model_ctx.capacity_adjusted_min_replicas,
|
|
min(model_ctx.capacity_adjusted_max_replicas, preprocessing_replicas * 2),
|
|
)
|
|
decisions[model_id] = model_replicas
|
|
|
|
return decisions, {}
|
|
|
|
# __end_application_level_autoscaling_policy__
|
|
|
|
# __begin_stateful_application_level_policy__
|
|
from typing import Dict, Tuple, Any
|
|
from ray.serve.config import AutoscalingContext
|
|
from ray.serve._private.common import DeploymentID
|
|
|
|
def stateful_application_level_policy(
|
|
contexts: Dict[DeploymentID, AutoscalingContext]
|
|
) -> Tuple[Dict[DeploymentID, int], Dict[DeploymentID, Dict[str, Any]]]:
|
|
"""Example policy demonstrating per-deployment state persistence."""
|
|
decisions = {}
|
|
policy_state = {}
|
|
|
|
for deployment_id, ctx in contexts.items():
|
|
# Read previous state for this deployment (persisted from last iteration)
|
|
prev_state = ctx.policy_state or {}
|
|
scale_count = prev_state.get("scale_count", 0)
|
|
last_replicas = prev_state.get("last_replicas", ctx.current_num_replicas)
|
|
|
|
# Simple scaling logic: scale based on queue depth
|
|
desired_replicas = max(
|
|
ctx.capacity_adjusted_min_replicas,
|
|
min(
|
|
ctx.capacity_adjusted_max_replicas,
|
|
ctx.total_num_requests // 10,
|
|
),
|
|
)
|
|
decisions[deployment_id] = desired_replicas
|
|
|
|
# Store per-deployment state that persists across iterations
|
|
policy_state[deployment_id] = {
|
|
"scale_count": scale_count + 1,
|
|
"last_replicas": desired_replicas,
|
|
}
|
|
|
|
return decisions, policy_state
|
|
|
|
|
|
# __end_stateful_application_level_policy__
|
|
# __begin_apply_autoscaling_config_example__
|
|
from typing import Any, Dict
|
|
from ray.serve.config import AutoscalingContext
|
|
|
|
|
|
def queue_length_based_autoscaling_policy(
|
|
ctx: AutoscalingContext,
|
|
) -> tuple[int, Dict[str, Any]]:
|
|
# This policy calculates the "raw" desired replicas based on queue length.
|
|
# Ray Serve automatically applies scaling factors, delays, and bounds from
|
|
# the deployment's autoscaling_config on top of this decision.
|
|
|
|
queue_length = ctx.total_num_requests
|
|
|
|
if queue_length > 50:
|
|
return 10, {}
|
|
elif queue_length > 10:
|
|
return 5, {}
|
|
else:
|
|
return 0, {}
|
|
# __end_apply_autoscaling_config_example__
|
|
|
|
# __begin_apply_autoscaling_config_usage__
|
|
from ray import serve
|
|
from ray.serve.config import AutoscalingConfig, AutoscalingPolicy
|
|
|
|
@serve.deployment(
|
|
autoscaling_config=AutoscalingConfig(
|
|
min_replicas=1,
|
|
max_replicas=10,
|
|
metrics_interval_s=0.1,
|
|
upscale_delay_s=1.0,
|
|
downscale_delay_s=1.0,
|
|
policy=AutoscalingPolicy(
|
|
policy_function=queue_length_based_autoscaling_policy
|
|
)
|
|
),
|
|
max_ongoing_requests=5,
|
|
)
|
|
class MyDeployment:
|
|
def __call__(self) -> str:
|
|
return "Hello, world!"
|
|
|
|
app = MyDeployment.bind()
|
|
# __end_apply_autoscaling_config_usage__ |