When generating cache-keys for rendered content in web applications, you have to take into account all variables that might change the result.
In dynamic environments like rails these can be defined in different places: the controller, a model, the session or the server environment. And they can be referenced in the template, in a template rendered within the template or a helper.
Can you think of a way to automatically generate a list of variables that contribute to the content of the rendered template, maybe using ParseTree?
Asked
Active
Viewed 237 times
2

odo
- 31
- 3
1 Answers
0
I use a "freshness key" in my cache keys, probably learned from this article: http://blog.leetsoft.com/2007/5/22/the-secret-to-memcached
This allows me to easily invalidate all cache related to a resource, regardless of the url, despite the fact that memcached does not provide a facility for iterating existing keys.
I typically generate mine using a combination of the request.url and the logged in user id and freshness_key, e.g.
#
# return the freshness_key for caching this particular collection
def get_freshness_key_for(collection_name)
Rails.cache.fetch("#{self.id}:#{collection_name}") { "#{self.send(collection_name).maximum(:updated_at).to_i}:#{Time.now.to_i}" }
end
#
# set the freshness_key for caching this particular collection;
# typically called after_save from an Observer for this collection
#
def set_freshness_key_for(collection_name)
Rails.cache.write("#{self.id}:#{collection_name}", "#{self.send(collection_name).maximum(:updated_at).to_i}:#{Time.now.to_i}")
end
# returns the cache_key for this client, the desired collection, and the
# current url with the latest freshness_key
#
# the url is hashed so as not to overrun memcached's key length limit
def cache_key_for(collection_name, request_url)
freshness_key = self.get_freshness_key_for(collection_name)
"#{self.id}:#{Digest::MD5.hexdigest(request_url)}:#{freshness_key}"
end
I'll use it in a controller:
@posts_cache_key = cache_key_for(:posts)
@posts = cache(@posts_cache_key) do
Post.paginate(
:page => params[:page],
:per_page => params[:pp]
)
end
...and in the view:
<% cache(:key => "#{@posts_cache_key}:posts_list_fragment") do -%>
... html here ...
<% end -%>
Typically I'll have an Observer for the collection model:
class PostObserver < ActiveRecord::Observer
def after_save(post)
post.client.set_freshness_key_for(:posts)
end
def after_destroy(post)
post.client.set_freshness_key_for(:posts)
end
end
Hope this helps

jemminger
- 5,133
- 4
- 26
- 47