2

I was reading a few guides on caching in Rails but I am missing something important that I cannot reconcile.

I understand the concept of auto expiring cache keys, and how they are built off a derivative of the model's updated_at attribute, but I cannot figure out how it knows what the updated_at is without first doing a database look-up (which is exactly what the cache is partly designed to avoid)?

For example:

cache @post

Will store the result in a cache key something like:

posts/2-20110501232725

As I understand auto expiring cache keys in Rails, if the @post is updated (and the updated_at attribute is changed, then the key will change. But What I cannot figure out, is how will subsequent look-ups to @post know how to get the key without doing a database look-up to GET the new updated_at value? Doesn't Rails have to KNOW what @post.updated_at is before it can access the cached version?

In other words, if the key contains the updated_at time stamp, how can you look-up the cache without first knowing what it is?

Ash
  • 24,276
  • 34
  • 107
  • 152
  • Question doesn't seems clear.. Can you rephrase that what is the expected result? If you have tried something, its good to include that here – RAJ Aug 03 '14 at 14:42
  • Fair enough, clarified my confusion. – Ash Aug 03 '14 at 14:52
  • You have updated the question, looks good now... so rolled back downvote... – RAJ Aug 03 '14 at 14:58

2 Answers2

2

In your example, you can't avoid hitting the database. However, the intent of this kind of caching is to avoid doing additional work that is only necessary to do once every time the post changes. Looking up a single row from the database should be extremely quick, and then based on the results of that lookup, you can avoid doing extra work that is more expensive than that single lookup.

You haven't specified exactly, but I suspect you're doing this in a view. In that case, the goal would be to avoid fragment building that won't change until the post does. Iteration of various attributes associated with the post and emission of markup to render those attributes can be expensive, depending on the work being done, so given that you have a post already, being able to avoid that work is the gain achieved in this case.

Chris Heald
  • 61,439
  • 10
  • 123
  • 137
  • This is a great answer! But is there a way to use auto expiring cache keys (perhaps using Rails.cache) or low-level caching to avoid the database hit? – Ash Aug 06 '14 at 03:20
  • Well, you can, but at the end of the day you're going to have a non-expiring cache key that you have to maintain. You could keep the metadata for that post in a cache entry, read that, and then use that for your cache key, but at that point you're basically just treating the cache store like a second DB to look up the cache key information from - you always have to get that information from *somewhere*. This might be worth it if the cache is super fast and DB access is super slow, but otherwise the complexity cost is likely not worth any performance gains. – Chris Heald Aug 06 '14 at 07:32
  • Yeah I understand that, it's just that I am working on a site that gets a non-trivial amount of traffic and I'm trying to work out a way to reduce the number of database IOPS to help with site scalability. I'm not trying to be belligerent, but if i understand you correctly, the short answer is that any caching between the database and the webserver stacks will need to be done manually. Is this correct? – Ash Aug 06 '14 at 22:31
  • Well, you can use any kind of auto-expiring cache key you'd like, but you have to get it from somewhere - in that sense, it's manual (you probably don't want to cache DB results in-memory between requests, for example), but Rails does make it easy if you choose cache keys that tend to expire themselves by nature. You might look into full-request caching techniques like Varnish, though, if scale is that big of a concern. – Chris Heald Aug 07 '14 at 00:28
1

As I understand your question. You're trying to figure out the black magic of how caching works. Good luck.

But I think the underlying question is how do updates happen?

A cache element should have a logical key based on some part of the element, e.g. compound key, some key name based on the id for the item. You build this key to call the cache fragment when you need it. The key is always the same otherwise you can't have certainly that you're getting what you want.

One underlying assumption of caching is that the cache value is transient, i.e. if it goes away or is out of date its not a big deal. If it is a big deal then caching isn't the solution to your problem. Caching is meant to alleviate high load, i.e. a lot of traffic hitting the same thing in your database. Similar to a weblog where 1,000,000 people might be reading a particular blog post. Its not meant to speed up your database. That is done through SQL optimization, sharding, etc.

If you use Dalli as your cache store then you can set the expiry.

Essentially a caching loop in Rails AFAIK works like this:

enter image description here

So to answer your question:

The key gets updated when you update it. An operation that is tied to the update of the post. You can set an expiry time, which essentially accomplishes the desired result by forcing the cache update via a new lookup/cache write. As far as the cache is concerned its always reading the cache element that corresponds to the key. If it gets updated, then it will read the updated element, but its not the cache's responsibility to check against the database.

What you might be looking for is something like a prepared statement. Tenderlove on Prepared Statements or a faster datastore like a less safe Postgres (i.e. tuned to NoSQL without ACID) or a NoSQL type of database here.

Also do you have indexes in your database? DB requests will be slow without proper indexes. You might just need to "tune" your database.

Also there is a wonderful gem called cells which allows you to do a lot more with your views, including faster returns vs rendering partials, at least in my experience. It also has some caching functions.

Community
  • 1
  • 1
engineerDave
  • 3,887
  • 26
  • 28