Aiohttp: Timeout exception when reading a response body

Created on 29 Aug 2019  路  6Comments  路  Source: aio-libs/aiohttp

Long story short

Hello
I have a strange issue with aiohttp.client, sometimes when I try to read a response body, it crashes by timeout.

My function:

async def test(self, value: str) -> dict:
       timeout = aiohttp.ClientTimeout(total=30)
       async with aiohttp.ClientSession(timeout=self.timeout) as session:
            resp = await session.get(
                urljoin(url, "api/"), params={"value": value}
            )
       if resp.status == 200:
            response = await resp.json()
            return response
       else:
            err_message = await resp.text()
            raise MyException(resp.status, err_message)

Traceback:

---------------------------------------------------------------------------
TimeoutError                              Traceback (most recent call last)
<ipython-input-95-032af7fa0ee9> in <module>
----> 1 resp = asyncio.run(st.query("select * from sources * where source matches \"groennieuws.nl\" and author matches \"Wesley Van Der Linde\";"))

~/.pyenv/versions/3.7.3/lib/python3.7/asyncio/runners.py in run(main, debug)
     41         events.set_event_loop(loop)
     42         loop.set_debug(debug)
---> 43         return loop.run_until_complete(main)
     44     finally:
     45         try:

~/.pyenv/versions/3.7.3/lib/python3.7/asyncio/base_events.py in run_until_complete(self, future)
    582             raise RuntimeError('Event loop stopped before Future completed.')
    583 
--> 584         return future.result()
    585 
    586     def stop(self):

~/scripts/consumer.py in query(self, yql_query)
     77         if resp.status == 200:
     78             response = await resp.json()
---> 79             return response
     80         else:
     81             err_message = await resp.text()

~/.pyenv/versions/3.7.3/envs/test/lib/python3.7/site-packages/aiohttp/client_reqrep.py in json(self, encoding, loads, content_type)
   1015         """Read and decodes JSON response."""
   1016         if self._body is None:
-> 1017             await self.read()
   1018 
   1019         if content_type:

~/.pyenv/versions/3.7.3/envs/test/lib/python3.7/site-packages/aiohttp/client_reqrep.py in read(self)
    967         if self._body is None:
    968             try:
--> 969                 self._body = await self.content.read()
    970                 for trace in self._traces:
    971                     await trace.send_response_chunk_received(self._body)

~/.pyenv/versions/3.7.3/envs/test/lib/python3.7/site-packages/aiohttp/streams.py in read(self, n)
    357             blocks = []
    358             while True:
--> 359                 block = await self.readany()
    360                 if not block:
    361                     break

~/.pyenv/versions/3.7.3/envs/test/lib/python3.7/site-packages/aiohttp/streams.py in readany(self)
    379         # without feeding any data
    380         while not self._buffer and not self._eof:
--> 381             await self._wait('readany')
    382 
    383         return self._read_nowait(-1)

~/.pyenv/versions/3.7.3/envs/test/lib/python3.7/site-packages/aiohttp/streams.py in _wait(self, func_name)
    295             if self._timer:
    296                 with self._timer:
--> 297                     await waiter
    298             else:
    299                 await waiter

~/.pyenv/versions/3.7.3/envs/test/lib/python3.7/site-packages/aiohttp/helpers.py in __exit__(self, exc_type, exc_val, exc_tb)
    583 
    584         if exc_type is asyncio.CancelledError and self._cancelled:
--> 585             raise asyncio.TimeoutError from None
    586         return None
    587 

TimeoutError:

Steps to reproduce

resp = asyncio.run(test("test"))

Your environment

aiohttp==3.5.4
python3.7.3

client question

All 6 comments

You're using a 30-second timeout. What did you expect to happen instead?

I don't expect to get the timeout error when I read a response body.
Also, I don't have the same issue with the requests library

Example:

[requests.get('http://localhost:8080/test/', params=params) for i in range(150)] 

It works fine

Aiohttp.client example:

[test(value) for i in range(150)] 
TimeoutError ...

It craches

I think this is by design. The timeout is applied to the whole client session, not just to exchanging headers.
requests is a synchronous lib and I bet they read the whole payload in the request function. This approach is dangerous because it can crash your process if you hit a huge file (or a number of those) which would consume all your memory.
With aiohttp, you can process such things by chunks making it safer and possible to discard processed chunks freeing up the memory.

Plz fix the indentation in your snippet: it's unclear whether your if-blocks are in the async CM or not.

@webknjaz Thanks for explaining.
I put the if-blocks in the async and it fixed the issue.
I really appreciate your help ;)

same issue, and also can be fixed by put the if-blocks in the async, but i cannot explain it, would you please tell me why? @myarik @webknjaz

Was this page helpful?
0 / 5 - 0 ratings