Ognjen Regoje bio photo

Ognjen Regoje
But you can call me Oggy


I make things that run on the web (mostly).
More ABOUT me and my PROJECTS.

me@ognjen.io LinkedIn

Using CSS to more efficiently cache user-specific content

#caching #css #rails #technical

When caching an object you have to supply a key. When caching a product partial that key would contain the ID and the timestamp when the product was last updated.

If the product partial contains information that’s stored outside of the product you have to include that in the cache key. For instance, if the product partial had the users’ area, the cache key would contain the product ID, last updated timestamp and the area.

That means that you effectively have to have a cache entry for each unique area that users can be in. That also means that once the product changes you have to invalidate all of those entries.

This can become very inefficient very quickly. For instance, if there are many possible regions that users can be in. It’s particularly harmful if the difference between the versions is very small.

This is most frequently tackled by JavaScript populate the element either on page load or through a new request. The downside of this is that there is often a brief period when the element is empty. If a request is triggered, it means an additional request that has to be served that doesn’t do much.

Instead, there are ways to do this by generating CSS and rendering it in the head that can make this more efficient.

Using :after

Using :after is suitable when just a word or two is needed. For instance, if you just need to show the users’ neighbourhood name.

The cached partial would have html like this:

<span class="user-neighbourhood"></span>

This would be populated with CSS placed in the head that has the neighbourhood set in the content of the :after for that class:

<style>
.user-neighbourhood:after {
  content: '<%= current_user.neighbourhood_name%>';
}
</style>

By doing this, the content is there from the start, there is no flash, there are no additional requests and JavaScript doesn’t execute.

This is very useful in cases where there would have to be hundreds of cache entries that all differ by one word. Not only would there be lots of cache entries using up space but the number of misses would be much higher as well.

I used this in the search bar that has cached popular products and categories that shows a couple of words of user-specific content.

Using classes

Selectively showing classes is useful when there is slightly more content.

For example, if you needed to show the shipping options to the users area. You can render an element for each area that has a class that hides it and a class that identifies it (and that will be used to show it.)

The HTML would look like this:

<div class="delivery-info area-1-info">...</div>
<div class="delivery-info area-2-info">...</div>

The base CSS would look like this:

.delivery-info {
  display: none;
}

The generated CSS would be:

<style>
.area-<%= current_user.area_id%>-info {
  display: block !important;
}
</style>

That way, when the page is loaded the correct info will be immediately shown.

This worked much better then accomplishing a similar thing using JavaScript. Firstly, JavaScript cannot trigger immediately no matter how fast. There would almost always be a flash when the elements appear.

The downside of this is that the transferred html would be slightly larger. If you keep the elements to their bare minimum this is manageable.

I used this where we had to show the delivery information for the user that was dependant on the product supplier. Using JavaScript here would have meant that delivery information for all suppliers would have to be requested. This meant extracting all the suppliers on the page, querying for all the delivery settings based on the current users location, then placing them all on the page.

Using CSS was much more efficient.

Downsides

  1. It couples appearance and data
  2. It renders CSS inline
  3. It slightly increases the size of transferred HTML