Claude-skill-registry b2c-custom-caches
Implement custom caching with CacheMgr in B2C Commerce. Use when adding application-level caching, cache invalidation, or optimizing performance with custom cache regions. Covers cache definition JSON, CacheMgr API, and cache entry lifecycle.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/b2c-custom-caches" ~/.claude/skills/majiayu000-claude-skill-registry-b2c-custom-caches && rm -rf "$T"
skills/data/b2c-custom-caches/SKILL.mdB2C Custom Caches
Custom caches improve code performance by storing data that is expensive to calculate, takes a long time to retrieve, or is accessed frequently. Caches are defined in JSON files within cartridges and accessed via the Script API.
When to Use Custom Caches
| Use Case | Example |
|---|---|
| Expensive calculations | Check if any variation product is on sale for base product display |
| External system responses | Cache in-store availability or prices from external APIs |
| Configuration settings | Store configuration data from JSON files or external sources |
| Frequently accessed data | Product attributes, category data, site preferences |
Limitations
| Constraint | Value |
|---|---|
| Total memory per app server | ~20 MB for all custom caches |
| Max caches per code version | 100 |
| Max entry size | 128 KB |
| Supported value types | Primitives, arrays, plain objects, (not ) |
| Cross-server sync | None (caches are per-application-server) |
Defining a Custom Cache
File Structure
my_cartridge/ ├── package.json # References caches.json └── caches.json # Cache definitions
package.json
Add a
caches entry pointing to the cache definition file:
{ "name": "my_cartridge", "caches": "./caches.json" }
caches.json
Define caches with unique IDs and optional expiration:
{ "caches": [ { "id": "ProductAttributeCache" }, { "id": "ExternalPriceCache", "expireAfterSeconds": 300 }, { "id": "SiteConfigCache", "expireAfterSeconds": 60 } ] }
| Property | Required | Description |
|---|---|---|
| Yes | Unique ID across all cartridges in code version |
| No | Maximum seconds an entry is retained |
Using Custom Caches
Script API Classes
| Class | Description |
|---|---|
| Entry point for accessing defined caches |
| Cache instance for storing and retrieving entries |
Basic Usage
var CacheMgr = require('dw/system/CacheMgr'); // Get a defined cache var cache = CacheMgr.getCache('ProductAttributeCache'); // Get value (returns undefined if not found) var value = cache.get('myKey'); // Store value directly cache.put('myKey', { data: 'value' }); // Remove entry cache.invalidate('myKey');
Recommended Pattern: get with Loader
Use
get(key, loader) to automatically populate the cache on miss:
var CacheMgr = require('dw/system/CacheMgr'); var Site = require('dw/system/Site'); var cache = CacheMgr.getCache('SiteConfigCache'); // Loader function called only on cache miss var config = cache.get(Site.current.ID + '_config', function() { // Expensive operation - only runs if not cached return loadConfigurationFromFile(Site.current); });
Scoped Cache Keys
Include scope identifiers in keys to separate entries by context:
var CacheMgr = require('dw/system/CacheMgr'); var Site = require('dw/system/Site'); var cache = CacheMgr.getCache('ProductCache'); // Site-scoped key var siteKey = Site.current.ID + '_' + productID; var productData = cache.get(siteKey, loadProductData); // Catalog-scoped key var catalogKey = 'catalog_' + catalogID + '_' + productID; var catalogData = cache.get(catalogKey, loadCatalogData); // Locale-scoped key var localeKey = request.locale + '_' + contentID; var content = cache.get(localeKey, loadLocalizedContent);
Cache Methods
| Method | Description |
|---|---|
| Returns cached value or |
| Returns cached value or calls loader, stores result |
| Stores value directly (overwrites existing) |
| Removes entry for key |
Best Practices
Do
- Use
pattern for automatic populationget(key, loader) - Include scope (site, catalog, locale) in cache keys
- Set appropriate
for time-sensitive dataexpireAfterSeconds - Handle cache misses gracefully (data may be evicted anytime)
- Use descriptive cache IDs
Don't
- Include personal user data in cache keys (keys may appear in logs)
- Store Script API objects (only primitives and plain objects)
- Rely on cache entries existing (no persistence guarantee)
- Expect cross-server cache synchronization
- Store
values (useundefined
instead)null
Cache Invalidation
Caches are automatically cleared when:
- Any file in the active code version changes
- A new code version is activated
- Data replication completes
- Code replication completes
Manual invalidation only affects the current application server:
var cache = CacheMgr.getCache('MyCache'); // Invalidate single entry (current app server only) cache.invalidate('myKey'); // Storing undefined has same effect as invalidate cache.put('myKey', undefined);
Common Patterns
Caching External API Responses
var CacheMgr = require('dw/system/CacheMgr'); var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry'); var priceCache = CacheMgr.getCache('ExternalPriceCache'); function getExternalPrice(productID) { return priceCache.get('price_' + productID, function() { var service = LocalServiceRegistry.createService('PriceService', { createRequest: function(svc, args) { svc.setRequestMethod('GET'); svc.addParam('productId', args.productID); return null; }, parseResponse: function(svc, response) { return JSON.parse(response.text); } }); var result = service.call({ productID: productID }); return result.ok ? result.object : null; }); }
Caching Expensive Calculations
var CacheMgr = require('dw/system/CacheMgr'); var saleCache = CacheMgr.getCache('ProductSaleCache'); function isProductOnSale(masterProduct) { return saleCache.get('sale_' + masterProduct.ID, function() { var variants = masterProduct.variants.iterator(); while (variants.hasNext()) { var variant = variants.next(); if (isInPromotion(variant)) { return true; } } return false; }); }
Configuration Cache with Site Scope
var CacheMgr = require('dw/system/CacheMgr'); var Site = require('dw/system/Site'); var File = require('dw/io/File'); var FileReader = require('dw/io/FileReader'); var configCache = CacheMgr.getCache('SiteConfigCache'); function getSiteConfig() { var siteID = Site.current.ID; return configCache.get(siteID + '_config', function() { var configFile = new File(File.IMPEX + '/src/config/' + siteID + '.json'); if (!configFile.exists()) { return null; } var reader = new FileReader(configFile); var content = reader.getString(); reader.close(); return JSON.parse(content); }); }
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Cache not found exception | Cache ID not defined in any caches.json | Add cache definition to caches.json |
| Duplicate cache ID error | Same ID used in multiple cartridges | Use unique IDs across all cartridges |
| Entry not stored | Value exceeds 128 KB limit | Reduce data size or cache subsets |
| Entry not stored | Value contains Script API objects | Use only primitives and plain objects |
| Unexpected cache misses | Different app server or cache cleared | Always handle misses gracefully |
Check the custom error log and custom warn log for cache-related messages.