вторник, 15 декабря 2009 г.

Template cache

Есть у меня дурная привычка, просматривать svn log джанги по утрам, благодаря которой и появился этот блог, в общем, встречайте первый блин:

Сегодня я хочу поговорить о тормозах, и, как мне кажется, самое медленное место в джанге (помимо кривых рук программистов) это темплейты.

Сам принцип их работы очень напоминает cgi - на каждый запрос мы все делаем заново, заново ищем файл, заново читаем файл (или еще что) и наконец заново компилируем (получаем объект Template).
Конечно на этом работа с темплейтами не заканчивается, сам рендеринг еще не начинался, и основные тормоза как раз в нем, но повторюсь это выполняется на каждый запрос, и это правда не быстро.

Для решения этой проблемы 2 года назад открыли тикет 6262, предлагалось кэшировать темплейты после их первой загрузки, все бы хорошо, и сама задача выглядит не сложной, но есть забавные подводные камни.

Дело в том, что некоторые тэги хранят промежуточные данные в аттрибутах объекта Node, и они не потокобезопасны, например:
{% cycle 'row1' 'row2' %}Поток 1, итерация 1 - 'row1'Поток 2, итерация 1 - 'row2'Поток 1, итерация 2 - 'row1'Поток 2, итерация 2 - 'row2'

Как видно, поведение тэга при одновременной обработке 2 запросов явно отличается от ожидаемого.

Чтобы решить эту, не совсем очевидную проблему, решили хранить переменные тэгов, прямо в объекте контекста.

class CycleNode(Node):    def __init__(self, cyclevars):        self.cyclevars = cyclevars    def render(self, context):        if self not in context.render_context:            context.render_context[self] = itertools.cycle(self.cyclevars)        cycle_iter = context.render_context[self]        return cycle_iter.next()

И теперь поведение "cycle" будет таким, как от него ожидается.

Это дало возможность достаточно легко приделать кэширование и еще раз поломать обратную совместимость )
Т.е. тем, кто хочет использовать это кэширование стоит проверить свои тэги на потокобезопасность, а иначе юзер может сильно удивляться обновляя страницу и видя каждый раз новые данные.


Кстати, заодно была переделана сама система загрузки шаблонов.

Раньше было так:
TEMPLATE_LOADERS = (    'django.template.loaders.filesystem.load_template_source',    'django.template.loaders.app_directories.load_template_source',)

А стало:
TEMPLATE_LOADERS = (    'django.template.loaders.filesystem.Loader',    'django.template.loaders.app_directories.Loader',)

Поменялось совсем чуть-чуть, но в этом чуть-чуть сама соль )
Сейчас это не функции, а классы (ну наконец то!!11) с простым интерфейсом. А старый стиль объявлен устаревшим и будет удален после релиза 1.4

Так вот, чтобы начать использовать кэш, надо только сделать так:
TEMPLATE_LOADERS = (    ('django.template.loaders.cached.Loader', (        'django.template.loaders.filesystem.Loader',        'django.template.loaders.app_directories.Loader',    )),)



И все, больше не будет повторной загрузки темплейта.

Кстати, если у вас свои загрузчики темплейтов, то мигрироваться будет совсем не сложно, все что нам нужно - это переопределить 1 функцию.

class Loader(BaseLoader):    is_usable = True    def load_template_source(self, template_name, template_dirs=None):        pass

Так же как и раньше надо написать load_template_source, которая принимает имя темплейта и возвращает тупл из его текстового содержимого и имени файла.

Ну вот вроде и все )

ps надеюсь было интересно )

Комментариев нет:

Отправить комментарий