Varnish ESI concept is great. By default, varnish caches whole html page. Therefore, a page is always the same, for all users.
But there are a few use cases when this is not ideal.
Sometimes, your page can be cache for hours but there is small part that you would like to refresh more often, every 15 minutes for instance. Let’s say your home page shows the weather or the stock market. You want data to be up to date but you don’t want to refresh the whole page every 15 minutes as it would add more stress on your backend.
Another classic example is the e-commerce website. Catalogue and product pages are good candidates for varnish cache and are pretty often pretty expensive to build. But the moment the user add a product to his cart, varnish must be turn off as we cannot send this user the same page as everyone anymore. We need to show him the products he selected.
ESI allows to define “holes” in the page. Varnish will cache everything but will make subqueries to the backend to fill the holes.
<header> <h1>page title</h1> <div class=“cart”> <esi:include src=“/user/cart” /> </div> </header>
And /user/cart could return:
<ul> <li> Product 1</li> <li> Product 2</li> </ul>
This way, you still get most of your page cache, reduce the load on your backend and keep a dynamic website. What’s wrong then ?
To me, there are two pitfalls to know about before using ESI.
Error management
I found out about this first one the hard way, in production. What happens when the url for your ESI (/user/cart in the previous example), for what ever reason, fails ?
In my case, the backend returned an error page with 500 error code. And what Varnish did ? Well, it did it’s job. It took the answer from the backend and fill the hole.
The website ended up with a piece of error page in the header (only the part that could fit) and the rest looked totally normal (Hopefully, you customized your error page and do not show a Guru Meditation ;-)
+-------------------------------------------------------------+ | | | Sorry, this website encountered an error. | | Please try again later. | | | +-------------------------------------------------------------+ | | | +---------------+ +---------+ +---------+ +---------+ | | | Filters | | | | | | | | | | | | | | | | | | | | +---------+ | +---------+ +---------+ +---------+ | | | | Product 1 Product 2 Product 3 | | | +---------+ | | | | | | | | +---------+ | +---------+ +---------+ +---------+ | | | | | | | | | | | | | +---------+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +---------+ +---------+ +---------+ | | | | | | | | +---------+ +---------+ +---------+ | | | | | | | | | | | | +---------------+ | | | | | | | | | | | | | | | +----------------------+---------+---+---------+--+---------+-+
I search for a solution but there is apparently no way to deal with it.
The site if not functional and if this ESI returns an error, chances are that another page not in cache would also fail.
I would like to be able to catch this error and return a complete error page. This is just not possible. The main request and the sub one are independent of each other. This is a no go for me.
You could have a dedicated error page for this particular ESI url with fallback content but it adds complexity to your error management and the end result still feels awkward as a user.
We could even imagine some javascript in the error page to wipe the whole page content and only show the error message but we now go way too fare just to manage some occasional 500 errors.
Overuse and performance
The second risk with ESI is performance. ESI goal, just as Varnish in general, is to improve your website performance. However, if you use ESI too much or in a wrong way, you can easily have opposite results.
By default, Varnish caches your page and no request goes to your backend server. If for example, you decide to use ESI and include 5 blocks of personalized content on the page with 5 ESI having no cache. Now, for each request of your page, 5 requests goes to your backend. if theses blocks are present on all your pages, you just multiplied your web traffic by 5.
I do agree these request will be smaller and have less impact than a whole page as you just render one block of content. However, you loose the economy of scale.
For each of these ESI request, you have to bootstrap your web framework, you might have some config to read on disk, you need to load your current user from the database… All this work is shared when you build a whole page but with ESI, it has to be repeated for each fragment. Maybe it is negligible and ESI still provide better performance but you need to at least think about it and do the math. Otherwise, you might degrade your site performance.
For this two reasons, most of the time, I consider Varnish ESI are not worst it. For many use cases, you can achieve the same functionality with Ajax calls and get overall a better user experience, better performance server side easier setup and maintenance with only a very small performance effect client side.
PS:
Another use case is simply to case a fragment that is identical on many pages. To me, the risk of breaking display in case of an error and the work requires to extract these fragment to specific urls is not worst it. Your page will be cached anyway and not call many times. Backend caches should be use for this use case (memcached / redis/ file…) and it is often already managed by your framework / CMS.