Full page and fragment caching
The method by which the WebSphere Application Server caches JSP files is based on how the JSP
is written. If the page output for a particular HCL 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 element,
consume-subfragments (CSF) along with the HCL Commerce controller servlet
(com.ibm.commerce.struts.ECActionServlet.class
in Version 9.0.0.x or
com.ibm.commerce.struts.v2.ECActionServlet.class
in
Version 9.0.1+ and if you are using Strut 2 configuration
com.ibm.commerce.struts.v2.ECActionStrutsServlet.class
) as
the servlet name. When the cache-entry is defined in this manner, then the page
output is cached in a manner known as full page caching. The big
advantage of using consume-subfragments with the controller
servlet is performance, but if there is personalized information on the page, such
as a mini shopping cart, then full page caching with fragments
can be used.
If the page output has sections that is user-dependent, then the page output is cached in a manner known as fragment caching. That is, the JSP pages are cached as separate cache entries, and get reassembled when they are requested. For fragment (JSP) caching, HCL Commerce has to execute the command to determine which JSP is to be executed before the dynamic caching mechanism can determine if the JSP can be served up from cache or not. The advantage of this method is flexibility, because different cache entries can be reassembled together to form a page, based on user information.
In some cases it is practical to exclude certain fragments from being cached with an entire page. Instead of being cached with the full page, the fragments are cached separatly. For example, if there is a personalized welcome message or a mini current order page, then the do-not-consume property is used. The parent entry is 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 intact for the entire page excluding the child fragment that is cached separately.
Full page caching
When the property element consume-subfragments (CSF) is used, the cache entry for the parent page (the one marked with CSF) will include all the content from all fragments in its cache entry, resulting in one big cache entry that has no includes or forwards, but rather, the content from the whole tree of entries.
When a servlet is cached, only the content of that servlet is stored. The cache includes place holders for any other fragments to which it includes or forwards. consume-subfragments (CSF) tells the cache not to stop saving content when it includes a child servlet. The parent entry (the one marked CSF) will include all the content from all fragments in its cache entry, resulting in one big cache entry that has no includes or forwards, but the content from the whole tree of entries. This can save a significant amount of application server processing, but is typically only useful when the external HTTP request contains all the information needed to determine the entire tree of included fragments.
For example, if the <cache-entry>
is defined as follows:
<cache-entry>
<class>servlet</class>
<name>strutsaction</name>
<property name="consume-subfragments">true</property>
<property name="save-attributes">false</property>
<property name="store-cookies">false</property>
<!-- StoreCatalogDisplay?storeId=<storeId> -->
<cache-id>
<component id="" type="pathinfo">
<required>true</required>
<value>/StoreCatalogDisplay</value>
</component>
<component id="storeId" type="parameter">
<required>true</required>
</component>
</cache-id>
</cache-entry>
Where strutsaction is
com.ibm.commerce.struts.v2.ECActionServlet.class
in HCL Commerce Versions
9.0.0.x, or
com.ibm.commerce.struts.v2.ECActionServlet.class
in Versions 9.0.1+ and if you are using Strut 2
configuration
com.ibm.commerce.struts.v2.ECActionStrutsServlet.class
.
Note that when the save-attributes
property is set
to false
, the request attributes are not saved with
the cache entry. When the store-cookies
property is
set to false
, the request cookies are not saved
with the cache entry.
In the preceding example, the cache servlet entry will contain a consumed include of StoreCatalogDisplay.jsp which is the JSP file forwarded by the StoreCatalogDisplay command.
Fragment cache
For a fragment to be cacheable it needs to be self-executable. Each dynamically included JSP file
has to have its own <cache-entry>
defined in
the cachespec.xml
file in order to be served up by
the dynamic cache when it receives a request. Otherwise, each
dynamically included JSP file will be re-executed for each request.
For example, consider if
StoreCatalogDisplay.jsp dynamically
includes CachedHeaderDisplay.jsp,
CachedFooterDisplay.jsp and
CachedStoreCatalogDisplay.jsp and you
only set up a <cache-entry>
for the
CachedStoreCatalogDisplay.jsp. Then,
when you request the StoreCatalogDisplay page,
the CachedStoreCatalogDisplay.jsp,
CachedHeaderDisplay.jsp and
CachedFooterDisplay.jsp files will get
executed if they are not cached. Here is an example of how to define
the <cache-entry>
for
CachedStoreCatalogDisplay.jsp:
<cache-entry>
<class>servlet</class>
<name>/AdvancedB2BDirect/ShoppingArea/CatalogSection/CategorySubsection/CachedStoreCatalogDisplay.jsp</name>
<property name="save-attributes">false</property>
<cache-id>
<component id="storeId" type="parameter">
<required>true</required>
</component>
<component id="catalogId" type="parameter">
<required>false</required>
</component>
</cache-id>
</cache-entry>
<cache-entry>
. Consider a sample Web page.
The following figure shows a personalized Item
Display page for the Advanced B2B store, that
contains user-specific information. There is not much value to
caching this whole page.
Example of a personalized page
The next figure 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
In some cases it is practical to exclude certain fragments from being cached with an entire page. Instead of being cached with the full page, the fragments are cached separate. For example, if there is a personalized welcome message or a mini current order page, then the do-not-consume property is used. The parent entry is 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 intact for the entire page excluding the child fragment that is 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.
Construct cache ID rules in order to cache the product page and the mini current order page. The mini current order page is unique for each user, therefore in order to cache it, the user's ID is the cache ID. Create a request attribute DC_userId so the user ID is used 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>false</required>
<not-value>-1002</not-value>
</component>
</cache-id>
</cache-entry>
<cache-entry>
<class>servlet</class>
<name>strutsaction</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>
Where strutsaction is
com.ibm.commerce.struts.v2.ECActionServlet.class
in HCL Commerce Versions
9.0.0.x, or
com.ibm.commerce.struts.v2.ECActionServlet.class
in Versions 9.0.1+ and if you are using Strut 2
configuration
com.ibm.commerce.struts.v2.ECActionStrutsServlet.class
.
When using the DC_userId request attribute to
construct the cache ID for the fragment cache entry in a
full page with fragment caching scenario,
use the snippet above in the component ID. This prevents the
full page entry from consuming the
fragment incorrectly resulting from a cache miss on the cacheable
fragment by the generic user. If the full page
consumes the fragment by the generic user, then any subsequent hits
to this full page by other users will incorrectly
get a cache hit on the full page.
The following image shows what the cache entries look like:
The following is the content of the MiniCurrentOrderDisplay
cache entry:
The following is the MiniCurrentOrderDisplay
which is excluded in the
StoreCatalogDisplay
cache entry:
Using the DC_userId in a full page with fragment caching
When using the DC_userId request attribute to construct the cache ID for the fragment cache entry in a full page with fragment caching scenario, use the following snippet in the component ID. This prevents the full page entry from consuming the fragment incorrectly resulting in a cache miss on the cacheable fragment by the generic user. If the full page consumes the fragment by the generic user, then any subsequent hits to this full page by other users will incorrectly get a cache hit on the full page. This will also improve system performance because it enables the generic user to obtain cache hits on the first request, instead of on the second hit where the DC_userId is populated with the value of -1002 from the session cookie.
<cache-entry>
<class>servlet</class>
<name>/path_to_JSP/mini-cart.jsp</name>
<property name="do-not-consume">true</property>
<property name="save-attributes">false</property>
<cache-id>
<component id="storeId" type="parameter">
<required>true</required>
</component>
<component id="DC_userId" type="attribute">
<required>false</required>
<not-value>-1002</not-value>
</component>