In order to allow a command call be intercepted by the dynamic cache, the command must be written to the WebSphere Command Framework with its implementation class extending from CacheableCommandImpl (in the com.ibm.websphere.command package). To simplify command writing for command-based invalidation, WebSphere Commerce has updated the abstract classes, ControllerCommandImpl and TaskCommandImpl to extend from CacheableCommandImpl so that any commands extend from these abstract classes would also extend from CacheableCommandImpl and therefore be eligible for command-based invalidation.
When writing such commands, it is also important to know what the invalidation IDs are, and understand the invalidation rules that intercepts calls to the commands. Since invalidation IDs are generated based on methods and fields present in the command as input parameters, all the methods that are required to construct the invalidation IDs, should be provided in the command interface and be implemented.
An example of using command invalidation in WebSphere Commerce
The following example shows how WebSphere Commerce uses command invalidation. When the command DeleteMemberGroupMemberCmdImpl which deletes a particular member belonging to a particular member group is executed successfully, the dynamic cache will invalidate the cache-entry defined in the invalidation rule. In this example, it is defined as "DC_userId:userId" where userId is the value being returned from the getMemberId method. (See Cache servlet filtering for the details of caching the member group information in WebSphere Commerce.) For example, DC_userId:-1000, DC_userId:-1001, and so on. This command has a get method, getMemberId(), that retrieves the user ID that is being deleted and this method is used in computing which cache entries with a dependency ID based on a user ID gets deleted. The same logic applies for the command AddMemberGroupMemberCmdImpl which also has a get method, getMemberId():
<cache-entry> <class>command</class> <name>com.ibm.commerce.membergroup.commands.AddMemberGroupMemberCmdImpl</name> <name>com.ibm.commerce.membergroup.commands.DeleteMemberGroupMemberCmdImpl</name> <invalidation>DC_userId <component type="method"id="getMemberId"> <required>true</required> </component> </invalidation> </cache-entry>
Note:
All the above invalidation rules are shipped with WebSphere Commerce in the sample cache policy. You can find more sample invalidation rules in the WebSphere Commerce installation directory under the /samples/dynacache/invalidation subdirectory. Please see the README file entitled "Sample invalidation cache policies for Dynacache" for more information on how to incorporate the invalidation rules into the cachespec.xml file.
Cache invalidation example
The following example shows how to set up caching policies in the cachespec.xml file to cache the ProductDisplay JSP page for the Consumer Direct business model in WebSphere Commerce and how to invalidate the cache entry by defining the invalidation rules in the same XML file. The example defines multiple dependency IDs along with the cache ID generation rule for the JSP file. Each dependency ID is used to invalidate the cache entry when the cache entry is updated under different circumstances. This example only shows a subset of policies required to invalidate the CachedProductDisplay JSP. For a complete example and detailed information, see the README file in the WC_installdir/samples/dynacache/invalidation directory.
<cache> <cache-entry> <class>servlet</class> <name>/ConsumerDirect/ShoppingArea/CatalogSection/CatalogEntrySubsection/CachedProductDisplay.jsp</name> <property name="save-attributes">false</property> <!-- Cache ProductDisplay.jsp --> <cache-id> <component id="storeId" type="parameter"> <required>true</required> </component> <component id="catalogId" type="parameter"> <required>true</required> </component> <component id="productId" type="parameter"> <required>true</required> </component> <component id="DC_lang" type="attribute"> <required>true</required> </component> <component id="DC_curr" type="attribute"> <required>true</required> </component> <component id="DC_porg" type="attribute"> <required>true</required> </component> <component id="DC_cont" type="attribute"> <required>true</required> </component> <component id="DC_mg" type="attribute"> <required>true</required> </component> </cache-id> <!-- Used for invalidating the product display cache entry --> <!-- that belongs to a specific store --> <dependency-id>storeId <component id="storeId" type="parameter"> <required>true</required> </component> </dependency-id> <!-- Used for invalidating the cache entry of a specific product --> <dependency-id>productId <component id="productId" type="parameter"> <required>true</required> </component> </dependency-id> <!-- Used for invalidating the product display cache entry --> <!-- that belongs to a specific catalog in the store --> <dependency-id>storeId:catalogId <component id="storeId" type="parameter"> <required>true</required> </component> <component id="catalogId" type="parameter"> <required>true</required> </component> </dependency-id> <!-- Used for invalidating the product display cache entry --> <!-- that is under a specific contract --> <dependency-id>contractId <component id="DC_cont0" type="attribute"> <required>true</required> </component> </dependency-id> </cache-entry> <cache-entry> <class>command</class> <sharing-policy>not-shared</sharing-policy> <name>com.ibm.commerce.catalogmanagement.commands.AddCatalogDescCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.UpdateCatalogDescCmdImpl</name> <!-- ********************************************************* --> <!-- Invalidate all the product page cache entries that --> <!-- might be affected when the catalog description is changed --> <!-- ********************************************************* --> <invalidation>storeId:catalogId <component id="getStoreId" type="method"> <required>true</required> </component> <component id="getCatalogId" type="method"> <required>true</required> </component> </invalidation> </cache-entry> <cache-entry> <class>command</class> <sharing-policy>not-shared</sharing-policy> <name>com.ibm.commerce.catalogmanagement.commands.ListpriceAddCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.ListpriceDeleteCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.ListpriceUpdateCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.OfferAddCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.OfferDeleteCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.OfferUpdateCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.ProductAttributeUpdateCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.AttributeValueUpdateCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.AddListpriceCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.DeleteListpriceCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.UpdateListpriceCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.AddOfferCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.DeleteOfferCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.UpdateOfferCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.UpdateAttributeCmdImpl</name> <name>com.ibm.commerce.catalogmanagement.commands.UpdateAttributeValueCmdImpl</name> <!-- ********************************************************* --> <!-- Invalidate the specific product page cache entry when the --> <!-- product is updated --> <!-- ********************************************************* --> <invalidation>productId <component id="getCatentryId" type="method"> <required>true</required> </component> </invalidation> </cache-entry> <cache-entry> <class>command</class> <sharing-policy>not-shared</sharing-policy> <name>com.ibm.commerce.contract.commands.ContractSuspendCmdImpl</name> <name>com.ibm.commerce.contract.commands.ContractTCDeployCmdImpl</name> <!-- ********************************************************* --> <!-- Invalidate all the product page cache entries under a --> <!-- specific contract --> <!-- ********************************************************* --> <invalidation>contractId <component id="getContractId" type="method"> <required>true</required> </component> </invalidation> </cache-entry> <cache-entry> <class>command</class> <name>com.ibm.commerce.tools.devtools.store.commands.StoreProfileUpdateCmdImpl</name> <name>com.ibm.commerce.tools.devtools.flexflow.ui.commands.impl.FlexflowUpdateCmdImpl</name> <name>com.ibm.commerce.store.commands.StoreOpenCmdImpl</name> <name>com.ibm.commerce.store.commands.StoreCloseCmdImpl</name> <!-- ********************************************************* --> <!-- Invalidate all the product page cache entries that --> <!-- belong to the store when the store is updated --> <!-- ********************************************************* --> <invalidation>storeId <component id="getStoreId" type="method"> <required>true</required> </component> </invalidation> </cache-entry> <cache-entry> <class>command</class> <sharing-policy>not-shared</sharing-policy> <name>com.ibm.commerce.catalogimport.commands.CatalogImportJobAddCmd</name> <!-- ********************************************************* --> <!-- Invalidate all the product page cache entries that --> <!-- belong to the store when the store catalog is updated --> <!-- ********************************************************* --> <invalidation>storeId <component id="getStoreId" type="method"> <required>true</required> </component> </invalidation> </cache-entry> </cache>
Cache Invalidation API (DynaCacheInvalidation)
The WebSphere Commerce scheduler periodically calls the DynaCacheInvalidation command to process the records in the CACHEIVL table and then calls the dynamic cache invalidation API to invalidate the cache entries. The startTime is retrieved from the checkpoint file. The checkpoint file is written into the instance_name/cache directory. This directory gets created the first time that DynaCacheInvalidation is executed. The timestamp of the last record is written into the checkpoint file during the wind up stage of the command.
CACHEIVL table
The rules for the DynaCacheInvalidation command when processing the CACHEIVL table are as follows:
- The "clearall" string value in the TEMPLATE or DATA_ID columns of the CACHEIVL table is used by DynaCacheInvalidation to clear the cache by dynamic cache invalidation API (clear).
- If the TEMPLATE column is set, then the DynaCacheInvalidation command calls the dynamic cache invalidation API (invalidateByTemplate) and uses the name as the template ID (for example, /webapp/wcs/stores/AdvancedB2BDirect/ShoppingArea/CatalogSection/CategorySubsection/CachedStoreCatalogDisplay.jsp). If the "clearall" string value (which is case insensitive) is found in TEMPLATE column, then the DATA_ID column is ignored and the DynaCacheInvalidation command will clear cache; if the TEMPLATE column is not empty, the command will invalidate by the template ID, ignoring the DATA_ID column.
- If the DATA_ID column is set and the template name is not set, then the DynaCacheInvalidation command calls the dynamic cache invalidation API (invalidateById) and uses the DATA_ID as the ID (for example, StoreCatalogDisplay:storeId:10151). If the TEMPLATE column is empty and the "clearall" string value is found in the DATA_ID column, then the command will clear cache.
- When the dynamic cache invalidation API is called, it invalidates the cache entries.
Clear parameter on DynaCacheInvalidation
An alternate mechanism to clearing the cache using the "clearall" string value in the CACHEIVL table as described above is to use the "clear" parameter in the DynaCacheInvalidation API:
- Place the execution view JSP located at WC56_installdir/samples/dynacache/CacheInvalidation.jsp into the store directory (for example, WC56_installdir/installedApps/cell_name/WC_instance_name.ear/WAR.war/AdvancedB2BDirect)
- Log in as a user with administrative rights to tools or stores pages.
- From the same browser in step 2 above, issue the following command: http://node_name/webapp/wcs/stores/servlet/DynaCacheInvalidation?clear=true
The Cache Invalidation Report Page gets displayed with all cache entries cleared.
Invalidation in external caches
Result caches can be pushed to external caches such as IBM HTTP Server or Edge Server. Invalidation will occur in external caches when:
- The cache is congested
- Time out occurs
- Invalidation message is sent from WebSphere Application Server
Web server plug-in cache
For WebSphere Application Server 5.0, the Web server plug-in contains a built-in Edge Side Include (ESI) processor. The ESI processor has the ability to cache whole pages, as well as fragments, providing a higher cache hit ratio. The cache implemented by the ESI processor is an in-memory cache, not a disk cache. Therefore, the cache entries are not saved when you restart the Web server.
When a request is received by the Web server plug-in, it is sent to the ESI processor, unless the ESI processor is disabled. (It is enabled by default.) If a cache miss occurs, a Surrogate-Capabilities header is added to the request and the request is forwarded to WebSphere Application Server. If the dynamic servlet cache is enabled in the application server and the response indicates that the cache entry can be cached on the Web or Edge Server, the application server returns a Surrogate-Control header in the response to the WebSphere plug-in.
The ESI processor is configurable through the WebSphere Web server plug-in configuration file, plugin-cfg.xml. For example:
<Property Name="esiEnable" Value="true"/> <Property Name="esiMaxCacheSize" Value="1024"/> <Property Name="esiInvalidationMonitor" Value="true" />
where:
- esiEnable is used to disable the ESI processor by setting the value to false. ESI is enabled by default.
- esiMaxCacheSize is the maximum size of the cache in 1K bytes. The default maximum size of the cache is 1 MB. If the cache is full, the first entry to be removed from the cache is the entry that is closest to expiration. (The default expiration time for ESI cache entries is 24 hours.)
- esiInvalidationMonitor specifies whether or not the ESI processor should receive invalidations from the application server.
There are three methods by which the cache entries are removed from the ESI cache:
- An entry's expiration timeout is invoked.
- An entry is purged to make room for new entries.
- The application server sends an explicit invalidation for a group of entries.
In order for this mechanism to be enabled, the esiInvalidationMonitor must be set to true and the DynaCacheEsi.ear application which is located in the installableApps directory must be installed on the application server.
Note: The DynaCacheEsi.ear should use the VH_instance_name as the virtual host.
For more information on the ESI processor, see the topic "Configuring Edge Side Include caching" in the WebSphere Application Server Information Center.
Simple file servlet
By default, WebSphere Commerce does not cache static data such as images and HTML. This is set by setting the system property com.ibm.servlet.file.esi.timeOut to a value of 0 (zero). If you want to enable images and HTML caching, you can either:
- Set up the cache entry in the cachespec.xml file for the SimpleFileServlet. Setting up the cache entry in the cachespec.xml is a better approach because controlling what is being cached from WebSphere Application Server also gives the you the ability to invalid the cache entries when needed.
Note that you are storing possibly all the images as cache entries at the application server which may compromise performance when the maximum number of cache entries is reached. It is recommended that a lower priority value relative to other cache entries be set for the SimpleFileServlet cache entry.
- Modify the system property com.ibm.servlet.file.esi.timeOut setting to a non-zero timeout value.
A reason why invalidation is required for some images file is that in some business models, the logo and banner can be easily modified.
For example:
<cache-entry> <class>servlet</class> <name>com.ibm.ws.webcontainer.servlet.SimpleFileServlet.class</name> <property name="EdgeCacheable">true</property> <cache-id> <component id="" type="pathinfo"> <required>true</required> </component> </cache-id> <dependency-id> <component id="" type="pathinfo"> <required>true</required> </component> </dependency-id> <timeout>1800</timeout> </cache-entry>
Here is an example of the cache entry for invalidating the images.
<cache-entry> <class>command</class> <name>com.ibm.commerce.tools.devtools.store.commands.StoreLogoUpdateCmdImpl</name> <invalidation>ConsumerDirect/images/logo.gif</invalidation> </cache-entry> <cache-entry> <class>command</class> <name>com.ibm.commerce.tools.devtools.store.commands.StoreBannerUpdateCmdImpl</name> <invalidation>ConsumerDirect/images/banner.gif</invalidation> </cache-entry>