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

fix: loadbalance stream based on response #6122

Merged
merged 11 commits into from
Dec 6, 2023

Conversation

NarekA
Copy link
Contributor

@NarekA NarekA commented Dec 4, 2023

The current load balancer assumes all GET requests are streaming and all POST requests are not. This may not be true for user-added fast-api endpoints and in the past we have talked about using POST for streaming. (One of the benefits of this is the Swagger UI better documents the payload for POST endpoints).

It makes more sense to use the response content-type to determine when to stream.

Goals:

  • Allow Post streaming POST endpoints and non-streaming GET endpoints
  • check and update documentation. See guide and ask the team.

Copy link

codecov bot commented Dec 4, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (1c2a7c2) 73.86% compared to head (bc91179) 75.18%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6122      +/-   ##
==========================================
+ Coverage   73.86%   75.18%   +1.32%     
==========================================
  Files         152      152              
  Lines       14069    14062       -7     
==========================================
+ Hits        10392    10573     +181     
+ Misses       3677     3489     -188     
Flag Coverage Δ
jina 75.18% <100.00%> (+1.32%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@JoanFM
Copy link
Member

JoanFM commented Dec 4, 2023

Hey @NarekA,

Thanks for the contribution, can you check the failed tests and add a test showing what u are intending to fix?

@NarekA
Copy link
Contributor Author

NarekA commented Dec 4, 2023

@JoanFM I think the issue came about because I was using request.content_type instead of response.content_type. I think the latest commit should fix it.

@NarekA NarekA force-pushed the fix-loadbalancer-forwarding-method branch from e0a8118 to d287848 Compare December 4, 2023 21:11

async with _RequestContextManager(
session._request(request.method, target_url, **request_kwargs)
) as response:
Copy link

Choose a reason for hiding this comment

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

@NarekA why not directly use the request method from the session?, it's already wrapping the context manager for you.

    def request(
        self, method: str, url: StrOrURL, **kwargs: Any
    ) -> "_RequestContextManager":
        """Perform HTTP request."""
        return _RequestContextManager(self._request(method, url, **kwargs))
        ```

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Somehow I missed this, will fix.

Comment on lines 162 to 165
request_kwargs = {
'headers': request.headers,
'params': request.query,
}
Copy link

Choose a reason for hiding this comment

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

The original code did not forward the headers & query, which makes me wonder if it's intentional?

Copy link
Member

Choose a reason for hiding this comment

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

it was not intentional

return web.Response(
body=content,
status=response.status,
content_type=response.content_type,
Copy link

Choose a reason for hiding this comment

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

should the header be added here as well?

Copy link

Choose a reason for hiding this comment

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

Also by looking at the original GET implementation, I wonder if we just need to use StreamResponse all the way. Basically the load balancer just stream whatever it receives out.

Copy link
Member

Choose a reason for hiding this comment

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

yes this would be a good idea

@JoanFM
Copy link
Member

JoanFM commented Dec 5, 2023

Most of the errors on tests will be fixed by #6124

@NarekA NarekA force-pushed the fix-loadbalancer-forwarding-method branch from 02d3979 to 42f444a Compare December 5, 2023 21:16
return web.Response(
body=content,
status=response.status,
content_type=response.content_type,
Copy link

Choose a reason for hiding this comment

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

Also by looking at the original GET implementation, I wonder if we just need to use StreamResponse all the way. Basically the load balancer just stream whatever it receives out.

Comment on lines -164 to -167
if payload:
request_kwargs['json'] = payload
except Exception:
self.logger.debug('No JSON payload found in request')
Copy link

Choose a reason for hiding this comment

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

Looking at the original implementation, i have this idea:
it looks like the original logic was only to write a debug log which is not useful at all for production application. Can we just act as a pure proxy here for performance consideration? Something like:

async with session.request(request.method, data=request.iter_any(), **request_kwargs) as response:
    ....

@NarekA @JoanFM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For some reason, if I try to pass the content in any other way besides the json field, I get an error here. I've tried everything at this point, if you can get this to work, I am interested.

Copy link

Choose a reason for hiding this comment

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

what error do you see?

@NarekA
Copy link
Contributor Author

NarekA commented Dec 6, 2023

@JoanFM I'm finding these tests to be prohibitively difficult to debug. Any idea what is going wrong? I need a better way of replicating them locally.

self.logger.debug('No JSON payload found in request')

async with session.request(
request.method, url=target_url, **request_kwargs
Copy link

Choose a reason for hiding this comment

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

You might consider adding auto_decompress=False here. I've seen issue when upstream compresses the response, without this flag, together with the upstream header, there will be an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, just fixed this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like this fixed everything!

@JoanFM JoanFM merged commit 129b7b3 into jina-ai:master Dec 6, 2023
131 checks passed
@NarekA NarekA deleted the fix-loadbalancer-forwarding-method branch December 6, 2023 19:03
@JoanFM
Copy link
Member

JoanFM commented Dec 6, 2023

Thanks for this great contribution!

@JoanFM JoanFM mentioned this pull request Dec 14, 2023
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.

3 participants