Learn how Scrivito CMS can help you deliver amazing digital experiences
See Scrivito CMS in action

Utilizing Fragment Caching

Utilizing Fragment Caching

Caching helps to deliver the content of any website faster. Scrivito already caches a lot of the data to reduce the number of API service requests. Even though most of the data is already cached, generating the actual HTML markup can still take a considerable amount of time. To further improve the performance and free up resources on the server, parts of the generated markup can also be cached.

Rails already provides several helpers for caching parts of a view or even the result of a full request. If you are not yet familiar with caching in Rails, you should read the excellent Rails guide on this topic.

Caching fragments

Fragment caching allows you to save the computation result of a specific part of a view template and recall it in future requests. Whenever you identify markup that is generated often but rarely changes, making it cacheable should be beneficial.

Rails provides the great cache helper for easily implementing fragment caching. Based on this helper, Scrivito provides the scrivito_cache helper which is a wrapper around Rails' built-in fragment caching and makes it aware of Scrivito content. Content cached with scrivito_cache is recalculated as any published CMS object changes. So, if a part of a template only depends on Scrivito content, it can be given a unique key, and Scrivito takes care of the rest. For example:

<% scrivito_cache "#{@obj.id}-body" do %>   <%= scrivito_tag :div, @obj, :body %> <% end %>

Sadly, often it is not as easy as just caching everything. You might have a widget that displays a stock price and should be updated periodically. Luckily, all the options of Rails’ cache helper can be used with scrivito_cache. For example, in the template of your stock price widget you could cache the view using the following piece of code:

<% scrivito_cache "#{@obj.id}-#{widget.id}-stock-price", expires_in: 15.minutes do %>   <%= render_stock_price %> <% end %>

The expires_in option ensures that the stock price is recalculated at least every 15 minutes.

Please note that the scrivito_cache helper caches data of the published content only. This is to keep the amount of cached data small.

Low-level caching

Fragment caching is the go-to method for improving the performance of a Rails site. However, sometimes fragment caching may not be possible, e.g. if the data to be cached is not part of a view. For these cases Rails features a low-level cache that lets you store any kind of data. If the cached value originates from data handled by Scrivito, it needs to be invalidated whenever it changes. Think of displaying the weather information for an address saved in Scrivito. If the call to the weather service is slow, it should be cached. However, if an editor changes the address, they expect the information to update immediately. For this use case, the address itself can be used as part of the cache key:

class Location < Obj attribute :address, :string def weather_info cache_key = ["location-weather-info", id, address.parameterize] Rails.cache.fetch(cache_key, expires_in: 5.minutes) do   calculate_weather_info end   end private def calculate_weather_info   # A complex calculation or a call to a remote service… end end

This saves the weather for 5 minutes, but whenever an editor changes the address, the data is refetched instantly because the cache key itself has changed.

If the data to be cached depends on just a couple of pieces of information, this approach is a very good fit. However, often multiple data sources are at play, making it difficult to keep track of their dependencies and to debug errors. For example, if you add a field that lets the editor choose whether the temperature should be displayed in degrees Celsius or Fahrenheit, you would need to add this piece of information to the cache key to have the data updated if the unit changes.

Therefore, it may be more efficient to just recalculate the cached data whenever any piece of data changes. This is exactly the use case for the Workspace#cache_key method. This method returns a long string that changes whenever any number of objects in the working copy changes. This causes the cached data to be recalculated a bit more often, but you don’t need to manage the dependencies. Just let Rails and Scrivito take care of everything:

class Location < Obj … def weather_info     cache_key = [Workspace.current.cache_key, id, "weather-info"] Rails.cache.fetch(cache_key, expires_in: 5.minutes) do   calculate_weather_info end end … end