Пример #1
0
async def test_power_of_two_choices(serve_instance):
    q = PowerOfTwoPolicyQueueActor.remote()
    enqueue_futures = []

    # First, fill the queue for backend-1 with 3 requests
    await q.set_traffic.remote("svc", {"backend-1": 1.0})
    for _ in range(3):
        future = q.enqueue_request.remote(RequestMetadata("svc", None), "1")
        enqueue_futures.append(future)

    # Then, add a new backend, this backend should be filled next
    await q.set_traffic.remote("svc", {"backend-1": 0.5, "backend-2": 0.5})
    for _ in range(2):
        future = q.enqueue_request.remote(RequestMetadata("svc", None), "2")
        enqueue_futures.append(future)

    runner_1, runner_2 = (make_task_runner_mock() for _ in range(2))
    for _ in range(3):
        await q.dequeue_request.remote("backend-1", runner_1)
        await q.dequeue_request.remote("backend-2", runner_2)

    await asyncio.gather(*enqueue_futures)

    assert len(await runner_1.get_all_calls.remote()) == 3
    assert len(await runner_2.get_all_calls.remote()) == 2
Пример #2
0
async def test_alter_backend(serve_instance, task_runner_mock_actor):
    q = RandomPolicyQueueActor.remote()

    await q.set_traffic.remote("svc", {"backend-1": 1})
    await q.dequeue_request.remote("backend-1", task_runner_mock_actor)
    await q.enqueue_request.remote(RequestMetadata("svc", None), 1)
    got_work = await task_runner_mock_actor.get_recent_call.remote()
    assert got_work.request_args[0] == 1

    await q.set_traffic.remote("svc", {"backend-2": 1})
    await q.dequeue_request.remote("backend-2", task_runner_mock_actor)
    await q.enqueue_request.remote(RequestMetadata("svc", None), 2)
    got_work = await task_runner_mock_actor.get_recent_call.remote()
    assert got_work.request_args[0] == 2
Пример #3
0
async def test_single_prod_cons_queue(serve_instance, task_runner_mock_actor):
    q = RandomPolicyQueueActor.remote()
    q.link.remote("svc", "backend")
    q.dequeue_request.remote("backend", task_runner_mock_actor)

    # Make sure we get the request result back
    result = await q.enqueue_request.remote(RequestMetadata("svc", None), 1)
    assert result == "DONE"

    # Make sure it's the right request
    got_work = await task_runner_mock_actor.get_recent_call.remote()
    assert got_work.request_args[0] == 1
    assert got_work.request_kwargs == {}
Пример #4
0
    def _make_metadata(self):
        method_name = self.method_name
        if method_name is None:
            method_name = "__call__"

        # create RequestMetadata instance
        request_in_object = RequestMetadata(
            self.endpoint_name,
            TaskContext.Python,
            self.relative_slo_ms,
            self.absolute_slo_ms,
            call_method=method_name,
            tracing_metadata=self.tracing_metadata,
        )
        return request_in_object
Пример #5
0
async def test_split_traffic_random(serve_instance, task_runner_mock_actor):
    q = RandomPolicyQueueActor.remote()

    await q.set_traffic.remote("svc", {"backend-1": 0.5, "backend-2": 0.5})
    runner_1, runner_2 = [make_task_runner_mock() for _ in range(2)]
    for _ in range(20):
        await q.dequeue_request.remote("backend-1", runner_1)
        await q.dequeue_request.remote("backend-2", runner_2)

    # assume 50% split, the probability of all 20 requests goes to a
    # single queue is 0.5^20 ~ 1-6
    for _ in range(20):
        await q.enqueue_request.remote(RequestMetadata("svc", None), 1)

    got_work = [
        await runner.get_recent_call.remote()
        for runner in (runner_1, runner_2)
    ]
    assert [g.request_args[0] for g in got_work] == [1, 1]
Пример #6
0
async def test_fixed_packing(serve_instance):
    packing_num = 4
    q = FixedPackingPolicyQueueActor.remote(packing_num=packing_num)
    await q.set_traffic.remote("svc", {"backend-1": 0.5, "backend-2": 0.5})

    runner_1, runner_2 = (make_task_runner_mock() for _ in range(2))
    # both the backends will get equal number of queries
    # as it is packed round robin
    for _ in range(packing_num):
        await q.dequeue_request.remote("backend-1", runner_1)
        await q.dequeue_request.remote("backend-2", runner_2)

    for backend, runner in zip(["1", "2"], [runner_1, runner_2]):
        for _ in range(packing_num):
            input_value = "should-go-to-backend-{}".format(backend)
            await q.enqueue_request.remote(RequestMetadata("svc", None),
                                           input_value)
            all_calls = await runner.get_all_calls.remote()
            for call in all_calls:
                assert call.request_args[0] == input_value
Пример #7
0
async def test_round_robin(serve_instance, task_runner_mock_actor):
    q = RoundRobinPolicyQueueActor.remote()

    await q.set_traffic.remote("svc", {"backend-1": 0.5, "backend-2": 0.5})
    runner_1, runner_2 = [make_task_runner_mock() for _ in range(2)]

    # NOTE: this is the only difference between the
    # test_split_traffic_random and test_round_robin
    for _ in range(10):
        await q.dequeue_request.remote("backend-1", runner_1)
        await q.dequeue_request.remote("backend-2", runner_2)

    for _ in range(20):
        await q.enqueue_request.remote(RequestMetadata("svc", None), 1)

    got_work = [
        await runner.get_recent_call.remote()
        for runner in (runner_1, runner_2)
    ]
    assert [g.request_args[0] for g in got_work] == [1, 1]
Пример #8
0
async def test_slo(serve_instance, task_runner_mock_actor):
    q = RandomPolicyQueueActor.remote()
    await q.link.remote("svc", "backend")

    all_request_sent = []
    for i in range(10):
        slo_ms = 1000 - 100 * i
        all_request_sent.append(
            q.enqueue_request.remote(
                RequestMetadata("svc", None, relative_slo_ms=slo_ms), i))

    for i in range(10):
        await q.dequeue_request.remote("backend", task_runner_mock_actor)

    await asyncio.gather(*all_request_sent)

    i_should_be = 9
    all_calls = await task_runner_mock_actor.get_all_calls.remote()
    all_calls = all_calls[-10:]
    for call in all_calls:
        assert call.request_args[0] == i_should_be
        i_should_be -= 1
Пример #9
0
    async def __call__(self, scope, receive, send):
        # NOTE: This implements ASGI protocol specified in
        #       https://asgi.readthedocs.io/en/latest/specs/index.html

        if scope["type"] == "lifespan":
            await self.handle_lifespan_message(scope, receive, send)
            return

        error_sender = self._make_error_sender(scope, receive, send)

        assert (self.route_table
                is not None), "Route table must be set via set_route_table."
        assert scope["type"] == "http"
        current_path = scope["path"]
        if current_path == "/-/routes":
            await Response(self.route_table).send(scope, receive, send)
            return

        # TODO(simon): Use werkzeug route mapper to support variable path
        if current_path not in self.route_table:
            error_message = (
                "Path {} not found. "
                "Please ping http://.../-/routes for routing table"
            ).format(current_path)
            await error_sender(error_message, 404)
            return

        endpoint_name, methods_allowed = self.route_table[current_path]

        if scope["method"] not in methods_allowed:
            error_message = ("Methods {} not allowed. "
                             "Avaiable HTTP methods are {}.").format(
                                 scope["method"], methods_allowed)
            await error_sender(error_message, 405)
            return

        http_body_bytes = await self.receive_http_body(scope, receive, send)

        # get slo_ms before enqueuing the query
        try:
            relative_slo_ms, absolute_slo_ms = self._parse_latency_slo(scope)
        except ValueError as e:
            await error_sender(str(e), 400)
            return

        headers = {k.decode(): v.decode() for k, v in scope["headers"]}
        request_metadata = RequestMetadata(
            endpoint_name,
            TaskContext.Web,
            relative_slo_ms=relative_slo_ms,
            absolute_slo_ms=absolute_slo_ms,
            call_method=headers.get("X-SERVE-CALL-METHOD".lower(), "__call__"),
        )

        assert (
            self.route_table
            is not None), "Router handle must be set via set_router_handle."
        try:
            result = await self.router_handle.enqueue_request.remote(
                request_metadata, scope, http_body_bytes)
            await Response(result).send(scope, receive, send)
        except Exception as e:
            error_message = "Internal Error. Traceback: {}.".format(e)
            await error_sender(error_message, 500)