chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
import pytest
|
||||
|
||||
from ray.serve._private.common import (
|
||||
REPLICA_ID_FULL_ID_STR_PREFIX,
|
||||
DeploymentID,
|
||||
DeploymentStatus,
|
||||
DeploymentStatusInfo,
|
||||
DeploymentStatusInternalTrigger,
|
||||
DeploymentStatusTrigger,
|
||||
ReplicaID,
|
||||
RunningReplicaInfo,
|
||||
)
|
||||
from ray.serve._private.constants import SERVE_DEPLOYMENT_ACTOR_PREFIX
|
||||
from ray.serve._private.utils import get_deployment_actor_name, get_random_string
|
||||
from ray.serve.generated.serve_pb2 import (
|
||||
DeploymentStatusInfo as DeploymentStatusInfoProto,
|
||||
)
|
||||
|
||||
|
||||
def test_get_deployment_actor_name():
|
||||
"""Test deterministic actor name for deployment-scoped actors."""
|
||||
dep_id = DeploymentID(name="MyDeployment", app_name="my_app")
|
||||
assert get_deployment_actor_name(dep_id, "prefix_tree", "v1") == (
|
||||
f"{SERVE_DEPLOYMENT_ACTOR_PREFIX}my_app::MyDeployment::v1::prefix_tree"
|
||||
)
|
||||
|
||||
dep_id_default_app = DeploymentID(name="Other") # app_name="default"
|
||||
assert get_deployment_actor_name(dep_id_default_app, "x", "abc") == (
|
||||
f"{SERVE_DEPLOYMENT_ACTOR_PREFIX}default::Other::abc::x"
|
||||
)
|
||||
|
||||
|
||||
def test_replica_id_formatting():
|
||||
deployment = "DeploymentA"
|
||||
unique_id = get_random_string()
|
||||
|
||||
app = "my_app"
|
||||
replica_id = ReplicaID(
|
||||
unique_id, deployment_id=DeploymentID(name=deployment, app_name=app)
|
||||
)
|
||||
assert (
|
||||
replica_id.to_full_id_str()
|
||||
== f"{REPLICA_ID_FULL_ID_STR_PREFIX}{app}#{deployment}#{unique_id}"
|
||||
)
|
||||
assert (
|
||||
str(replica_id)
|
||||
== f"Replica(id='{unique_id}', deployment='{deployment}', app='{app}')"
|
||||
)
|
||||
|
||||
|
||||
def test_replica_id_from_str():
|
||||
unique_id = get_random_string()
|
||||
|
||||
# Test without app name.
|
||||
full_id_str = f"{REPLICA_ID_FULL_ID_STR_PREFIX}DeploymentA#{unique_id}"
|
||||
replica_id = ReplicaID.from_full_id_str(full_id_str)
|
||||
assert replica_id.unique_id == unique_id
|
||||
assert replica_id.deployment_id == DeploymentID(name="DeploymentA", app_name="")
|
||||
|
||||
# Test with app name.
|
||||
full_id_str = f"{REPLICA_ID_FULL_ID_STR_PREFIX}App1#DeploymentA#{unique_id}"
|
||||
replica_id = ReplicaID.from_full_id_str(full_id_str)
|
||||
assert replica_id.unique_id == unique_id
|
||||
assert replica_id.deployment_id == DeploymentID(name="DeploymentA", app_name="App1")
|
||||
|
||||
|
||||
def test_invalid_id_from_str():
|
||||
unique_id = get_random_string()
|
||||
|
||||
# Missing prefix.
|
||||
full_id_str = f"DeploymentA#{unique_id}"
|
||||
with pytest.raises(AssertionError):
|
||||
ReplicaID.from_full_id_str(full_id_str)
|
||||
|
||||
# Too many delimiters.
|
||||
full_id_str = f"{REPLICA_ID_FULL_ID_STR_PREFIX}DeploymentA###{unique_id}"
|
||||
with pytest.raises(ValueError):
|
||||
ReplicaID.from_full_id_str(full_id_str)
|
||||
|
||||
|
||||
def test_is_replica_id():
|
||||
unique_id = get_random_string()
|
||||
|
||||
assert not ReplicaID.is_full_id_str(f"DeploymentA##{unique_id}")
|
||||
assert not ReplicaID.is_full_id_str(f"DeploymentA#{unique_id}")
|
||||
assert ReplicaID.is_full_id_str(
|
||||
f"{REPLICA_ID_FULL_ID_STR_PREFIX}DeploymentA#{unique_id}"
|
||||
)
|
||||
assert ReplicaID.is_full_id_str(
|
||||
f"{REPLICA_ID_FULL_ID_STR_PREFIX}#App1#DeploymentA#{unique_id}"
|
||||
)
|
||||
|
||||
|
||||
class TestDeploymentStatusInfo:
|
||||
def test_name_required(self):
|
||||
with pytest.raises(TypeError):
|
||||
DeploymentStatusInfo(
|
||||
status=DeploymentStatus.HEALTHY,
|
||||
status_trigger=DeploymentStatusTrigger.CONFIG_UPDATE_STARTED,
|
||||
)
|
||||
|
||||
def test_deployment_status_required(self):
|
||||
with pytest.raises(TypeError):
|
||||
DeploymentStatusInfo(
|
||||
name="test_name",
|
||||
status_trigger=DeploymentStatusTrigger.CONFIG_UPDATE_STARTED,
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"status,status_trigger",
|
||||
list(zip(list(DeploymentStatus), list(DeploymentStatusTrigger))),
|
||||
)
|
||||
def test_proto(self, status, status_trigger):
|
||||
deployment_status_info = DeploymentStatusInfo(
|
||||
name="test_name",
|
||||
status=status,
|
||||
status_trigger=status_trigger,
|
||||
message="context about status",
|
||||
)
|
||||
serialized_proto = deployment_status_info.to_proto().SerializeToString()
|
||||
deserialized_proto = DeploymentStatusInfoProto.FromString(serialized_proto)
|
||||
reconstructed_info = DeploymentStatusInfo.from_proto(deserialized_proto)
|
||||
|
||||
assert deployment_status_info == reconstructed_info
|
||||
|
||||
def test_handle_transition_deployment_actor_failed_when_already_deploy_failed(
|
||||
self,
|
||||
):
|
||||
"""DEPLOYMENT_ACTOR_FAILED must be handled in DEPLOY_FAILED block.
|
||||
|
||||
When status is already DEPLOY_FAILED, repeated ticks call handle_transition
|
||||
with DEPLOYMENT_ACTOR_FAILED. Without handling, the trigger falls through
|
||||
and returns self (old message). With handling, returns updated copy.
|
||||
"""
|
||||
info = DeploymentStatusInfo(
|
||||
name="test",
|
||||
status=DeploymentStatus.DEPLOY_FAILED,
|
||||
status_trigger=DeploymentStatusTrigger.DEPLOYMENT_ACTOR_FAILED,
|
||||
message="original error message",
|
||||
)
|
||||
new_message = (
|
||||
"The deployment failed to start deployment actors 2 times in a row."
|
||||
)
|
||||
result = info.handle_transition(
|
||||
trigger=DeploymentStatusInternalTrigger.DEPLOYMENT_ACTOR_FAILED,
|
||||
message=new_message,
|
||||
)
|
||||
assert result is not None
|
||||
assert result.status == DeploymentStatus.DEPLOY_FAILED
|
||||
assert result.status_trigger == DeploymentStatusTrigger.DEPLOYMENT_ACTOR_FAILED
|
||||
assert result.message == new_message
|
||||
|
||||
|
||||
def test_running_replica_info():
|
||||
"""Test hash value of RunningReplicaInfo"""
|
||||
|
||||
replica_id = ReplicaID("asdf123", deployment_id=DeploymentID(name="my_deployment"))
|
||||
actor_name = replica_id.to_full_id_str()
|
||||
|
||||
# Test that replicas with same attributes have same hash
|
||||
replica1 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
)
|
||||
replica2 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
)
|
||||
# Test that cross-language setting affects hash
|
||||
replica3 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=True,
|
||||
)
|
||||
assert replica1._hash == replica2._hash
|
||||
assert replica3._hash != replica1._hash
|
||||
|
||||
# Test that backend_http_port affects hash so long-poll updates
|
||||
# propagate when the backend HTTP port changes.
|
||||
replica4 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
backend_http_port=8001,
|
||||
)
|
||||
replica5 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
backend_http_port=8002,
|
||||
)
|
||||
assert replica4._hash != replica1._hash
|
||||
assert replica4._hash != replica5._hash
|
||||
|
||||
# Test that network endpoint changes affect hash so wrappers and
|
||||
# long-poll consumers refresh when the replica moves or its gRPC
|
||||
# port changes.
|
||||
replica6 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip_a",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
port=9000,
|
||||
)
|
||||
replica7 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip_b",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
port=9000,
|
||||
)
|
||||
replica8 = RunningReplicaInfo(
|
||||
replica_id=replica_id,
|
||||
node_id="node_id",
|
||||
node_ip="node_ip_a",
|
||||
availability_zone="some-az",
|
||||
actor_name=actor_name,
|
||||
max_ongoing_requests=1,
|
||||
is_cross_language=False,
|
||||
port=9001,
|
||||
)
|
||||
assert replica6._hash != replica7._hash
|
||||
assert replica6._hash != replica8._hash
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
sys.exit(pytest.main(["-v", "-s", __file__]))
|
||||
Reference in New Issue
Block a user