mdrozdziel
mdrozdziel

Reputation: 5558

What's the most optimal way to regenerate static cache with Ruby on Rails?

I have a pretty slow controller action, which does some raporting here and there. I only need to refresh the data every few days, so it was no brainer to static cache the result.

The problem is, that the action takes solid few minutes to complete and I am not sure whats the most optimal way to expire the old data and replace them with the new ones.

Now the problem with just generic expire/request is that for few minutes (time when the action is running) those data are unavailable.

Is there any resonable way to overcome this gap using just static cache mechanisms in Rails? Or should I just rebuild the whole thing in a different way?

Upvotes: 0

Views: 981

Answers (1)

Thomas Klemm
Thomas Klemm

Reputation: 10856

Rails has a built-in way to use stale caches for just a bit longer when it expires while the new cache value is being regenerated. It's the :race_condition_ttl setting used in conjuction with :expires_in, as describe in the Rails Guides on Caching.

With Rails Fragment caching the syntax should be:

<% cache 'my_awesome_cache_key', :expires_in => 12.hours.to_i, :race_condition_ttl => 12.hours.to_i %>
  # This block will be cached
<% end %>

Results:

  • 1) First request: Cache is empty, so block will be executed and results written to cache
  • 2) All requests between the next 24 hours: Will be served straight from the cache (which is fresh)
  • 3a) First request after 24 hours but WITHIN grace period will be served from the then slightly stale cache; the cache will be regenerated in the background: the block is executed and written to the cache as soon as its finished
  • 3b) First request after 24 hours but out of the grace period will be handled as 1), i.e. not served directly but block will be executed, cache will be written and request will be served.

race_condition_ttl was introduced to prevent multiple processes from regenerating a cache simultaneuosly, which would result in all processes reading the data from the db at once etc. on a highly requested resource, but I think it should work well for your situation.

How to choose the timing for :expires_in and :race_condition_ttlis your choice then, I'd suggest calculating it like this: expires_in + race_condition = expires_in_that_you_would_usually_set. This way the cache is regenerated more often but also fresher, especially if the action/view is not rendered that often.

Upvotes: 3

Related Questions