#ruby-on-rails #caching #tags
#ruby-on-rails #кэширование #Теги
Вопрос:
Наше приложение использует Rails.cache
в контроллере для кэширования некоторых элементов за пределами области представления (например, мета-тегов), затем использует fragment_caching для большей части представления.
Представление кэширует одну основную модель, но мы использовали данные из 5 других моделей (не связанных ассоциацией) внутри этого основного кэша. Срок действия фрагмента легко истечь с помощью средства очистки в основной модели, но эти дополнительные модели также меняются и должны инициировать истечение срока действия этой страницы.
Мы не можем использовать маршрут регулярных выражений для удаления ключей кэша, потому что мы должны ссылаться на эту запись кэша только по основной модели — другие модели определяются дорогостоящим запросом, который мы выполняем внутри блока кэша в контроллере.
Есть ли в Rails 3 способ по существу использовать теги для обозначения записи в кэше, чтобы мы могли удалить ее при изменении любой из 6 моделей на странице, но мы все равно можем найти запись в кэше только по ключу основной модели?
Вот некоторый фиктивный код, чтобы выразить идею:
В контроллере
@cache_key = "/page/#{params[:name]}/#{params[:id]}"
unless fragment_exist? ( { :slug => @cache_key })
# run our processes here that will be needed in the view,
# then cache the data that is used outside the view
Rails.cache.write(@cache_key, { (data goes here) } )
# run our expensive query here:
@similar_pages = Page.pricey_query!.limit(5).all
else
cached = Rails.cache.read(@cache_key)
end
В представлении
- cache( {:slug => @cache_key} ) do
- @similar_pages.each do |page|
= image_tag page.photos.first.image.url
-# more pretty stuff here
Моя цель:
- Я: «О, страница @cache_key изменилась, давайте прекратим ее действие!»
- Rails:
Okay, easy!
- Я: «Одна из похожих страниц изменила свою первую фотографию, что мне делать?»
- Rails:
Umm... #(*$^*@ .. does ... not ... compute.
Комментарии:
1. Некоторые системы кэширования имеют больше возможностей, чем другие. Какие вы собираетесь использовать?
2. Я пока не нашел ни одного, который поддерживал бы систему, подобную тегам. Ни встроенное кэширование, ни cache_fu, ни cache_money не поддерживают систему, подобную тегам.
3. Вы рассматривали возможность создания Observer для управления этим за вас?
4. Я сталкиваюсь с той же проблемой, что и с Sweeper — мне нужно получить доступ к кэшированному элементу с помощью одного ключа, но мне нужно иметь возможность завершить его с помощью любой из 6 «фраз». Я объясню в вопросе.
5. Также хотел сказать, что ни одно из доступных хранилищ кэша, таких как filestore или memcached, похоже, не поддерживает эту концепцию.
Ответ №1:
Как утверждает тадман в комментариях к вопросу, мне пришлось изобрести свое собственное решение, поскольку Rails технически не допускает теги в том смысле, что они мне были нужны. Вот обобщенное решение для тех, кто заинтересован в выполнении чего-то подобного:
Я создал новую таблицу под названием SimilarPages
:
create_table :similar_pages, {:id => false} do |t|
t.integer :page_id, :similar_page_id
# you could also do `t.string :tag_name` or similar
end
add_index :similar_pages, :page_id
add_index :similar_pages, :similar_page_id
Технически, я мог бы создать has_many
связь с самоссылкой на Pages
, но я решил не делать этого, поскольку мне никогда не нужно ссылаться на это таким образом. Я только что создал простую SimilarPage
модель:
class SimilarPage < ActiveRecord::Base
belongs_to :page
belongs_to :similar_page, :class_name => 'Page'
end
Затем, используя ar-extensions
(потому что я ленив, а также потому, что я хотел сделать это в одном операторе INSERT), я делаю это в блоке кэша:
SimilarPage.delete_all("page_id = '#{@page_id}'")
SimilarPage.import [:page_id, :similar_page_id], @similar_pages.collect {|s| SimilarPage.new(:page_id=>@page_id,:similar_page_id=>s.id)}
В моем expire_cache_for
методе моего наблюдателя я делаю это:
SimilarPage.where(:similar_page_id => expiring_page.id).all.each do |s|
ActionController::Base.new.expire_fragment(/page_show__#{s.page_id}__.*/)
# the regexp is for different currencies being cached ^
Rails.cache.delete("page_show_#{s.page_id}")
end
Ответ №2:
В этом видео есть отличное объяснение того, как Rails генерирует теги для истечения срока действия содержимогоhttp://railslab.newrelic.com/2009/02/19/episode-8-memcached
Короче говоря, если это так, то если вы передадите весь свой объект в кэш, он сгенерирует ключ кэша, который использует временную метку, которая будет обновляться при изменении вашего объекта.
Комментарии:
1. Просто обратите внимание, видео выше предназначено для memcached, но генерация ключа cahche не специфична для этого
Ответ №3:
Кассир может быть полезен. «Кэширование на основе тегов»
# in your view
cache @some_record, :tag => 'some-component'
# later
Cashier.expire 'some-component'