9

I'm trying to write a tornado web application that uses sqlalchemy in some request handlers. These handlers have two parts: one that takes a long time to complete, and another that uses sqlalchemy and is relatively fast. I would like to make the slow part of the request asynchronous, but not the sqlalchemy part. Can I do something like the following code and be safe?

class ExampleHandler(BaseHandler):
    async def post(self):
        loop = asyncio.get_event_loop()
        await loop.run_in_executor(...)   # very slow (no sqlalchemy here)

        with self.db_session() as s:      # sqlalchemy session
            s.add(...)
            s.commit()

        self.render(...)

The idea is to have sqlalchemy still blocking, but have the computational heavy part not blocking the application.

Miguel
  • 658
  • 1
  • 6
  • 19

1 Answers1

4

The tornado web server uses asynchronous code to get around the limit of the python Global Interpreter Lock. The GIL, as it is colloquially known, allows only one thread of execution to take place in the python interpreter process. Tornado is able to answer many requests simultaneously because of its use of an event loop. The event loop can perform one small task at a time. Let's take your own post handler to understand this better.

In this handler, when the python interpreter gets to the await keyword, it pauses the execution of the function and queues it for later on its event loop. It then checks the event loop to respond to other events that may have queued up there, like responding to a new connection or servicing another handler.

When you block in an asynchronous function, you freeze the entire event loop as it is unable to pause your function and service anything else. What this actually means for you is that your web server will not accept or service any requests while your async function blocks. It will appear as if your web server is hanging and indeed it is stuck.

To keep the server responsive, you have to find a way to execute your sqlalchemy query in an asynchronous non-blocking manner.

Ananth
  • 848
  • 11
  • 26
  • Hey Miguel, if you need any more info or if something is not clear enough I can edit my answer. What isn't working for you? – Ananth Nov 15 '18 at 11:01
  • As far as I understand, the `run_on_executor` assigns execution of my slow part of the code to a thread pool. Now, the sqlalchemy is not made to be asynchronous and that's why I'm using it blocking the eventloop to avoid mixing sessions. As I understand it, the code seems correct, but I'm not 100% sure and can be missing something. There so many different libraries and ways to do it... and I don't completely understand the details... Anyway, @Ananth excellent explanation of async/await. – Miguel Nov 15 '18 at 17:33
  • @Anath I was looking for a good reference book which uses both tornado framework along with SQLALchemy. Do you know any? – Masoud Masoumi Moghadam May 28 '19 at 15:25
  • I've no idea. I'm using the aiohttp framework now. It's build on top of the python standard library's asyncio module. – Ananth May 28 '19 at 18:24