Я очень люблю Template Toolkit и считаю его лучшим по набору возможностей, удобству и гибкости шаблонизатором ;) Но есть одна проблема с этим шаблонизатором, а именно: за всё надо платить. В данном случае за функциональность и гибкость приходится платить скоростью обработки. Долгое время считал эту проблему нерешаемой, приходилось с грустью констатировать, что обработка шаблона стоит на первом месте по затратам времени при генерации большинства страниц... Но хотелось невозможного... Возникла идея кэшировать так называемые "подшаблоны", т. е. шаблоны, вызываемые из шаблонов более верхнего уровня. То есть, например, у нас есть шаблон index.tt, в нём инклюдятся (с помощью INCLUDE или PROCESS) подшаблоны header.tt и footer.tt (общие шапка и подвал для всего сайта). В header.tt и footer.tt инклюдится ещё что-то и т. п. Так вот, мы заметили, что именно обработка подшаблонов в нашем случае занимает довольно много времени, до 90% и более от общего времени работы TT. При этом многие подшаблоны тянут данные из базы (с помощью моделей Catalyst) или ещё откуда-то, содержат кучу сложной логики. Всё это обрабатывается достаточно долго... При этом, что самое обидное, выдача подшаблонов (те данные, что подставляются в вышестоящий шаблон) практически статична, мало меняется во времени. Конечно, с кэшированием не всё так просто: подшаблоны могут отдавать разную выдачу в разных контекстах -- например, выдача может зависеть от того, залогинен ли пользователь в системе и от того, какой выбран язык сайта. Но все эти проблемы решаемы -- я расскажу как. Так вот... Путь к невозможному начался с попыток профайлинга времени обработки подшаблонов. В процессе исследования темы наткнулся на замечательную статью Profiling in Template Toolkit via overriding, без которой этот текст бы сейчас не писался. Затем исследование продолжилось. И, наконец, закончилось написанием модуля Template::Context::SRS. Хотел рассказать об этом модуле на YAPC 2010 и к тому времени "причесать" и выложить на CPAN, но чувствую не дотерплю и выкладываю сейчас. ;) Перед выкладкой на CPAN модуль, конечно, нужно причесать: написать документацию и тесты и отвязать от нашего внутреннего модуля "SRS::Cache", но это очень легко делается, просто нужно этим заняться. Может быть кто-то даже поможет с этим ;) Тем не менее, всё это работает. Работает стабильно. Уже несколько месяцев на production: "не единого разрыва" по причине механизма кэширования. * * * Инструкция по эксплуатации. Всё очень просто: 1. Где-то в проекте тупо делается "use Template::Context::SRS;". 2. Затем в шаблоне, в котором хотим закэшировать выдачу какого-то подшаблона, делаем как-то так: [% PROCESS index_reg_bar.inc lang = lang pricegroup = pricegroup __cache_time = -1 %] Ключевым здесь является передача служебной переменной "__cache_time". Она и включает кэширование. "-1" означает -- кэшировать вечно. Но можно задать конкретное время устаревания кэша в секундах. Прочие параметры имеют не менее сакральный и важный смысл -- это не просто передача параметров в подшаблон, а ещё и критерий для формирования ключа кэширования. Ключи кэширования имеют значение в том случае, если выдача подшаблона зависит от каких-то условий: текущего языка и т.п. Так вот, уникальная комбинация имён / значений передаваемых параметров и будет служить уникальным ключом кэширования. Например, в предыдущем примере выдача шаблона зависит от двух параметров -- языка (lang) и тарифного плана пользователя (pricegroup). В нетривиальных случаях, когда подшаблон достаточно чувствителен к контексту и зависит от множества параметров, вызов может быть достаточно сложным: [%- PROCESS header_screen_body_cached.inc new_messages_num = user_messages.size() || user_messages_num || 0 __nocache_banner_no = banner_no __nocache_lang = lang __nocache_wide_flag = wide_flag __nocache_path_query = path_query __nocache_user_id = user_id __nocache_user_balance = user_balance __nocache_servername = servername __nocache_redirect_after_login = redirect_after_login __nocache_path = path __nocache_show_tooltips = show_tooltips __nocache_show_tipsofday = show_tipsofday __nocache_nostdheader = nostdheader __nocache_tab_item = tab_item __nocache_subtab_item = subtab_item __nocache_load_token_js = load_token_js __nocache_title_nopad = title_nopad __nocache_page_title_body = page_title_body __nocache_page_title = page_title __nocache_req_method = c.req.method __cache_time = user_messages.size() ? 0 : -1 -%] Параметры, начинающиеся с "__nocache_" -- фиктивные, используются только для формирования ключа кэширования. В подшаблон они не попадают -- удаляются перед его вызовом. В данном случае значения параметров, передаваемых через "__nocache_", всё равно попадают в шаблон -- как соответствующие глобальные переменные. Например, "page_title" -- переменная, видимая в текущем неймспейсе и она же будет видна в неймспейсе подшаблона. Можно было бы, конечно написать "page_title = page_title", но, по моему, это выглядит как-то странно. С "__nocache_" вроде бы это более явно -- ясно, что этот параметр нужен только для формирования ключа кэширования и не используется в подшаблоне. * * * Итак, всё это работает. Результат достигнут -- обработка многих тяжёлых шаблонов, например, главной страницы, ускорилась в разы. Этот текст занимает больше байт, чем сам модуль ;)