b2c-custom-caches
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseB2C Custom Caches
B2C 自定义缓存
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.
自定义缓存通过存储计算成本高、检索耗时久或访问频繁的数据来提升代码性能。缓存定义在cartridge内的JSON文件中,可通过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 |
| 使用场景 | 示例 |
|---|---|
| 计算成本高的操作 | 为基础商品展示检查是否有变体商品正在促销 |
| 外部系统响应 | 缓存来自外部API的门店库存或价格数据 |
| 配置设置 | 存储来自JSON文件或外部源的配置数据 |
| 频繁访问的数据 | 商品属性、分类数据、站点偏好设置 |
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, |
| Cross-server sync | None (caches are per-application-server) |
| 约束项 | 取值 |
|---|---|
| 每个应用服务器的总内存 | 所有自定义缓存约20 MB |
| 每个代码版本的最大缓存数量 | 100 |
| 缓存条目的最大大小 | 128 KB |
| 支持的值类型 | 基本类型、数组、普通对象、 |
| 跨服务器同步 | 无(缓存为每个应用服务器独立存储) |
Defining a Custom Cache
定义自定义缓存
File Structure
文件结构
my_cartridge/
├── package.json # References caches.json
└── caches.json # Cache definitionsmy_cartridge/
├── package.json # 引用caches.json
└── caches.json # 缓存定义文件package.json
package.json
Add a entry pointing to the cache definition file:
cachesjson
{
"name": "my_cartridge",
"caches": "./caches.json"
}添加条目指向缓存定义文件:
cachesjson
{
"name": "my_cartridge",
"caches": "./caches.json"
}caches.json
caches.json
Define caches with unique IDs and optional expiration:
json
{
"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 |
使用唯一ID定义缓存,可选择设置过期时间:
json
{
"caches": [
{
"id": "ProductAttributeCache"
},
{
"id": "ExternalPriceCache",
"expireAfterSeconds": 300
},
{
"id": "SiteConfigCache",
"expireAfterSeconds": 60
}
]
}| 属性 | 是否必填 | 描述 |
|---|---|---|
| 是 | 在同一代码版本的所有cartridge中需唯一 |
| 否 | 缓存条目保留的最大秒数 |
Using Custom Caches
使用自定义缓存
Script API Classes
Script API 类
| Class | Description |
|---|---|
| Entry point for accessing defined caches |
| Cache instance for storing and retrieving entries |
| 类 | 描述 |
|---|---|
| 访问已定义缓存的入口类 |
| 用于存储和检索缓存条目的缓存实例 |
Basic Usage
基础用法
javascript
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');javascript
var CacheMgr = require('dw/system/CacheMgr');
// 获取已定义的缓存
var cache = CacheMgr.getCache('ProductAttributeCache');
// 获取值(未找到则返回undefined)
var value = cache.get('myKey');
// 直接存储值
cache.put('myKey', { data: 'value' });
// 删除条目
cache.invalidate('myKey');Recommended Pattern: get with Loader
推荐模式:结合Loader使用get方法
Use to automatically populate the cache on miss:
get(key, loader)javascript
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);
});使用在缓存未命中时自动填充缓存:
get(key, loader)javascript
var CacheMgr = require('dw/system/CacheMgr');
var Site = require('dw/system/Site');
var cache = CacheMgr.getCache('SiteConfigCache');
// 仅在缓存未命中时调用Loader函数
var config = cache.get(Site.current.ID + '_config', function() {
// 高成本操作 - 仅在未缓存时执行
return loadConfigurationFromFile(Site.current);
});Scoped Cache Keys
带作用域的缓存键
Include scope identifiers in keys to separate entries by context:
javascript
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);在键中包含作用域标识符,按上下文区分不同条目:
javascript
var CacheMgr = require('dw/system/CacheMgr');
var Site = require('dw/system/Site');
var cache = CacheMgr.getCache('ProductCache');
// 站点作用域的键
var siteKey = Site.current.ID + '_' + productID;
var productData = cache.get(siteKey, loadProductData);
// 目录作用域的键
var catalogKey = 'catalog_' + catalogID + '_' + productID;
var catalogData = cache.get(catalogKey, loadCatalogData);
// 语言环境作用域的键
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 |
| 方法 | 描述 |
|---|---|
| 返回缓存值或 |
| 返回缓存值,若未命中则调用loader并存储结果 |
| 直接存储值(覆盖现有条目) |
| 删除对应键的条目 |
Best Practices
最佳实践
Do
建议
- Use pattern for automatic population
get(key, loader) - Include scope (site, catalog, locale) in cache keys
- Set appropriate for time-sensitive data
expireAfterSeconds - Handle cache misses gracefully (data may be evicted anytime)
- Use descriptive cache IDs
- 使用模式实现自动填充
get(key, loader) - 在缓存键中包含作用域(站点、目录、语言环境)
- 为时间敏感的数据设置合适的
expireAfterSeconds - 优雅处理缓存未命中的情况(数据可能随时被淘汰)
- 使用描述性的缓存ID
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 (use
undefinedinstead)null
- 在缓存键中包含用户个人数据(键可能出现在日志中)
- 存储Script API对象(仅使用基本类型和普通对象)
- 依赖缓存条目始终存在(无持久化保证)
- 期望跨服务器缓存同步
- 存储值(使用
undefined替代)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:
javascript
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);在以下情况时缓存会自动清除:
- 活动代码版本中的任何文件发生变更
- 激活新的代码版本
- 数据复制完成
- 代码复制完成
手动失效仅影响当前应用服务器:
javascript
var cache = CacheMgr.getCache('MyCache');
// 失效单个条目(仅当前应用服务器)
cache.invalidate('myKey');
// 存储undefined与执行invalidate效果相同
cache.put('myKey', undefined);Common Patterns
常见模式
Caching External API Responses
缓存外部API响应
javascript
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;
});
}javascript
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
缓存高成本计算结果
javascript
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;
});
}javascript
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
带站点作用域的配置缓存
javascript
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);
});
}javascript
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.
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 缓存未找到异常 | 缓存ID未在任何caches.json中定义 | 在caches.json中添加缓存定义 |
| 重复缓存ID错误 | 多个cartridge使用了相同的ID | 在所有cartridge中使用唯一的ID |
| 条目未存储 | 值超过128 KB限制 | 减小数据大小或缓存子集数据 |
| 条目未存储 | 值包含Script API对象 | 仅使用基本类型和普通对象 |
| 意外的缓存未命中 | 应用服务器不同或缓存已被清除 | 始终优雅处理未命中情况 |
查看自定义错误日志和自定义警告日志获取缓存相关信息。