Let's assume that you have a complex CQ-based site with a vast number of pages. Each page consists of many complex components, but content served by these components is usually static -- so it doesn't change with each user visiting it. Think about an article component, image, header, footer and so on. It's a good idea to use a cache here, so CQ won't have to build the same content with every request. The most popular caching solution available with CQ is the dispatcher – the Apache module which saves visited sites into static files. It can improve performance of your site considerably. With one restriction: all your logged-in users have to share the same first name.
I'm not John
After configuring the dispatcher, an unforeseen thing happens. You forget about the small component displaying the name of the logged-in user. If the first user visiting your site logged-in as John, a page with his name was cached and now each successive visitor sees the 'Welcome John' message. Because of this component you cannot use the dispatcher, as it turned out that the page is dynamic and therefore cannot be cached. Wouldn't it be great to cache the almost whole page and leave this small component dynamic? How to do it?
Fill my placeholder
We have to replace the dynamic component with some kind of placeholder. It should be always the same, so can be cached. On the other hand, it should be somehow filled with the actual component content after the dispatcher serves the site. A natural choice here are the include tags.
The Apache HTTP server offers a simple markup language that allows to include part of a page before returning it to the client. A sample Server Side Include (SSI) tag looks as follows:
<!--#include virtual="/includes/header.html" -->
SSI is an ideal solution here. You can replace your component with an include tag. It'll be cached and evaluated every time the user requests a page. But how to get the component content (instead of the whole page)? Use Sling! You can request any component by using its path:
It's not that simple
Each include tag type has its drawbacks. AJAX is a common choice here, as it doesn't require any additional infrastructure, only a browser. Unfortunately, the list of the disadvantages is quite long:
- it may not work on feature phones (like Blackberry or Symbian devices),
- it may slow down a site (as a browser has to make a few requests),
- search engines won't index the dynamic content,
- the client may see an incomplete page with some blank spaces, filled eventually after the AJAX request.
SSI and ESI don't have these problems. The client or search engine will get the whole page and the include process is transparent. However, this solutions isn't perfect either. Each developer has to have Apache configured with SSI or some proxy that is able to interpret ESI (eg. Varnish or Rack-ESI).
Last but not least, all types of include tags has one, big disadvantage. Developers has to be aware which parts of a page will be included dynamically and these components should be written in a special way. It isn't easy to change the include tag type or to transform a component to its included version.
Fortunately, there is SDI
Sling Dynamic Include (SDI) is an OSGi service that is able to automatically replace configured components with the include tags. You can enter list of components (using their resource types) into SDI configuration, choose the include tag type (SSI, ESI or AJAX) and SDI will do the rest. It's totally transparent, you can use it with any components and developers don't have to write any code related to the includes. Every configuration change is seen immediately in the page content. You can enable it only on production machines or on the publish instances, etc. Its installation is easy (SDI is a OSGi bundle) and the service is stable and well-tested.
How does this brilliant thing work?
If you request a page, Sling will make a couple of sub-requests, one for each component. SDI filters all these sub-requests and if it sees that some of them regard the components configured as dynamic it'll replace the actual content with the include tag. As a result, the dispatcher will get a modified page containing include tags instead of dynamic components. After that, include tags are evaluated (by Apache, CDN or a browser), the other result is made and the user gets a complete page.
Is it fast?
I've made two tests. In the first one I measured speed of the CQ instance with the dispatcher but with no cache. I requested the main Geometrixx site and my CQ was able to handle about 130 requests per second. After enabling a cache and SDI (configured to replace the userinfo component with the SSI include), the number increased ten times, to about 1250 requests per second. Basically, it's the difference between rendering a whole page and rendering only a single component. If you'd like to dynamically include more components or if your components are more complex you may get worse results. The Apache HTTP server package contains a simple and useful benchmark tool called ab. You can use it to make your own tests.
The chart below presents the performance of both configurations vs the number of client threads:
How can I get it?
SDI, as well as its documentation, are available on our Github. Enjoy! You may also ask questions using the comments form below.
Sling Dynamic Include is an open-source tool that replaces selected components with different types of include tags and therefore allows to put dynamic content into a page cached by the dispatcher. If you have an almost-static-site with a simple dynamic component and you want to cache it, SDI will be a perfect tool to do that.
Thanks to Przemyslaw Pakulski for idea and support.