3

I have been using cache for a long time and recently discovered that my fragment caching doesn't stop my controller from executing the code, as it always has been. I have boiled the problem down to have to do with the cache_key that seems to be a new feature?

This is my previous solution that no longer works as expected.

Product#Show-view:

cache('product-' + @product.id.to_s)  do
# View stuff
end

Product#Show-controller:

unless fragment_exist?('product-19834') # ID obviously dynamically loaded
# Perform slow calculations
end

The caching works fine. It writes and reads the fragment, but it still executes the controller (which is the whole reason I want to use caching). This boils down to the fragment has an added unique id so the fragment created is something like:

views/product-19834/b05c4ed1bdb428f73b2c73203769b40f

So when I check if the fragment_exist I am not checking for the right string (since I am checking for 'views/product-19834'). I have also tried to use:

fragment_exist?("product-#{@product.id}/#{@product.cache_key}")

but it checks with a different cache_key than is actually created.

I would rather use this solution than controller-caching or gems like interlock.

My question is: - How do I, in the controller, check if a fragment exist for a specific view considering this cache key?

Christoffer
  • 2,271
  • 3
  • 26
  • 57
  • 1
    Have you used action caching? – Vishnu Atrai Aug 21 '15 at 11:29
  • I have tried that some time back but it turned out not to cause problems elsewhere, I pretty much need to use this solution. It feels like it should not be too much out of the box, right? – Christoffer Aug 21 '15 at 11:39
  • The view hash strings appended by the cache_key is an MD5 hash of your view template. Unfortunately that will be tough to access from your controller. I recommend refactoring your controller logic to avoid having time consuming logic there dependent on the outcome of view fragment caches existing. For example you could get around this by model caching the methods that are time consuming within the controller instead. If that's not possible, a quick workaround could be to monkey patch rails to stop adding the MD5 hash when generating a cache_key: http://stackoverflow.com/a/25839145/3448554 – Kelsey Hannan Aug 21 '15 at 22:39
  • Thanks, Kelseydh. The solution in that link (skip_digest => true) worked fine! You can put that as an answer if you want. – Christoffer Aug 22 '15 at 16:42

2 Answers2

7

As Kelseydh pointed out in the link, the solution to this is to use skip_digest => true in the cache request:

View

cache ("product" + @product.id, :skip_digest => true) 

Controller

fragment_exist?("product-#{@product.id}")
cvshepherd
  • 3,947
  • 3
  • 20
  • 14
Christoffer
  • 2,271
  • 3
  • 26
  • 57
1

It might be worth pointing out that while the proposed solution (fragment_exist?) could work, it's more like a hack.

In your question, you say

It writes and reads the fragment, but it still executes the controller (which is the whole reason I want to use caching)

So what you actually want is "controller caching". But fragment caching is "view caching":

Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store (Rails Guides 5.2.3)

For "controller caching", Rails already provides some options:

Which are all, from my point of view, better suited for your particular use case.

claasz
  • 2,059
  • 1
  • 14
  • 16