Implement Caching in the Web-Server Layer
Learning Objectives
After completing this unit, you’ll be able to:
- List the Salesforce B2C Commerce architectural layers.
- Describe the general logic for when a page contains markers for remote includes.
- Describe a scenario on a web server where you combine static and dynamic data.
- Explain why the script API is preferable to templates for caching.
Strategize Caching
The more quickly your site loads, the more likely it is to keep a shopper engaged and lead to a sale. As seconds tick past, other sites and activities clamor for their attention. Each step through an implementation’s architectural layers costs time. An effective caching strategy moves content delivery closer to the shopper, which makes for a faster and more reliable website.
In the B2C world, you want to show data as fast as possible. When a shopper clicks a product tile, for example, the product details must come all the way from the server. The page layout, however, is the same from product to product. If you save the layout in cache on the client, at least part of the page serves fast. Every little bit counts.
Cache is a reserved storage location that collects data to help websites, browsers, and apps load faster. Whether it’s a computer, laptop, phone, web browser, or app, you have lots of ways to implement caching. Caching affects all layers of an application’s architecture. The more layers an application traverses, the slower the response time and the lower the scalability of the app.
Caching at Cloud Kicks
Linda Rosenberg, an admin at Cloud Kicks, a high-end sneaker company, wants to ensure her B2C Commerce storefront uses every means possible to improve performance. She takes a look at the Headless Implementation Strategies for Salesforce B2C Commerce Trailhead module to understand distributed and in-memory caching for static content and the API layer.
She knows that setting up caching with Salesforce B2C Commerce means a lot more than adding <iscache>
tags to a few templates. B2C Commerce manages caching in these architectural layers using different cache methods.
Layer |
Caching Method |
Web-server |
Page Cache |
Application-server |
Custom Cache (CacheMgr) |
Storage |
Custom Attributes or Custom Objects |
Caching is so important that each contributor must adopt a mindset whereby they automatically consider how their piece impacts the whole. Let’s look at what can happen with a simple application change.
Cloud Kicks marketing wants to show a heart icon with a red outline for each product on a search result. They want a solid, red heart icon to display for items on the shopper’s wishlist.
Vijay Lahiri, Cloud Kicks developer, simply updates the search results page to render HTML that represents the wishlist state of a given product.
After thinking more about it, Vijay questions his first approach. He doesn't want to bog down the app. While this approach is functionally correct, the application must generate separate search results for each shopper who searches for a product. That means the application must dynamically calculate the information for each product tile, traversing all architectural layers… for every shopper.
Vijay remembers that wishlist contents don’t change a lot, so he changes his approach to:
- Expose the wishlist contents, such as shopper information or login state in the header, as an HTML data attribute in a preexisting dynamic include.
- Update the heart icon status using client-side JavaScript.
On the server side, the new approach adds almost no cost, and the operation on the client side is relatively minor. The approach uses the available layers to achieve great performance and better scalability compared to Vijay’s original, server-side solution.
Web-Server Layer
Page caching happens in the top architectural layer, which is the web-server layer. Server-side includes (SSI) are placeholders in the page for which the web-server layer issues new requests. Each request can have its own cache policy. For example, on a static content page that’s entirely cacheable, Vijay includes dynamic information, such as the shopper’s login state, using a remote include.
While the main request might be cached for a few days, another request is issued that dynamically retrieves the shopper information to display in the header. The sub-request isn’t cached. The web-server tier inserts the dynamically computed piece of the page into the cached frame, resulting in a page that contains both cached and uncached elements.
The page cache is a key-value store, which is a table with references. The key is the full URL, including the query string, while the value is a reference to the cached page. The page can contain markers for remote includes that are resolved the same way using this general logic.
- The client makes the request.
- The application checks if the cache entry for the request path exists.
- If it does, the application uses the cached response and continues processing remote includes. For each remote include, the application starts at 1.
- If it doesn’t, the application calls the application layer to obtain a response.
- The application checks if the response is marked for caching.
- If it is, the application saves the data in the page cache.
- The application returns the response.
Dynamic Parameters in Includes
Avoid adding dynamic parameters to includes, which are likely to change frequently but don’t impact the response. These URLs create different cache entries, with the results computed first, before the cache entry is created. This adds more time, and can (involuntarily) contribute to excessively high cache entries and a lower cache-hit rate.
https://www.domain.com/path?param1=1 https://www.domain.com/path?param1=2 (change in parameter) https://www.domain.com/path?param1=1¶m2=abc (additional parameter) https://www.domain.com/path?param2=abc¶m1=1 (change in parameter order) https://www.domain.de/path?param1=1 (different domain) https://www.domain.com/otherpath?param1=1 (different path)
Using a Position Parameter
Vijay’s application adds a position parameter to product tiles in a search result. A position parameter specifies the order in which the tiles display. Each tile is cached many times, depending on its position in the search result, even though the same product always displays. When the shopper navigates search results on the product detail page (PDP), the app must pass search information using the product detail page URL. A better way to pass search information is to implement a client-side solution, which drastically increases search page throughput.
Ignore URL Parameters with No Meaning
B2C Commerce sometimes appends parameters with no meaning to URLs on the page, such as the campaign ID parameter of a newsletter campaign. For caching purposes, you can configure B2C Commerce to ignore these parameters using the Business Manager feature switch, Web Adapter Cache Key Ignore by Query String
.
In this module, we assume you are a Salesforce B2C Commerce admin with the proper permissions to perform these tasks. If you’re not a B2C Commerce admin, that’s OK. Read along to learn how your merchandiser takes these steps in a staging instance. Don’t try to follow our steps in your Trailhead Playground, because B2C Commerce isn’t available in the Trailhead Playground.
If you have a development instance of B2C Commerce, you can try out these steps in your instance. If you don’t have a development instance, ask your manager if there is one that you can use.
Enable Caching of 404 Responses
If you want to make your site more scalable, try enabling caching of 404 responses using these steps.
- Open Business Manager.
- Click Administration > Global Preferences > Feature Switches.
- Scroll down, and select Enable 404 response caching overrides.
Personalized Caching
B2C Commerce provides out-of-the-box personalized caching, which amends the cache key according to the currently active price books and applicable promotions. For example, imagine two shoppers are viewing the same page through the same URL. Shopper A uses price book X, while shopper B uses price book Y. In this case, the same product with the same URL is cached twice. The application serves the shoppers who use price book X a respective cache entry the same way as the application does for shoppers who use price book Y.
Depending on the number of price books and promotions, this scenario can lead to a large increase in cache entries, even if each entry’s product price is different. Use personalized caching only when necessary, for example, when you want to serve different cached content to different parts of your audience.
Scripts and Templates
As a best practice, use the script API instead of the <iscache>
tag. The dw.system.Response#setExpires(milliseconds)script
API controls the response caching behavior. While both approaches control the caching of the entire response, not individual templates, using the ISML tag presents these challenges.
- Using the
<iscache>
tag can be confusing, because it might suggest you are just caching a template. - It can be difficult to figure out which template defines the caching behavior of a response, because templates can be nested, with each one having its own
<iscache>
tag, where the lowest defined cache time is applied. - A template can be used in different contexts that require different cache policies, making the implementation complex.
Here are some examples of script API calls.
// SFRA controller cached via decorator server.get('Show', cache.applyDefaultCache, function (req, res, next) {} ); // Set cache time directly at the response (doesn’t rely on SFRA) response.setExpires(new Date().getTime() + durationInMinutes * 60 * 1000); // Apply personalized caching response.setVaryBy('price_promotion'); // Cache times don’t have to be constants. Get creative! // Apply layered caching times by product availability levels var availabilityModel = product.getAvailabilityModel(); if (availabilityModel.isOrderable(50)) { // Cache 5 days when >= 25 orderable response.setExpires(new Date().getTime() + 120 * 60 * 60 * 1000); } else if (availabilityModel.isOrderable(20)) { // Cache 4 hours when >= 10 orderable response.setExpires(new Date().getTime() + 4 * 60 * 60 * 1000); } else { // Cache 30 minutes when < 10; orderable response.setExpires(new Date().getTime() + 30 * 60 * 1000); }
Next Steps
In this unit, you learned why paying attention to caching at every architectural layer is critical for optimal site performance. You also explored caching in the web-server layer. Next, take a deeper look into caching in the application-server and storage layers.
Resources
- Standard Actions: JSP Tags
- Salesforce Help: Content Cache
- Salesforce Help: Page Caching
- Trailhead: Headless Implementation Strategies for Salesforce B2C Commerce (the Implement a Content Delivery Network and Caching unit)
- Salesforce: Caching Strategies