-
-
Notifications
You must be signed in to change notification settings - Fork 10
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
start_connection raises an IndexError when socket creation fails with uvloop #93
Comments
This code comes from stdlib. Is it possible that creating a socket can raise something other than OSError? |
It happened in one of my projects and if I'm not wrong it was a |
Can you post steps to recreate the issue without manually patching the exception, and post the exception trace you received in production? |
I don't know how to reproduce the issue in the real world tbh. It happened couple of times and I couldn't find any specific situation which could result the issue.
|
That traceback seems weirdly formatted, but I'm assuming the original exception comes from anyio, so maybe this is really a bug in anyio which should be raising some kind of OSError...? |
To give some more context, the exceptions for [
RuntimeError('File descriptor 27 is used by transport <TCPTransport closed=False reading=True 0x2a4ce36edd00>'),
RuntimeError('File descriptor 27 is used by transport <TCPTransport closed=False reading=True 0x2a4ce36edd00>')
] |
Experienced same problems too. We use aiohttp and after 3.10 sometimes ClientSession stops working at all, all requests end with timeout error. Before this, we see such errors in our logs: Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/cian_http/client.py", line 112, in request
async with self._get_session().request(
File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 1355, in __aenter__
self._resp: _RetType = await self._coro
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 659, in _request
conn = await self._connector.connect(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 557, in connect
proto = await self._create_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1002, in _create_connection
_, proto = await self._create_direct_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1305, in _create_direct_connection
transp, proto = await self._wrap_create_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1061, in _wrap_create_connection
sock = await aiohappyeyeballs.start_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohappyeyeballs/impl.py", line 81, in start_connection
sock = await _connect_sock(
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohappyeyeballs/impl.py", line 166, in _connect_sock
await loop.sock_connect(sock, address)
File "uvloop/loop.pyx", line 2613, in sock_connect
File "uvloop/loop.pyx", line 1087, in uvloop.loop.Loop._sock_connect
File "uvloop/loop.pyx", line 835, in uvloop.loop.Loop._add_writer
File "uvloop/loop.pyx", line 762, in uvloop.loop.Loop._ensure_fd_no_transport
RuntimeError: File descriptor 59 is used by transport <TCPTransport closed=False reading=True 0x55a6ec5c9460> and Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/cian_http/client.py", line 112, in request
async with self._get_session().request(
File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 1353, in __aenter__
self._resp = await self._coro
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/client.py", line 657, in _request
conn = await self._connector.connect(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 564, in connect
proto = await self._create_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 975, in _create_connection
_, proto = await self._create_direct_connection(req, traces, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1319, in _create_direct_connection
transp, proto = await self._wrap_create_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohttp/connector.py", line 1073, in _wrap_create_connection
sock = await aiohappyeyeballs.start_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/aiohappyeyeballs/impl.py", line 102, in start_connection
first_exception = all_exceptions[0]
~~~~~~~~~~~~~~^^^
IndexError: list index out of range Our configuration: aiohttp.ClientSession(
cookie_jar=aiohttp.DummyCookieJar(),
trace_configs[...],
connector=aiohttp.TCPConnector(limit=200),
) We don't know how to reproduce it, it happens rarely but impact is too high, so we had to pin aiohttp 3.9.5 without aiohappyeyeballs. |
Its possible there is a race in the asyncio staggered code where there are two winners like #100 |
We haven't seen any Is there any workaround? As far as I understand from my traceback, |
FWIW, this is the exact same code from I haven't found any cpython issues similar to this one |
Is everyone with the issue using uvloop? |
It looks like uvloop can raise |
But can asyncio raise RuntimeError in such situations too? |
If it can it means the code in |
Also please answer my question about using uvloop. I can't fix the issue if I don't know where to look for the source. |
For the original issue, yes we are using uvloop via uvicorn. |
Yes, we use uvloop as you can see from my traceback. |
selector_events can raise |
|
|
But it's still unclear why there is no such problem when aiohappyeyeballs isn't used. We've seen such tracebacks only with aiohttp 3.10. |
I expect |
We could start catching the That won't solve the problem with |
So it means it's not so difficult to make MRE? |
Tell me if I'm wrong, but it seems like sock_connect can't be cancelled at all. import asyncio
import socket
import time
import anyio
import uvloop
async def main():
while True:
sock = socket.socket()
with anyio.fail_after(0.05):
s = time.time()
await asyncio.get_running_loop().sock_connect(sock, ('54.159.225.35', 443)) # httpbin
print(time.time() - s)
uvloop.run(main()) I see not timeouts and get only this output:
UPD: I forgot about making socket non-blocking |
It looks like it tries to handle cancellation correctly here https://github.com/MagicStack/uvloop/blob/79d5dcdb0785d9319b8b04f99e7a733ed56e9b46/uvloop/loop.pyx#L3318 https://github.com/MagicStack/uvloop/blob/79d5dcdb0785d9319b8b04f99e7a733ed56e9b46/uvloop/loop.pyx#L3336 |
I tried this and no luck so maybe a race somewhere else that interacts poorly with `sock_connect import asyncio
import socket
import uvloop
import random
async def main():
loop = asyncio.get_running_loop()
while True:
tasks = []
for _ in range(10):
sock = socket.socket()
tasks.append(
asyncio.Task(
loop.sock_connect(sock, ("54.159.225.35", 443)),
eager_start=True,
loop=loop,
)
)
sleep = random.randint(0, 1)
if sleep:
await asyncio.sleep(sleep)
for task in tasks:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
uvloop.run(main()) |
I am not a uvloop expert by any means. We need someone who has experience with uvloop to figure this one out. |
I will also clarify, we use aiohttp on server side with |
cancellation is just a guess where the problem is as thats so hard to get right it usually ends up being where the problem is. Its also possible that the new |
I've tried a few more things but haven't had any luck getting uvloop to throw RuntimeError. Hopefully someone will come up with a reproducer so we can open an issue with uvloop |
Can you still reproduce with 2.4.3? |
I haven't bumped the version to 2.4.3 yet. Let me do that and I'll let you know if it happened again. Thanks for resolving the issue. |
@bdraco I can confirm that the |
I've experienced the
|
I suspect that without aiohappyeyeballs, the socket creation is done inside |
I made a small change to
So basically, the call to |
fixed via #114 |
Describe the bug
If creating the socket fails with an
Exception
other than anOSError
, thestart_connection
method will raise anIndexError
when it's trying to get the first exception.To Reproduce
Logs
Python version
aiohttp version
OS
macOs
Additional context
No additional context.
The text was updated successfully, but these errors were encountered: