Repository: ray-project/ray
File: python/ray/serve/tests/unit/test_deployment_scheduler.py (Python)

Code before:
            upscales={
                d_id: [
                    ReplicaSchedulingRequest(
                        replica_id=ReplicaID(unique_id="r0", deployment_id=d_id),
                        actor_def=MockActorClass(),
                        actor_resources={"CPU": 2, "customA": 0.1},
                        actor_options={"name": "random"},
                        actor_init_args=(),
                        on_scheduled=on_scheduled,
                    )
                ]
            },
            downscales={},
        )

    def test_actor_creation_failure_does_not_decrement_resources(self):
        """When actor creation fails for a replica, available resources
        should not be decremented so subsequent replicas in the same
        scheduling batch can still use that node.
        """

        d_id = DeploymentID(name="deployment1")
        node_id = NodeID.from_random().hex()

        cluster_node_info_cache = MockClusterNodeInfoCache()
        # Node has exactly 2 CPUs — enough for two 1-CPU replicas.
        cluster_node_info_cache.add_node(node_id, {"CPU": 2})

        scheduler = default_impl.create_deployment_scheduler(
            cluster_node_info_cache,
            head_node_id_override="fake-head-node-id",
            create_placement_group_fn_override=None,
        )
        scheduler.on_deployment_created(d_id, SpreadDeploymentSchedulingPolicy())
        scheduler.on_deployment_deployed(
            d_id,
            ReplicaConfig.create(dummy, ray_actor_options={"num_cpus": 1}),
        )

        # Create a mock actor class whose .options().remote() raises on the
        # first call (simulating actor creation failure) but succeeds after.
        call_count = 0

        class FailOnceMockActorClass(MockActorClass):
            def remote(self, *args):
                nonlocal call_count
                call_count += 1
                if call_count == 1:
                    raise RuntimeError("Simulated actor creation failure")
                return super().remote(*args)


Review comment (bug):
Should we reduce this to 1 CPU? That way the second replica will only schedule if the first, failed replica did not decrement the resources.

Code after:
            upscales={
                d_id: [
                    ReplicaSchedulingRequest(
                        replica_id=ReplicaID(unique_id="r0", deployment_id=d_id),
                        actor_def=MockActorClass(),
                        actor_resources={"CPU": 2, "customA": 0.1},
                        actor_options={"name": "random"},
                        actor_init_args=(),
                        on_scheduled=on_scheduled,
                    )
                ]
            },
            downscales={},
        )

    def test_actor_creation_failure_does_not_decrement_resources(self):
        """When actor creation fails for a replica, available resources
        should not be decremented so subsequent replicas in the same
        scheduling batch can still use that node.
        """

        d_id = DeploymentID(name="deployment1")
        node_id = NodeID.from_random().hex()

        cluster_node_info_cache = MockClusterNodeInfoCache()
        # Node has exactly 1 CPU — enough for one 1-CPU replica.
        cluster_node_info_cache.add_node(node_id, {"CPU": 1})

        scheduler = default_impl.create_deployment_scheduler(
            cluster_node_info_cache,
            head_node_id_override="fake-head-node-id",
            create_placement_group_fn_override=None,
        )
        scheduler.on_deployment_created(d_id, SpreadDeploymentSchedulingPolicy())
        scheduler.on_deployment_deployed(
            d_id,
            ReplicaConfig.create(dummy, ray_actor_options={"num_cpus": 1}),
        )

        # Create a mock actor class whose .options().remote() raises on the
        # first call (simulating actor creation failure) but succeeds after.
        call_count = 0

        class FailOnceMockActorClass(MockActorClass):
            def remote(self, *args):
                nonlocal call_count
                call_count += 1
                if call_count == 1:
                    raise RuntimeError("Simulated actor creation failure")
                return super().remote(*args)