[00:01] performance optimization for web applications with many visitors. Instead of performing all the PHP processing and database work to render the same page over and over again, you can cache the resulting HTML once and serve that for a [00:17] period of time. And if you want to get even more out of full page caching, edge site includes is a standard that has a clever approach to this topic. It allows storing different parts of a page as their own cache entries in such a way [00:32] that they can even have different cache lifetimes and dynamic invalidation without affecting each other. Mo I am Benjamin and in this video I will look at how Shopware 6.7 implemented a multi-step full page cache in practice [00:48] that yields a significant performance improvement. But let's walk back and quickly review traditional full page caching using Shopware as an example. In the simplest case, caching the HTML output of a web application can be based [01:03] output of a web application can be based on the URL and HTTP request method. This assumes that there are no differences in the HTML response output across different users. For example, when there are users different based on the browser [01:17] language, when they are locked in or have guest status, or when different cookies or the session affects the output as soon as the page is generated [01:29] by the application, it could be cached for as short as a few minutes, uh, hours or even days so that all consecutive users visiting the same URL get the [01:41] response page from the full page. cache circumventing repeated database calls or calls to external services. This saves a lot of performance. In the case of Shopware, when stock or price of the product [01:56] changes, the cache gets invalidated and the next visitor gets the uncashed page again. This rerenders the main content but also header navigation completely from scratch. Shopware 6.7 now uses edge sites [02:12] includes to achieve different cache entries and lifetimes for the header and footer and the main content. An uncached product page can then use a cached header and footer and save the time generating and rendering them. For a [02:27] naive page cache implementation, this can cause a problem of stacking different cache lifetimes and during invalidation. Edge set includes cleverly work around this problem by storing only a reference [02:39] to the cached entries with the ESI include tag. That means even when the product page output is cached for a long time, it is always rendered with the newest header and footer output from the cache. Let's look at how Shopware [02:55] implements this in code and how it changed between versions 6.6 6 and 6.7 to better understand edge site includes and how they improve performance. Shopware uses the Symfony framework under the hood and Symfony comes with a [03:08] under the hood and Symfony comes with a fullyfledged HTTP cache and ESI support on the PHP level but it can also be swapped for a more complete solution based on the varnish reverse proxy. The entry point to this code and Shopware is [03:22] in the storefront base HTML Twick template. If we scroll down, we see the Symfony code to render an ESI tag for the front end header. Here there's a [03:35] function called render ESI. This renders the ESI include tag that you've seen the ESI include tag that you've seen before. Here in this case, the ESI tech before. Here in this case, the ESI tech is for the front end header uh route. [03:48] And if we look in the navigation controller of Shopware, this maps to the controller called header. And the header is loading all data from this header loader object and renders the header [04:02] loader object and renders the header template. differs from how it worked before. In Shopware 6.6 uh we already see the code with the feature flag for the ESI support but we [04:19] also see what the old code did. So in case of Shopware 6.6 six and all versions before instead of rendering the ESI tag it's actually rendering the header output here including the navigation and in case of Shopware the [04:35] navigation can take a few milliseconds uh 50 milliseconds 100 milliseconds and even up to a few hundred milliseconds in some cases in Shopware 6.6 six. The data [04:47] for this content is loaded by a class called generic page loader. The generic called generic page loader. The generic page loader uh if you look at it, you page loader uh if you look at it, you see it runs some code if the feature [05:01] flag cache rework is not activated. You see it calls the header loader load function. It calls the respective footer loader loading all the data it needs for [05:14] loader loading all the data it needs for a navigation and footer of the page uh in every request. Going back to the navigation controller, we can see in case of the ESI rendering um the header loader is [05:29] offloaded to this new controller here. So what this change uh allows Shopware So what this change uh allows Shopware to do is to get rid of all this code here and um you don't even need to run this anymore. All the database and [05:43] processing work to generate the header and the footer is offloaded to the new and the footer is offloaded to the new controller that can be cached using ESI. Let's switch to the current branch uh for 6.7 and we see the same code for the [05:58] generic page loader here and see how it fits on one page. Um all the code that was um used before to generate the navigation is now not needed anymore. [06:10] obviously most interested in the performance impact of this change. We can look at this in tight ways profiler can look at this in tight ways profiler and see an aggregated trace of 100 [06:23] different product pages where we can see the header page loader and the navigation page loader. Um, as you might remember, they were previously loaded in [06:36] the generic page loader of every page of Shopware 6.6 six. this data takes 40 milliseconds. And then we can also see [06:52] that rendering this data in the twig template again takes template again takes um about another 65 second milliseconds. So in total we have 110 milliseconds of rendering the navigation. And by [07:09] introducing caching for the header, uh, we can make sure that this is only rendered once. And we save 110 milliseconds for every new additional milliseconds for every new additional product page, every category page until [07:24] the cache for the navigation runs out and it needs to be regenerated and it needs to be regenerated once. We can compare this to a trace afterwards um by using the compare feature in tightways again comparing [07:39] against 100 uh traces um of product pages. And we can see here that there's pages. And we can see here that there's a performance drop of around 250 milliseconds between version 6.6 and 6.7. Not all of this can be attributed [07:55] to this ESI header change, but a good uh chunk of it um is related to this new feature. When we switch the compare order between both traces, we can see order between both traces, we can see how much the improvement is. So here in [08:10] how much the improvement is. So here in the old request, we see that the generic page loader is 30% uh slower in the old code because of this change. And um removing this code um reduces the the time by yeah 48 [08:26] milliseconds. And the same is true for the base navigation. We saw that before the base navigation. We saw that before around 65 milliseconds of rendering here around 65 milliseconds of rendering here makes the um Shopware 6.6 rendering much [08:39] slower. This was a quick rundown of how edge site includes work in Shopware and Symfony to affect meaningful performance improvements. If you're interested in more content, a few more suggestions will pop up on your screen. If you would [08:54] like to be informed about new content, please subscribe to our newsletter on tideway.com. The link is in the description.