Sanic Configuration help

Could you please help me to understand how Sanic configuration will work when we have blocking code as part of some methods.

For Instance in below example I have set response timeout as 1 sec in configuration but , it looks like it is not considering configuration response time value and code is waiting for whole 2 sec time.sleep(2) and returning the response. As per my understanding here I would except sanic return 503 response timeout in 1 sec ? please correct me if I am missing something here.

Same code is working as excepted, if I use asycio.sleep instead of sleep

from time import sleep
from sanic import Sanic
from sanic.log import logger
from sanic.response import text

Sanic_CONFIG = {
“REQUEST_MAX_SIZE”: 10000,
“REQUEST_BUFFER_QUEUE_SIZE”: 2000,
“REQUEST_TIMEOUT”: 1,
“RESPONSE_TIMEOUT”: 1,
“KEEP_ALIVE”: True,
“KEEP_ALIVE_TIMEOUT”: 15,
“ACCESS_LOG”: False
}

app = Sanic(‘Config Example’)
app.config.update(Sanic_CONFIG)

@app.route(’/’)
async def test(request):
sleep(2)
logger.info(‘Here is your log’)
return text(‘Hello World!’)

if name == “main”:
app.run(debug=True, access_log=True)

You should not use any blocking calls in async code. There is no way for Sanic to interrupt standard sleep, or to do anything else meanwhile (it cannot serve other requests, everything inside the worker just freezes). This is precisely why you should be using asyncio.sleep, and in general things that use await while they are doing something that takes longer than a millisecond to run.

Slower tasks and blocking calls should be run in threads. With Trio there is await trio.to_thread.run_sync(some_blocking_function) that makes this particularly easy. On asyncio side (which is what you normally use with Sanic) things are more complicated and you might need to roll your own. On the other hand, for most things there are already modules available that offer you async file I/O, databases, http client etc.

2 Likes

Thank you very much @Tronic we will explore trio, we might need to do some changes on our end to call sync functions (which are part of some common modules in our org which is being used by multiple applications running in flask, aws lambda functions) from async functions.

So if i am not understanding wrong, if we call sync functions inside async functions, Sanic will not consider custom server configurations values ? In order to make use of Sanic overrided sever configuration values , we need to make sure we are running / using async functions/modules ?

Please correct me , If i am thinking otherwise.

Thanks for your help!

It has nothing to do with Sanic reading the config values. Instead, it is like if your entire computer froze for two seconds, and only then Sanic gets to run and notices that the timeout has passed, and cancels the response. In async code other tasks (such as timeouts) can only execute when you await. If you don’t use await in your handler at all, this only happens right after the handler function returns.

Using a worker thread and awaiting for it to respond avoids the issue because Sanic keeps running normally in the main thread while the worker thread does some blocking operation. You also cannot use thread.join() or other blocking calls to wait for the result. That has to be an awaitable for any of this to make a difference because otherwise both threads will be blocked, and this is where things like trio.to_thread.run_sync come into play.

Since Sanic is based on Asyncio (and has only experimental support for Trio), you might wish to use https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor even though that is somewhat more complicated to use.

What I said applies to all async/await code on any framework, even in languages other than Python, not only Sanic, Asyncio and Trio.

2 Likes