-
Notifications
You must be signed in to change notification settings - Fork 550
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
event loop loop forever: make lots of sock_connec at the same time #378
Comments
Hey, thanks for the report! But please provide a reproducible sample code. I've tried several batches of 10k concurrent connections on the same server but cannot reproduce the issue. |
finally got some time server.py import asyncio
import socket
import uvloop
uvloop.install()
count = 0
backlog = 1024
async def connection_made(sock, loop):
global count
count += 1
print(f'{loop.time()}: {count}, {sock} connected')
await asyncio.sleep(1000)
def accept(sock):
loop = asyncio.get_running_loop()
for _ in range(backlog):
try:
conn, addr = sock.accept()
except (BlockingIOError, InterruptedError, ConnectionAbortedError):
return
conn.setblocking(False)
asyncio.create_task(connection_made(conn, loop))
async def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
sock.bind(('localhost', 56789))
sock.listen(backlog)
loop = asyncio.get_running_loop()
loop.add_reader(sock.fileno(), accept, sock)
await asyncio.sleep(1000)
if __name__ == '__main__':
asyncio.run(main()) client.py import asyncio
import socket
import sys
import uvloop
uvloop.install()
count = 0
async def connect(addr, loop):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
await loop.sock_connect(sock, addr)
return sock
async def wrapper(loop):
while 1:
try:
sock = await connect(('localhost', 56789), loop)
except BlockingIOError:
await asyncio.sleep(0)
else:
break
global count
count += 1
print(f'{loop.time()}: {count}, {sock} connected')
await asyncio.sleep(1000)
async def main(concurrency):
tasks = []
loop = asyncio.get_running_loop()
for x in range(concurrency):
tasks.append(asyncio.create_task(wrapper(loop)))
await asyncio.gather(*tasks)
if __name__ == '__main__':
# python client.py 1000
asyncio.run(main(int(sys.argv[1]))) concurrency = 1000, works well
concurrency = 5000, oops
using vanilla asyncio, works well
some kernel config |
CPython fixed the same issue in python/cpython#10419. Seems like under pressure, more write callbacks may happen before _remove_writer() is called, so we should check for done(). Fixes MagicStack#378
CPython fixed the same issue in python/cpython#10419. Seems like under pressure, more write callbacks may happen before _remove_writer() is called, so we should check for done(). Fixes MagicStack#378
Use _SyncSocketWriterFuture for cancellation, so that we could remove the writer directly in the callback. Fixes MagicStack#378 Closes MagicStack#389
Use _SyncSocketWriterFuture for cancellation, so that we could remove the writer directly in the callback. Fixes MagicStack#378 Closes MagicStack#389
PYTHONASYNCIODEBUG
in env?: yesserver side: accept connections and do nothing, just close it after a few seconds, simulating heartbeat timeout
client side: connect to server side a lots in the same time, e.g. -c 5000
it succeeded the first time, failed the second time
i cannot even terminate it, it loop forever, all i can do is to
kill -9
traceback logs:
after some checks, seems like the failed sock did not remove the writer correctly(or not in time?)
fut's done_callback is scheduled after the first time loop._sock_connect_cb invoked
at that time related writer is not removed and the socket fd is reused under heavy pressure?
then the same callback is invoked but for different fd?
checked asyncio, it check fut.done(), and uvloop check fut.cancelled()
The text was updated successfully, but these errors were encountered: