You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Handle asynchronous cancellations for HTTPConnection instances #785
The gist of the race condition is that when a web request is cancelled within a specific time window, it will degrade the state of the connection pool, leaving it unable to issue further requests. It also seems to create a deadlock.
It can be reproduced with the following code:
importtrioimporthttpcoreTIMEOUT= {"read": 5, "write": 5, "connect": 5, "pool": 5}
asyncdefsend_request(client, seen_response):
response=awaitclient.request("GET", "http://127.0.0.1:8000", extensions={"timeout": TIMEOUT})
# Print the first response that we see and set the "seen_response" flag.ifnotseen_response.is_set():
print(response)
seen_response.set()
asyncdefmain():
asyncwithhttpcore.AsyncConnectionPool() asclient:
while1:
asyncwithtrio.open_nursery() asnursery:
# This event is used to signal the first response we recieve.seen_response=trio.Event()
# Kick off one hundred HTTP requests.foridxinrange(100):
nursery.start_soon(send_request, client, seen_response)
# As soon as we see a response we can cancel out of the nursery,# rather than allowing all tasks to complete.awaitseen_response.wait()
nursery.cancel_scope.cancel()
trio.run(main)
This should produce a httpcore.PoolTimeout error on the second or third iteration, after which the loop enters into a deadlock.
The issue appears to be HTTPConnections, which do not have the._connection attribute.
Example
importtrioimporthttpcoreTIMEOUT= {"read": 5, "write": 5, "connect": 5, "pool": 5}
asyncdefsend_request(client, seen_response):
response=awaitclient.request("GET", "http://127.0.0.1:8000", extensions={"timeout": TIMEOUT})
# Print the first response that we see and set the "seen_response" flag.ifnotseen_response.is_set():
seen_response.set()
asyncdefmain():
asyncwithhttpcore.AsyncConnectionPool() asclient:
while1:
try:
asyncwithtrio.open_nursery() asnursery:
# This event is used to signal the first response we recieve.seen_response=trio.Event()
# Kick off one hundred HTTP requests.foridxinrange(100):
nursery.start_soon(send_request, client, seen_response)
# As soon as we see a response we can cancel out of the nursery,# rather than allowing all tasks to complete.awaitseen_response.wait()
nursery.cancel_scope.cancel()
exceptBaseException:
print(client.connections[0].has_expired())
print(client.connections[0].is_available())
returntrio.run(main)
OUTPUT
False
False
This is the section of code where connection pools determine whether or not the connection can be used for the request.
It appears that these types of connections are not releasing the connection pool. Connections that have not failed but are also not attempting to connect are not handled correctly.
How to make these kinds of connections. ?
This problem appears to occur if the newly created connection is cancelled before entering this try block.
changed the title [-]Race Condition with Async Task Cancellation Causes Deadlock[/-][+]Handle asynchronous cancellations for HTTPConnection instances[/+]on Sep 14, 2023
Activity
karpetrosyan commentedon Sep 1, 2023
The issue appears to be HTTPConnections, which do not have the._connection attribute.
Example
OUTPUT
This is the section of code where connection pools determine whether or not the connection can be used for the request.
httpcore/httpcore/_async/connection_pool.py
Lines 161 to 167 in b649bb0
It appears that these types of connections are not releasing the connection pool.
Connections that have not failed but are also not attempting to connect are not handled correctly.
How to make these kinds of connections. ?
This problem appears to occur if the newly created connection is cancelled before entering this try block.
httpcore/httpcore/_async/connection.py
Lines 75 to 99 in b649bb0
I'm not sure this is what we're looking for; what do you think, @tomchristie ?
karpetrosyan commentedon Sep 1, 2023
The simplified version of the reproducible code for this issue.
karpetrosyan commentedon Sep 11, 2023
@tomchristie Can you give me some advice here? Should I open a PR that resolves such cases?
[-]Race Condition with Async Task Cancellation Causes Deadlock[/-][+]Handle asynchronous cancellations for HTTPConnection instances[/+]