-
Notifications
You must be signed in to change notification settings - Fork 18
Replace ProcessPoolExecutor
with asyncio
page splitting
#92
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
Conversation
loop = asyncio.get_event_loop() | ||
responses = loop.run_until_complete(asyncio.gather(*prt_requests)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand how this hook flow works but I feel like you should start the event loop when you define the tasks and here just wait for the tasks to complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was considering that, but as we discussed that last week - in such scenario before_request
would also need to be async method which can't be done. Speakeasy requires hooks to be regular methods.
250c28f
to
c85a2d3
Compare
668c9c7
to
976b238
Compare
ProcessPoolExecutor
with asyncio
page splittingProcessPoolExecutor
with asyncio
page splitting
|
||
@pytest.mark.parametrize("split_pdf", [True, False]) | ||
@pytest.mark.parametrize("error_code", [500, 403]) | ||
def test_partition_handling_server_error(error_code, split_pdf, monkeypatch, doc_path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I managed to pass all tests except for this one. @badGarnet , if you're able to identify the source of the problem with a quick glance, let me know. I haven't managed to figure it out myself on time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now this test is passing, I just had to mock httpx response 😅
gen.yaml
Outdated
httpx: ">=0.27.0" | ||
aiolimiter: ">=1.1.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
two new dependencies: httpx is BSD-3-licensed, aiolimiter has MIT license
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no longer being used, resolving
@@ -135,43 +137,93 @@ def before_request( | |||
fallback_value=DEFAULT_CONCURRENCY_LEVEL, | |||
max_allowed=MAX_CONCURRENCY_LEVEL, | |||
) | |||
limiter = AsyncLimiter(max_rate=concurrency_level, time_period=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so by default it's 15 requests per second
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to set a hard limit to it? If it's per second then we could get over 15 pending requests with this approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to asyncio.Semaphore
, WDYT @mpolomdeepsense? https://medium.com/@kasperjuunge/semaphore-in-asyncio-1aaaf4038e30
response = requests.Response() | ||
response.status_code = status_code | ||
response._content = json.dumps(json_response).encode() # pylint: disable=W0212 | ||
response.headers["Content-Type"] = "application/json" | ||
return response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish speakeasy supported async client generation...
They rely on synchronous requests
library, forcing me to do the conversion here.
74613b8
to
473560e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
page_number = page_index + starting_page_number | ||
# Check if this page is the last one | ||
print(f"Page {page_number} of {all_pages_number}") | ||
if page_index == all_pages_number - 1: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ds-filipknefel you mentioned that there's a bug on the main branch. I'll incorporate your fix on Friday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocking for now as scale testing reveals some potential issues when using this code:
- locust base setup can't run the client with page splitting -> raises error that
There is no current event loop in thread 'Dummy-2'.
Investigating this more at the moment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue with locust was due to nesting of async event loops; not a problem for user experience
Update readme to highlight how to use the new page splitting logic safely.
aiohttp
/httpx
instead of synchronousrequests
How to verify that this PR works
Unit & Integration Tests
make install && make test
Manually
Where
test-client.py
has the following contents: