Подтвердить что ты не робот

Структурирование приложения Rails для кэширования русской куклы с использованием отношения has_many

После изучения DHH и других статей блога об истечении срока действия кеша и кэшировании русской куклы, я все еще не уверен, как обращаться с одним типом отношения. Конкретно, отношение has_many.

Поделитесь результатами моих исследований с примером приложения. Это немного рассказ истории, поэтому держитесь. Скажем, у нас есть следующие модели ActiveRecord. Все, о чем мы заботимся, это правильное изменение модели cache_key, правильно?

class Article < ActiveRecord::Base
  attr_accessible :author_id, :body, :title
  has_many :comments
  belongs_to :author
end

class Comment < ActiveRecord::Base
  attr_accessible :article_id, :author_id, :body
  belongs_to :author
  belongs_to :article, touch: true
end

class Author < ActiveRecord::Base
 attr_accessible :name
  has_many :articles
  has_many :comments
end

У нас уже есть одна статья с одним комментарием. И другим автором. Цель состоит в том, чтобы изменить статью cache_key для статьи в следующих случаях:

  • Изменения в теле или названии статьи
  • Изменения в тексте комментария
  • Изменения имени автора статьи
  • Изменения имени автора комментария комментария

Итак, по умолчанию мы хорошо применимы к случаям 1 и 2.

1.9.3-p194 :034 > article.cache_key
 => "articles/1-20130412185804"
1.9.3-p194 :035 > article.comments.first.update_attribute('body', 'First Post!')
1.9.3-p194 :038 > article.cache_key
 => "articles/1-20130412185913"

Но не для случая 3.

1.9.3-p194 :040 > article.author.update_attribute('name', 'Adam A.')
1.9.3-p194 :041 > article.cache_key
 => "articles/1-20130412185913"

Определить композитный метод cache_key для Article.

class Article < ActiveRecord::Base
  attr_accessible :author_id, :body, :title
  has_many :comments
  belongs_to :author

  def cache_key
    [super, author.cache_key].join('/')
  end
end

1.9.3-p194 :007 > article.cache_key
 => "articles/1-20130412185913/authors/1-20130412190438"
1.9.3-p194 :008 > article.author.update_attribute('name', 'Adam B.')
1.9.3-p194 :009 > article.cache_key
 => "articles/1-20130412185913/authors/1-20130412190849"

Win! Но, конечно, это не работает для случая 4.

1.9.3-p194 :012 > article.comments.first.author.update_attribute('name', 'Bernard A.')
1.9.3-p194 :013 > article.cache_key
 => "articles/1-20130412185913/authors/1-20130412190849"

Итак, какие варианты остались? Мы могли бы что-то сделать с ассоциацией has_many на Author, но has_many не принимает параметр {touch: true} и, вероятно, по какой-то причине. Я предполагаю, что это может быть реализовано несколько в следующих строках.

class Author < ActiveRecord::Base
  attr_accessible :name
  has_many :articles
  has_many :comments

  before_save do
    articles.each { |record| record.touch }
    comments.each { |record| record.touch }
  end
end

article.comments.first.author.update_attribute('name', 'Bernard B.')
article.cache_key
  => "articles/1-20130412192036"

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

Уверен, что пример использования 37signals может быть другим: project -> todolist -> todo. Но я представляю себе один предмет todo, также принадлежащий пользователю.

Как решить эту проблему кэширования?

4b9b3361

Ответ 1

Один метод, который я наткнулся, - это обработать его с помощью ключей кеша. Добавьте ссылку has_many_through для комментаторов в статью:

class Article < ActiveRecord::Base
  attr_accessible :author_id, :body, :title
  has_many :comments
  has_many :commenters, through: :comments, source: :author
  belongs_to :author
end

Затем в статье /show мы построим ключ кеша следующим образом:

<% cache [@article, @article.commenters, @article.author] do %>
  <h2><%= @article.title %></h2>
  <p>Posted By: <%= @article.author.name %></p>
  <p><%= @article.body %></p>
  <ul><%= render @article.comments %></ul>
<% end %>

Фокус в том, что ключ кеша, сгенерированный из ассоциации commenters, будет меняться всякий раз, когда комментарий добавляется, удаляется или обновляется. Хотя для этого требуется дополнительный SQL-запрос для генерации ключа кеша, он отлично работает с кэшем низкого уровня Rails и добавляет что-то вроде gem_type_cache может легко помочь с этим.

Я хотел бы посмотреть, есть ли у других людей более чистые решения для этого.

Ответ 2

Как указано здесь https://rails.lighthouseapp.com/projects/8994/tickets/4392-add-touch-option-to-has_many-associations, в моем случае я только что создал обратный вызов after_save для обновления временных меток связанных объектов.

  def touch_line_items_and_tactics
    self.line_item_advertisements.all.map(&:touch)
  end

В стороне, мы построили наше приложение rails в старой базе данных, которая имеет last_modified_time в качестве имени столбца, а семантика - "когда пользователь последний раз модифицировал его". Поэтому из-за различной семантики мы не могли использовать параметр :touch из коробки. Мне пришлось обезопасить cache_key и прикоснуться к таким методам https://gist.github.com/tispratik/9276110, чтобы сохранить обновленную временную метку в memcached вместо столбца updated_at databases.

Также обратите внимание, что я не мог использовать по умолчанию cache_timestamp_format из Rails, поскольку он имеет временные метки только до нескольких секунд. Я чувствовал необходимость иметь более гранулированную временную метку, поэтому я выбрал: nsec (наносекунды).

Временная метка с cache_timestamp_format: 20140227181414
Временная метка с nsec: 20140227181414671756000