Problem:
Python version: 3.7
We have one API which makes a call to the database. In order for the DB call to not block the event loop, we run the task in the background using asyncio. We get a large number of requests and we don't want other calls to be blocked while processing current request The request timeout for the incoming request is 60 seconds. It works fine in most of the cases. But the main thread is not responsive for few seconds when one of the task running in a thread is canceled.
for example, the below is a function in the controller. This calls the database. I have simplified it with time.sleep.
Code:
async def __transform(self, request: web.Request):
loop_example = LoopExample(request['parameters'])
result = await loop_example.run_loop()
return response
class LoopExample:
__loop = asyncio.get_event_loop()
def __init__(self, parameters):
self.parameters = user_context.logger
async def run_loop(self):
try:
print(f"main threadId: {threading.get_ident()}")
return await self.__loop.run_in_executor(None, self.wait)
except Exception as e:
self.logger.exception(e)
print(f"exception threadid(goes back to main thread): {threading.get_ident()}")
//the below method is the blocking call.
def wait(self):
print(f"blocking loop threadId: {threading.get_ident()}")
time.sleep(60)
Notes:
I have tested with sending multiple requests at the same time. I can see multiple thread IDs (one for each request) getting printed and obviously they are not blocking the main event loop. But when one of the task running in a thread is canceled (because when the input request's HTTP timeout, I get a CancelledError like this
"""in run_loop\n return await self.__loop.run_in_executor(None, self.wait)\nconcurrent.futures._base.CancelledError"} )""",
when this happens, the service is not responsive for few seconds. After a few seconds (~8 seconds), it is back to normal. So in the 8 seconds, other requests which hit the endpoint get a 502 (Gateway timeout error). Also from the docs https://docs.python.org/3.6/library/asyncio-eventloop.html, looks like python uses ThreadPoolExecutor as Default. I tried explicitly specifying the ThreadPoolExecutor and it's the same behavior. Can someone help me in identifying what is wrong here? Thanks.