-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Memory leak when doing https request #1029
Comments
Don't recreate ClientSession for every request -- it's pretty expensive. Use the single session for the whole program. |
yeah looks like some kind of leak is there even with a single session. Here's the updated script #!/usr/bin/env python3
import aiohttp
import asyncio
import gc
import random
async def test(url, session, sleep=10):
await asyncio.sleep(random.randint(0, sleep))
async with session.get(url) as resp:
await resp.text()
await asyncio.sleep(sleep)
async def test_with_session(url, sleep):
with aiohttp.ClientSession() as session:
await test(url, session, sleep)
async def main_multisession(url, concurrency, sleep):
tasks = [test_with_session(url, sleep) for i in range(concurrency)]
await asyncio.wait(tasks)
async def main_singlesession(url, concurrency, sleep):
with aiohttp.ClientSession() as session:
tasks = [test(url, session, sleep) for i in range(concurrency)]
await asyncio.wait(tasks)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--concurrency', type=int, default=10)
parser.add_argument('--loops', type=int, default=10)
parser.add_argument('--url', required=True)
parser.add_argument('--sleep', type=int, default=1)
parser.add_argument('--single-session', action='store_true')
args = parser.parse_args()
gc.disable()
loop = asyncio.get_event_loop()
print('calling url {}'.format(args.url))
fun = main_singlesession if args.single_session else main_multisession
for i in range(args.loops):
loop.run_until_complete(fun(args.url, args.concurrency, args.sleep))
print(gc.get_count()) and here's the results
|
updated leak detection script #!/usr/bin/env python3
import aiohttp
import asyncio
import gc
import random
async def test(url, session, sleep=10):
await asyncio.sleep(random.randint(0, sleep))
async with session.get(url) as resp:
await resp.text()
await asyncio.sleep(sleep)
async def test_with_session(url, sleep):
with aiohttp.ClientSession() as session:
await test(url, session, sleep)
async def do_multi(url, concurrency, sleep):
tasks = [test_with_session(url, sleep) for i in range(concurrency)]
await asyncio.wait(tasks)
async def wait_and_cancel(tasks):
tasks = [asyncio.ensure_future(task) for task in tasks]
try:
done, pending = await asyncio.wait(tasks)
except asyncio.CancelledError:
for task in tasks:
task.cancel()
async def do_single(url, concurrency, sleep):
with aiohttp.ClientSession() as session:
tasks = [test(url, session, sleep) for i in range(concurrency)]
await wait_and_cancel(tasks)
async def do_only_session(url, concurrency, sleep):
with aiohttp.ClientSession():
tasks = [asyncio.sleep(sleep) for i in range(concurrency)]
await wait_and_cancel(tasks)
async def do_only_get(url, concurrency, sleep):
tasks = [aiohttp.get(url) for i in range(concurrency)]
await wait_and_cancel(tasks)
async def do_requests_request(url, session):
resp = session.get(url)
resp.content
async def do_requests(url, concurrency, sleep):
import requests
with requests.Session() as session:
tasks = [do_requests_request(url, session) for i in range(concurrency)]
await wait_and_cancel(tasks)
async def do_empty(url, concurrency, sleep):
tasks = [asyncio.sleep(sleep) for i in range(concurrency)]
await wait_and_cancel(tasks)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--concurrency', type=int, default=10)
parser.add_argument('--loops', type=int, default=10)
parser.add_argument('--url', required=True)
parser.add_argument('--sleep', type=int, default=1)
parser.add_argument(
'--target',
choices=[
fun_name[3:] for fun_name in globals().keys()
if fun_name.startswith('do_')
],
required=True)
parser.add_argument('--single-session', action='store_true')
args = parser.parse_args()
gc.disable()
loop = asyncio.get_event_loop()
print('calling url {}'.format(args.url))
fun = globals()['do_{}'.format(args.target)]
prev_count = gc.get_count()
leaks = []
for i in range(args.loops):
task = asyncio.ensure_future(
fun(args.url, args.concurrency, args.sleep)
)
try:
loop.run_until_complete(task)
except KeyboardInterrupt:
task.cancel()
loop.run_forever()
task.exception()
raise
next_count = gc.get_count()
if prev_count and prev_count < next_count:
print('collectable found at iteration {i}: {next_count}'
.format_map(locals()))
leaks.append((i, next_count))
prev_count = next_count
print('found {} leaks in iterations {!r} (total iterations: {})'.format(
len(leaks),
[leak[0] for leak in leaks],
args.loops)) as you can see, vanilla asyncio is circular-reference free after starutp
aiohttp is not
but also
|
@asvetlov |
@mpaolini |
I think I'm experiencing a similar problem. I'm new to this kind of memory issues, but I've been doing a few novice tests using mem_top, and I've found that these keep increasing until kernel kills my python script for out of memory:
I'm not sure if this is related to aiohttp, or even to this specific issue you guys are reporting, so I apologize if this is off topic. I will try to create a small script to reproduce this problem and post it tomorrow. |
@zbagz If you don't need cookie processing at all you may implement your own no-op jar and pass it into session. |
Thanks for your help @asvetlov. I followed your advice as I don't need any cookie processing at all. This solved my problem:
|
@zbagz if you want to contribute your |
Fixed by #1162 |
I experienced memory leaks issues and narrowed it down to when I do https requests.
Here is the test code:
When I do https request I will see the memory goes up to 2.2g after 60 seconds. But when I do http request I will see the memory goes up to only 60m after 60 seconds.
The text was updated successfully, but these errors were encountered: