Files
ray-project--ray/python/ray/serve/tests/unit/test_deployment_version.py
T
2026-07-13 13:17:40 +08:00

413 lines
13 KiB
Python

import pytest
from ray.serve._private.config import DeploymentConfig
from ray.serve._private.deployment_state import DeploymentVersion
def test_validation():
# Code version must be a string.
with pytest.raises(TypeError):
DeploymentVersion(123, DeploymentConfig(), {})
def test_other_type_equality():
v = DeploymentVersion("1", DeploymentConfig(), {})
assert v is not None
assert v != "1"
assert v != None # noqa: E711
def test_code_version():
v1 = DeploymentVersion("1", DeploymentConfig(), {})
v2 = DeploymentVersion("1", DeploymentConfig(), {})
v3 = DeploymentVersion("2", DeploymentConfig(), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_deployment_config_basic():
v1 = DeploymentVersion("1", DeploymentConfig(user_config="1"), {})
v2 = DeploymentVersion("1", DeploymentConfig(user_config="1"), {})
v3 = DeploymentVersion("1", DeploymentConfig(user_config="2"), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_user_config_hashable():
v1 = DeploymentVersion("1", DeploymentConfig(user_config=("1", "2")), {})
v2 = DeploymentVersion("1", DeploymentConfig(user_config=("1", "2")), {})
v3 = DeploymentVersion("1", DeploymentConfig(user_config=("1", "3")), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_user_config_list():
v1 = DeploymentVersion("1", DeploymentConfig(user_config=["1", "2"]), {})
v2 = DeploymentVersion("1", DeploymentConfig(user_config=["1", "2"]), {})
v3 = DeploymentVersion("1", DeploymentConfig(user_config=["1", "3"]), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_user_config_dict_keys():
v1 = DeploymentVersion("1", DeploymentConfig(user_config={"1": "1"}), {})
v2 = DeploymentVersion("1", DeploymentConfig(user_config={"1": "1"}), {})
v3 = DeploymentVersion("1", DeploymentConfig(user_config={"2": "1"}), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_user_config_dict_vals():
v1 = DeploymentVersion("1", DeploymentConfig(user_config={"1": "1"}), {})
v2 = DeploymentVersion("1", DeploymentConfig(user_config={"1": "1"}), {})
v3 = DeploymentVersion("1", DeploymentConfig(user_config={"1": "2"}), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_user_config_nested():
v1 = DeploymentVersion(
"1", DeploymentConfig(user_config=[{"1": "2"}, {"1": "2"}]), {}
)
v2 = DeploymentVersion(
"1", DeploymentConfig(user_config=[{"1": "2"}, {"1": "2"}]), {}
)
v3 = DeploymentVersion(
"1", DeploymentConfig(user_config=[{"1": "2"}, {"1": "3"}]), {}
)
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_user_config_nested_in_hashable():
v1 = DeploymentVersion(
"1", DeploymentConfig(user_config=([{"1": "2"}, {"1": "2"}])), {}
)
v2 = DeploymentVersion(
"1", DeploymentConfig(user_config=([{"1": "2"}, {"1": "2"}])), {}
)
v3 = DeploymentVersion(
"1", DeploymentConfig(user_config=([{"1": "2"}, {"1": "3"}])), {}
)
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_num_replicas():
v1 = DeploymentVersion("1", DeploymentConfig(num_replicas=1), {})
v2 = DeploymentVersion("1", DeploymentConfig(num_replicas=2), {})
assert v1 == v2
assert hash(v1) == hash(v2)
def test_autoscaling_config():
v1 = DeploymentVersion(
"1",
DeploymentConfig(
autoscaling_config={"max_replicas": 2, "metrics_interval_s": 10}
),
{},
)
v2 = DeploymentVersion(
"1",
DeploymentConfig(
autoscaling_config={"max_replicas": 5, "metrics_interval_s": 10}
),
{},
)
v3 = DeploymentVersion(
"1",
DeploymentConfig(
autoscaling_config={"max_replicas": 2, "metrics_interval_s": 3}
),
{},
)
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_max_ongoing_requests():
v1 = DeploymentVersion("1", DeploymentConfig(max_ongoing_requests=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(max_ongoing_requests=5), {})
v3 = DeploymentVersion("1", DeploymentConfig(max_ongoing_requests=10), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert not v1.requires_actor_reconfigure(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
assert v1.requires_actor_reconfigure(v3)
def test_health_check_period_s():
v1 = DeploymentVersion("1", DeploymentConfig(health_check_period_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(health_check_period_s=5), {})
v3 = DeploymentVersion("1", DeploymentConfig(health_check_period_s=10), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_health_check_timeout_s():
v1 = DeploymentVersion("1", DeploymentConfig(health_check_timeout_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(health_check_timeout_s=5), {})
v3 = DeploymentVersion("1", DeploymentConfig(health_check_timeout_s=10), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_graceful_shutdown_timeout_s():
v1 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=5), {})
v3 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=10), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_graceful_shutdown_wait_loop_s():
v1 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_wait_loop_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_wait_loop_s=5), {})
v3 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_wait_loop_s=10), {})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_ray_actor_options():
v1 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v2 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v3 = DeploymentVersion("1", DeploymentConfig(), {"num_gpus": 0.1})
assert v1 == v2
assert hash(v1) == hash(v2)
assert v1 != v3
assert hash(v1) != hash(v3)
def test_max_replicas_per_node():
v1 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v2 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
max_replicas_per_node=1,
)
v3 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
max_replicas_per_node=1,
)
v4 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
max_replicas_per_node=2,
)
assert v1 != v2
assert hash(v1) != hash(v2)
assert v1.requires_actor_restart(v2)
assert v2 == v3
assert hash(v2) == hash(v3)
assert not v2.requires_actor_restart(v3)
assert v3 != v4
assert hash(v3) != hash(v4)
assert v3.requires_actor_restart(v4)
def test_placement_group_options():
v1 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v2 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
placement_group_bundles=[{"CPU": 0.1}],
)
v3 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
placement_group_bundles=[{"CPU": 0.1}],
)
v4 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
placement_group_bundles=[{"GPU": 0.1}],
)
assert v1 != v2
assert hash(v1) != hash(v2)
assert v1.requires_actor_restart(v2)
assert v2 == v3
assert hash(v2) == hash(v3)
assert not v2.requires_actor_restart(v3)
assert v3 != v4
assert hash(v3) != hash(v4)
assert v3.requires_actor_restart(v4)
v5 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
placement_group_bundles=[{"CPU": 0.1}],
placement_group_strategy="PACK",
)
v6 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
placement_group_bundles=[{"CPU": 0.1}],
placement_group_strategy="PACK",
)
v7 = DeploymentVersion(
"1",
DeploymentConfig(),
{"num_cpus": 0.1},
placement_group_bundles=[{"CPU": 0.1}],
placement_group_strategy="SPREAD",
)
assert v5 == v6
assert hash(v5) == hash(v6)
assert not v5.requires_actor_restart(v6)
assert v6 != v7
assert hash(v6) != hash(v7)
assert v6.requires_actor_restart(v7)
def test_requires_actor_restart():
# Code version different
v1 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v2 = DeploymentVersion("2", DeploymentConfig(), {"num_cpus": 0.1})
assert v1.requires_actor_restart(v2)
# Runtime env different
v1 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v2 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.2})
assert v1.requires_actor_restart(v2)
# Placement group bundles different
v1 = DeploymentVersion(
"1", DeploymentConfig(), {}, placement_group_bundles=[{"CPU": 0.1}]
)
v2 = DeploymentVersion(
"1", DeploymentConfig(), {}, placement_group_bundles=[{"CPU": 0.2}]
)
assert v1.requires_actor_restart(v2)
# Placement group strategy different
v1 = DeploymentVersion("1", DeploymentConfig(), {}, placement_group_strategy="PACK")
v2 = DeploymentVersion(
"1", DeploymentConfig(), {}, placement_group_strategy="SPREAD"
)
assert v1.requires_actor_restart(v2)
# Both code version and runtime env different
v1 = DeploymentVersion("1", DeploymentConfig(), {"num_cpus": 0.1})
v2 = DeploymentVersion("2", DeploymentConfig(), {"num_cpus": 0.2})
assert v1.requires_actor_restart(v2)
# Num replicas is different
v1 = DeploymentVersion("1", DeploymentConfig(num_replicas=1), {})
v2 = DeploymentVersion("1", DeploymentConfig(num_replicas=2), {})
assert not v1.requires_actor_restart(v2)
# Graceful shutdown timeout is different
v1 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=10), {})
assert not v1.requires_actor_restart(v2)
def test_requires_actor_reconfigure():
# Replicas need the updated user config to call the user-defined
# reconfigure method
v1 = DeploymentVersion("1", DeploymentConfig(user_config=1), {})
v2 = DeploymentVersion("1", DeploymentConfig(user_config=2), {})
assert v1.requires_actor_reconfigure(v2)
# Graceful shutdown loop requires actor reconfigure, since the
# replica needs the updated value to correctly execute graceful
# shutdown.
v1 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_wait_loop_s=1), {})
v2 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_wait_loop_s=2), {})
assert v1.requires_actor_reconfigure(v2)
# Graceful shutdown timeout shouldn't require actor reconfigure, as
# it's only used by the controller to decide when to force-kill a
# replica
v1 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(graceful_shutdown_timeout_s=10), {})
assert not v1.requires_actor_reconfigure(v2)
# Num replicas shouldn't require actor reconfigure, as it's only
# by the controller to decide when to start or stop replicas.
v1 = DeploymentVersion("1", DeploymentConfig(num_replicas=1), {})
v2 = DeploymentVersion("1", DeploymentConfig(num_replicas=2), {})
assert not v1.requires_actor_reconfigure(v2)
def test_requires_long_poll_broadcast():
# If max concurrent queries is updated, it needs to be broadcasted
# to all routers.
v1 = DeploymentVersion("1", DeploymentConfig(max_ongoing_requests=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(max_ongoing_requests=10), {})
assert v1.requires_long_poll_broadcast(v2)
# Something random like health check timeout doesn't require updating
# any info on routers.
v1 = DeploymentVersion("1", DeploymentConfig(health_check_timeout_s=5), {})
v2 = DeploymentVersion("1", DeploymentConfig(health_check_timeout_s=10), {})
assert not v1.requires_long_poll_broadcast(v2)
if __name__ == "__main__":
import sys
sys.exit(pytest.main(["-v", "-s", __file__]))