When determining a caching strategy for WebSphere Commerce, the key issues that need to be resolved are:
- Which pages should be cached
- Cache whole pages or page fragments
- Where caching should take place
- How to invalidate the cached data
Which pages should be cached
When determining which Web pages should be cached, good candidates for caching are pages that:
- Are accessed frequently
- Are stable for a period of time
- Contain contents that can be reused by a variety of users
A good example would be catalog display pages.
Cache whole pages or page fragments
All Web pages consist of smaller and simpler fragments. An example of a page fragment could be a header, sidebar, footer or an e-Marketing Spot. Breaking a Web page into fragments or components makes more caching possible for any page, even for personalized pages. Fragments should be designed to maximize their reusability.
Dynamic cache service allows WebSphere Commerce to support caching of Web pages, and caching of fragments of pages. Caching a whole Web page simply means that the entire page is cached as big cache entry including all the content from all fragments that have no includes or forwards. This can save a significant amount of application server processing but is typically useful when the external HTTP request contains all the information needed to find the entry.
Web pages can be cached using whole page caching or fragment caching or a combination of both methods. If Web pages are broken into different fragments and the fragments are cached individually, then the fragments can be reused for a wider audience. When a Web page is requested, then different fragments are reassembled to produce the page.
So when determining caching strategy, whether the Web page should be cached as a whole or as a fragment is another important factor. However if the page output has sections that are user-dependent, then the page output is cached in a manner known as fragment caching. That is, the JSP pages are cached as separate entries and will be reassembled when they are requested. If the page output for a particular WebSphere Commerce command always produces the same result based on the URL parameters and request attributes, then this page output can be cached with the cache-entry using the property consume-subfragments (CSF) , and the WebSphere Commerce's store controller servlet (com.ibm.commerce.server.RequestServlet) as the servlet name. When the cache-entry is defined in this manner, the page output is cached the same way as in WebSphere Commerce Version 5.4 which is known as whole page caching.
The big advantage of using consumed-sub fragments with the controller servlet is performance but if this mechanism is used, then the page output cannot contain personalized information.
For fragment (JSP) caching, WebSphere Commerce has to execute the command (controller, task and view commands) to identify which JSP is to be executed before dynamic cache can determine whether the JSP can be served from the cache or not. The advantage of this method is flexibility, because different cache entries could be reassembled to form a page based on user information.
Note: In order for a fragment to be cacheable, this fragment has to be executable on its own. That is, it can be executed independently of any other fragments of the Web page.
Consider a sample Web page. The following figure shows a personalized Item Display page for the Advanced B2B Direct store, that contains user-specific information. There is not much value to caching this whole page.
Example of a personalized page
The next figure below shows the same page broken down into fragments based on reusability and cacheability. In this example, the page can be broken into different fragments:
- Header and Requisition list - same across all customers.
- Item Display - same across all customers for this product ID.
- Sidebar and Request for Quote (RFQ) - same across all customers with the same user roles.
- Add to order - same across all customers with the same contract IDs.
Example of a personalized page fragmented for caching
In this case, all of the fragments become reusable or cacheable for a larger audience. Only fragments that are not cacheable need to be fetched from the backend, reducing server-side workload and improving performance.
Cache whole page excluding certain fragments
When there is small portion of a Web page that contains personalized information, for example, a personalized welcome message or a mini current order page, the do-not-consume property could become useful (Note: this property is only available with WebSphere Application Server version 5.0.2 + PQ81651 and later). To use this property, the parent entry would be marked with the property consume-subfragments and the child fragment that contains the personalization area would be marked with this do-not-consume property. With this combination, the performance gain of whole page caching remains intacted for the entire page besides the child fragment where it will be cached separately apart from its parent.
The following Web page which is a common product page and its sidebar dynamically includes a fragment (MiniCurrentOrderDisplay.jsp) that displays a personalized mini current order page.
In order to cache the product page and the mini current order page, we need to construct cache ID rules for them. Since the mini current order page is unique per user, therefore in order to cache it, we have to use the user's ID as the cache ID. WebSphere Commerce's cache filter (please refer to Cache filter section for more details) set up a request attribute DC_userId, so we can use that as the cache ID.
Here is an example of the cachespec.xml:
<cache-entry> <class>servlet</class> <name>/AdvancedB2BDirect/ShoppingArea/CurrentOrderSection/MiniCurrentOrderDisplay.jsp</name> <property name="do-not-consume">true</property> <property name="save-attributes">false</property> <cache-id> <component id="DC_userId" type="attribute"> <required>true</required> </component> </cache-id> </cache-entry>
<cache-entry> <class>servlet</class> <name>com.ibm.commerce.server.RequestServlet.class</name> <property name="store-cookies">false</property> <property name="save-attributes">false</property> <property name="consume-subfragments">true</property> <cache-id> <component id="" type="pathinfo"> <required>true</required> <value>/StoreCatalogDisplay</value> </component> <component id="storeId" type="parameter"> <required>true</required> </component> <component id="catalogId" type="parameter"> <required>true</required> </component> </cache-id> </cache-entry>
The following image shows what the cache entries look like:
The following is the content of the MiniCurrentOrderDisplay cache entry:
The following is the content of the StoreCatalogDisplay cache entry, which excludes the MiniCurrentOrderDisplay entry:
Where caching should take place
Theoretically, caching should take place in the tier closest to the user. In reality, other factors such as security and user specific data may influence the choice of the best place to cache the content. To maximize the benefit of dynamic caching, elements of a page should be fragmented as finely as possible so that they can be cached independently in different cache entries.
For example, the non-user specific, non-security sensitive fragments are generally useful to many users, and can be cached in a more public space and closer to the user. The security sensitive data should be cached behind the enterprise firewall.
How to invalidate the cached data
The hardest part of caching is to guarantee the accuracy of the content. The mechanism that is used to identify and update pages or fragments that are no longer valid is known as invalidation.
WebSphere Application Server dynamic cache service provides invalidation techniques that are rule-based, time-based, group-based, and programmatic. The service can also invalidate any remote caches that were configured externally.