Fragment caching is the first caching mechanism most Rails developer use. Many times 2-3 lines of code will result in a much faster application. But it is tricky to debug and can become a resource grave if not used well.
Our Sandbox Rails Application
To demonstrate the effects I create an address book application and use the Faker Gem to populate the database with entries.
With the Faker gem we generate 1,000 persons and 100 companies. Each person belongs_to a company.
This gives a nice data pool to show the effect of caching.
Then we add the has_many association and a to_s method to the company model.
Activate Caching in the Development Environment
Caching is deactivated by default in the development environment. You have to activate it for this screencast.
What does a fragment cache do?
Sometimes a view takes a lot of time to render the HTML which gets delivered to the browser. An example in the page#index view:
Because of the 1 second sleep in each loop run this page is very slow. It takes about 3,080 ms to be rendered by Rails:
So lets have a look at http://guides.rubyonrails.org/caching_with_rails.html#fragment-caching and use fragment caching to cache the result of the loop:
The first request obviously didn’t improve because the cache had to be written. But the second request was delivered in 74ms:
Rails generates a cache key from the MD5 sum of the file (in this case app/views/page/index.html.erb). So the cache gets expired when ever the content of the view file gets changed.
To delete the cache in your development environment you can use this rake task:
The use case of this is very limited. Normally fragment caching is used in combination of ActiveRecord.
The People Show View
When we request the show view of the first Person it takes about 120 ms:
But guess what! We can add a fragment cache:
After we have written the fragment cache with the first request any following request gets rendered with content from the cache:
The cache key this time is a little bit more complicated. It still adds an MD5 checksum of the view file so it gets busted when ever you change the content of that file (which is what we want). But in addition to that it adds a cache key for the Person object. This is provided by the ActiveRecord method cache_key (http://apidock.com/rails/ActiveRecord/Base/cache_key). You can test it in the Rails console:
Because of the cache_key method every update of the object in the database will result in a new cache key and therefor bust the existing cache.
Conclusion: We have an easy to use system to cache views which where rendered with data provided by ActiveRecord objects.
The People Index View
The people#index view is a bit more complex but Rails fragment caching has us covered. We can use @people and Rails will automagiacally create a cache_key for that.
Rails is kind enough not to blame us but we are waisting a lot of time by creating a cache_key for every Person in the @people variable. This has to be done every time the cache is used. Which is unnecessary. All we need to know is when the last Person instance was updated and that can be requested with the maximum method.
Additionally we have to account the count of all people. Because if one Person got deleted we have to expire the cache but the maximum(:updated_at) didn’t change.
As a result the cache_key is much smaller, needs less time to compute and therefor the page is delivered faster:
The Russion Doll Trap
The russion doll is a powerful tool. The idea is to not just cache the whole table but each line of it in an extra cache.
We have to add a fragment cache for each table row:
The first request will become very slow because Rails has to write 1,000 caches and that takes time. We could life with that. The main problem is that every time we edit a Person the process of reading 999 rows and writing 1 row followed by writing the whole table takes way longer than generating the whole table and write it.
In this case the use of russion doll doesn’t make any sense.
Do not use russion doll without testing and understanding it! It has the potential of either save your life or become a foot gun.