This project is archived and is in readonly mode.
ActionView cache helper is inefficient
Reported by Stephen Eley | August 31st, 2010 @ 05:12 AM
I have a simple app that displays the schedule and session details for a large conference. With potentially hundreds of partials displayed on a single page, it was a natural candidate for fragment caching. The caching works fine, but I noticed this redundancy in the logs:
Started GET "/sessions?type=full&scope=am" for 127.0.0.1 at 2010-08-30 23:36:51 -0400
Processing by SessionsController#index as HTML
Parameters: {"type"=>"full", "scope"=>"am"}
Read fragment views/localhost:3000/sessions?type=full&scope=am (0.8ms)
Rendered sessions/_header.haml (2.7ms)
Rendered sessions/_search.haml (171.8ms)
Exist fragment? views/localhost:3000/sessions?id=4c5c40f0609c843161000001 (0.9ms)
Read fragment views/localhost:3000/sessions?id=4c5c40f0609c843161000001 (1.5ms)
Exist fragment? views/localhost:3000/sessions?id=4c5c40f3609c843161000003 (0.8ms)
Read fragment views/localhost:3000/sessions?id=4c5c40f3609c843161000003 (0.7ms)
Exist fragment? views/localhost:3000/sessions?id=4c5c40f6609c843161000005 (0.7ms)
Read fragment views/localhost:3000/sessions?id=4c5c40f6609c843161000005 (0.6ms)
Exist fragment? views/localhost:3000/sessions?id=4c5c40f7609c843161000006 (0.9ms)
Read fragment views/localhost:3000/sessions?id=4c5c40f7609c843161000006 (1.1ms)
Exist fragment? views/localhost:3000/sessions?id=4c5c40f9609c843161000007 (0.8ms)
Read fragment views/localhost:3000/sessions?id=4c5c40f9609c843161000007 (0.7ms)
...And so forth. My observation is that on a cache hit, the
ActionView::Helpers::CacheHelper#cache
method has two
steps (underneath various layers of abstraction) of approximately
equal duration:
- Call the underlying cache store's
#exist?
method - Perform a
#read
to get the content
However, this is redundant because the #exist?
method calls the same lower-level #read_entry
method
that #read
does! We're retrieving the exact same value
from the cache store twice. The first time we return a
true or false and throw the value away; the second time, we keep
the value.
This is an obvious candidate for refactoring. Every cache store
has consistent behavior on #read
that returns nil in
the event of a cache miss. It'd be very easy to skip the existence
test and simply call the execute-and-write code if the read doesn't
return a value.
I'll submit a patch for this presently. I just wanted to register the issue first. For complex pages with many fragment hits, this has the potential to nearly double rendering performance.
Comments and changes to this ticket
-
Neeraj Singh September 2nd, 2010 @ 04:48 AM
- State changed from new to open
Stephen : nice catch.
# Return true if the cache contains an entry for the given key. # # Options are passed to the underlying cache implementation. def exist?(name, options = nil) options = merged_options(options) instrument(:exist?, name) do |payload| entry = read_entry(namespaced_key(name, options), options) puts entry.inspect if entry && !entry.expired? true else false end end end
Above method which is declared in AS cache.rb does get hold of the entire cached payload.
However there is one thing to notice. This is an API for these storage types.
# config.action_controller.cache_store = :memory_store # config.action_controller.cache_store = :file_store, "/path/to/cache/directory" # config.action_controller.cache_store = :drb_store, "druby://localhost:9192" # config.action_controller.cache_store = :mem_cache_store, "localhost" # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211") # config.action_controller.cache_store = MyOwnStore.new("parameter")
If you change the code in cache.rb to return the payload instead of true if there is content and use that value going forward then there is a possibility that a custom implementation of cache storage might be overriding 'def exist?' and might be returning true or false. In that case be using 'true' as the payload.
I guess this fix is very much needed. However we should publish this change in API so that custom impelementation of cache is not caught off guard.
-
Santiago Pastorino February 2nd, 2011 @ 04:49 PM
This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails core team are limited, and so we are asking for your help. If you can still reproduce this error on the 3-0-stable branch or on master, please reply with all of the information you have about it and add "[state:open]" to your comment. This will reopen the ticket for review. Likewise, if you feel that this is a very important feature for Rails to include, please reply with your explanation so we can consider it.
Thank you for all your contributions, and we hope you will understand this step to focus our efforts where they are most helpful.
-
Santiago Pastorino February 2nd, 2011 @ 04:49 PM
- State changed from open to stale
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>