SCGH SCGH - 1 month ago 25
HTML Question

How does tornado.web.RequestHandler.render_string() load a template without blocking the whole application?

I'm loading templates from a coroutine in Tornado using

render_string()
and it came to my mind that
render_string()
is not a coroutine. So we're accessing the disk form a coroutine, but we're not yielding any futures. I wonder how does this work and if it blocks the application or not.

Answer

It's true, the first time a given template is read from the disk, it blocks Tornado's event loop:

class Loader(BaseLoader):
    def _create_template(self, name):
        path = os.path.join(self.root, name)
        with open(path, "rb") as f:
            template = Template(f.read(), name=name, loader=self)
            return template

This initial load is probably fast, since the template is likely only a few kilobytes and already loaded into the machine's in-memory filesystem cache. Subsequent accesses of the same template by the same Tornado process are cached within Tornado itself:

class BaseLoader(object):
    def load(self, name, parent_path=None):
        """Loads a template."""
        name = self.resolve_path(name, parent_path=parent_path)
        with self.lock:
            if name not in self.templates:
                self.templates[name] = self._create_template(name)
            return self.templates[name]

So it doesn't seem worthwhile for Tornado to defer the filesystem access to a thread.

Often in Python async frameworks you'll see that not all I/O is performed asynchronously - quick and predictable blocking operations like accessing a file or a MySQL query might not block the loop long enough to worry about. What's critical is that long or unpredictable operations, like calling a remote HTTP service, are scheduled on the event loop.