413 lines
13 KiB
Python
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__]))
|