Rails 3 и Memcached - интеллектуальное кэширование без истечения срока - PullRequest
2 голосов
/ 08 апреля 2011

Я внедряю кеширование в свой Rails-проект через Memcached и, в частности, пытаюсь кешировать боковые блоки столбцов (самые последние фотографии, блоги и т. Д.), И в настоящее время у меня они истекают из кеша каждые 15 минут или около того. Это работает, но если я смогу сделать это более актуально, как всякий раз, когда новый контент добавляется, обновляется или еще что-то, это было бы лучше.

Я смотрел эпизод скринкастов Scaling Rails на Memcached http://content.newrelic.com/railslab/videos/08-ScalingRails-Memcached-fixed.mp4,, и в 8:27 на видео Грегг Поллак рассказывает об интеллектуальном кэшировании в Memcached таким образом, что интеллектуальные ключи (в этом примере updated_at timestamp) используются для замены ранее кэшированных элементов без истечения срока действия кэша. Таким образом, всякий раз, когда метка времени обновляется, кэш обновляется, поскольку ищет новую метку времени, я бы предположил.

Я использую боковой блок «Последние фотографии» для этого примера, и вот как он настроен ...

_side-column.html.erb:

<div id="photos"">
   <p class="header">Photos</p>
   <%= render :partial => 'shared/photos', :collection => @recent_photos %>     
</div>

_photos.html.erb

<% cache(photos) do %>
  <div class="row">
    <%= image_tag photos.thumbnail.url(:thumb) %>
    <h3><%= link_to photos.title, photos %></h3>
    <p><%= photos.photos_count %> Photos</p>
  </div>
</div>

<% end%>

При первом запуске Memcached кэширует блок как views / photos / 1-20110308040600 и перезагружает этот кэшированный фрагмент при обновлении страницы, пока что все хорошо. Затем я добавляю дополнительную фотографию к этой конкретной строке в бэкэнде и перезагружаю ее, но количество фотографий не обновляется. Журнал показывает, что он все еще загружается из views / photos / 1-20110308040600 и не получает обновленную метку времени. Все, что я делаю, похоже на то, что делает видео, что я делаю выше?

Кроме того, в этом вопросе есть вторая часть. Как вы видите в приведенном выше фрагменте, для коллекции вызывается запрос @recent_photos (из модуля в моей папке lib). Однако я заметил, что даже когда блок кэшируется, этот запрос SELECT все еще вызывается. Сначала я попытался обернуть весь фрагмент в блок как <% cache (@recent_photos) do%>, но, очевидно, это не сработало - тем более, что нет реальной отметки времени для всей коллекции, конечно, для отдельных элементов. , Как я могу предотвратить выполнение этого запроса, если результаты уже кэшированы?

UPDATE Что касается второго вопроса, я обнаружил, что разве Rails.cache.exist? может быть, просто мой билет, но хитрость заключается в подстановочной природе использования метки времени ...

ОБНОВЛЕНИЕ 2 Не обращая внимания на мой первый вопрос, я выяснил, почему кеш не обновлялся. Это потому, что поле updated_at не обновлялось. Причиной этого является то, что я добавлял / удалял элемент, который является вложенным ресурсом в родительском объекте, и мне, вероятно, нужно реализовать «прикосновение» к нему, чтобы обновить поле updated_at в родительском элементе.

Но мой второй вопрос все еще остается в силе ... основной запрос @recent_photos все еще вызывается, даже если фрагмент кэшируется ... есть ли способ использовать cache.exists? настроить таргетинг на кеш с именем что-то вроде / views / photos / 1-2011random?

Ответы [ 2 ]

2 голосов
/ 09 апреля 2011

Один из основных недостатков кэширования Rails заключается в том, что вы не можете надежно разделить контроллер и представление для кэшированных компонентов.Единственное решение, которое я нашел, - это встроить запрос в кешированный блок напрямую, но желательно с помощью вспомогательного метода.

Например, у вас, вероятно, есть что-то вроде этого:

class PhotosController < ApplicationController
  def index
    # ...

    @recent_photos = Photos.where(...).all

    # ...
  end
end

Первый инстинкт - запускать этот запрос только в том случае, если он потребуется представлению, например, проверка наличия кэшированного содержимого.К сожалению, существует небольшая вероятность того, что срок действия контента истечет в промежутке между проверкой его кэширования и фактическим отображением страницы, что приведет к ошибке визуализации шаблона при использовании значения nil @recent_photos.

Вот более простой подход:

<%= render :partial => 'shared/photos', :collection => recent_photos %>

Вместо использования переменной экземпляра используйте вспомогательный метод.Определите ваш вспомогательный метод так, как если бы вы загружали его внутри контроллера:

module PhotosHelper
  def recent_photos 
    @recent_photos ||= Photos.where(...).all
  end
end

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

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

1 голос
/ 30 апреля 2011

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

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

Я предполагаю, что при использовании этого метода всегда должен быть хотя бы один запрос, так как должен быть какой-то способ узнать, обновлена ​​ли временная метка или нет.

...