Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Serve] Expose serve request id from http request/resp #35789

Merged
merged 26 commits into from
Jun 29, 2023

Conversation

sihanwang41
Copy link
Contributor

@sihanwang41 sihanwang41 commented May 25, 2023

Why are these changes needed?

User can inject request id:

@serve.deployment
class Model:
    def __call__(self) -> int:
        return 1

serve.run(Model.bind())
resp = requests.get("http://localhost:8000", headers={"RAY_SERVE_REQUEST_ID": "123-234"})

Related issue number

Closes: #35785

Checks

  • I've signed off every commit(by using the -s flag, i.e., git commit -s) in this PR.
  • I've run scripts/format.sh to lint the changes in this PR.
  • I've included any doc changes needed for https://docs.ray.io/en/master/.
    • I've added any new APIs to the API Reference. For example, if I added a
      method in Tune, I've added it in doc/source/tune/api/ under the
      corresponding .rst file.
  • I've made sure the tests are passing. Note that there might be a few flaky tests, see the recent failures at https://flakey-tests.ray.io/
  • Testing Strategy
    • Unit tests
    • Release tests
    • This PR is not tested :(

@edoakes
Copy link
Contributor

edoakes commented May 25, 2023

cc @shayanhoshyari

@@ -191,3 +191,6 @@ class ServeHandleType(str, Enum):

# Serve HTTP request header key for routing requests.
SERVE_MULTIPLEXED_MODEL_ID = "serve_multiplexed_model_id"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, should this one be all-caps as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think headers are typically case-insensitive, but might as well be consistent...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, good point, I can push a fix in the master (since headers are case insensitive, I will not do cherry-pick in this case)

python/ray/serve/_private/constants.py Outdated Show resolved Hide resolved
@@ -164,6 +165,12 @@ async def _send_request_to_handle(handle, scope, receive, send) -> str:
return "500"

if isinstance(result, (starlette.responses.Response, RawASGIResponse)):
if isinstance(result, starlette.responses.Response):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm this makes me realize that my PR will complicate this a bit...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will let your pr checked in and then revisit here.

Comment on lines 76 to 79
# Set request id.
self.raw_headers.append(
[
RAY_SERVE_REQUEST_ID,
ray.serve.context._serve_request_context.get().request_id,
]
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to do this only in the HTTP proxy instead of modifying the three separate codepaths?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will rebase after your pr landed. and restructure the code. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you 🙏

Signed-off-by: sihanwang41 <sihanwang41@gmail.com>
Signed-off-by: sihanwang41 <sihanwang41@gmail.com>
Signed-off-by: sihanwang41 <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
@sihanwang41
Copy link
Contributor Author

@edoakes i put the request id into the constructor of the Response & RawASGIResponse.

  • I feel it is more intuitive when constructing the response, we inject the request_id (a little bit weird we change response headers in some other places).
  • Minimal change, so that I don't need inject request_id manually for other code path (e.g. handling different exceptions, we createResponse object to send back to sender).
    WDYT?

@edoakes
Copy link
Contributor

edoakes commented Jun 9, 2023

@sihanwang41 this approach won't work for wrapped ASGI apps (like fastapi integration) because we just stream the ASGI response messages directly there

Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Copy link
Contributor

@edoakes edoakes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks reasonable; as discussed offline let's just make it a middleware, should be cleaner

Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
@@ -207,6 +207,10 @@ class ServeHandleType(str, Enum):
os.environ.get("RAY_SERVE_ENABLE_EXPERIMENTAL_STREAMING", "0") == "1"
)

# Request ID used for logging. Can be provided as a request
# header and will always be returned as a response header.
RAY_SERVE_REQUEST_ID = "RAY_SERVE_REQUEST_ID"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RAY_SERVE_REQUEST_ID = "RAY_SERVE_REQUEST_ID"
RAY_SERVE_REQUEST_ID_HEADER = "RAY_SERVE_REQUEST_ID"

python/ray/serve/_private/http_proxy.py Show resolved Hide resolved
async def send_with_request_id(message):
if message["type"] == "http.response.start":
request_id = ray.serve.context._serve_request_context.get().request_id
headers = MutableHeaders(scope=message)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this update the underlying scope passed in?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure i understand the question, I assume all headers information for http.response.start is under the message directly.
The MutableHeaders will only extract headers from the message https://github.com/encode/starlette/blob/2168e47052239da5df35d5353bb986f760c51cef/starlette/datastructures.py#L534

Copy link
Contributor

@edoakes edoakes Jun 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I'm just confused because you're never modifying the message here, does headers.append(RAY_SERVE_REQUEST_ID, request_id) do it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, yes. the headers object hold the message header Dict ref. and whenever you update the headers, it will update the message. It is same as you update the message header directly, but this is more standard instead of updating raw dict by yourself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perfect thanks for explaining

python/ray/serve/_private/http_proxy.py Outdated Show resolved Hide resolved
python/ray/serve/tests/test_http_headers.py Outdated Show resolved Hide resolved
python/ray/serve/tests/test_http_headers.py Show resolved Hide resolved
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Signed-off-by: Sihan Wang <sihanwang41@gmail.com>
Copy link
Contributor

@edoakes edoakes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the tests! :)

@sihanwang41
Copy link
Contributor Author

ping for merge @edoakes , test failure is unrelated.

@edoakes edoakes merged commit 8321989 into ray-project:master Jun 29, 2023
arvind-chandra pushed a commit to lmco/ray that referenced this pull request Aug 31, 2023
…5789)

User can inject request id:
```
@serve.deployment
class Model:
    def __call__(self) -> int:
        return 1

serve.run(Model.bind())
resp = requests.get("http://localhost:8000", headers={"RAY_SERVE_REQUEST_ID": "123-234"})
```

Signed-off-by: e428265 <arvind.chandramouli@lmco.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[serve] Allow passing request_id in via header and return request_id via header
5 participants