重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
b2c-custom-caches by salesforcecommercecloud/b2c-developer-tooling
npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-custom-caches自定义缓存通过存储计算成本高、检索时间长或访问频繁的数据来提升代码性能。缓存定义在 cartridge 内的 JSON 文件中,并通过 Script API 进行访问。
| 使用场景 | 示例 |
|---|---|
| 昂贵的计算 | 检查基础产品展示时是否有任何变体产品正在促销 |
| 外部系统响应 | 缓存来自外部 API 的店内库存情况或价格 |
| 配置设置 | 存储来自 JSON 文件或外部源的配置数据 |
| 频繁访问的数据 | 产品属性、分类数据、站点偏好设置 |
| 约束 | 值 |
|---|---|
| 每个应用服务器的总内存 | 所有自定义缓存约 20 MB |
| 每个代码版本的最大缓存数 | 100 |
| 最大条目大小 | 128 KB |
| 支持的值类型 | 基本类型、数组、普通对象、null(不支持 ) |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
undefined| 跨服务器同步 | 无(缓存是每个应用服务器独立的) |
my_cartridge/
├── package.json # 引用 caches.json
└── caches.json # 缓存定义
添加一个指向缓存定义文件的 caches 条目:
{
"name": "my_cartridge",
"caches": "./caches.json"
}
使用唯一的 ID 和可选的过期时间来定义缓存:
{
"caches": [
{
"id": "ProductAttributeCache"
},
{
"id": "ExternalPriceCache",
"expireAfterSeconds": 300
},
{
"id": "SiteConfigCache",
"expireAfterSeconds": 60
}
]
}
| 属性 | 必需 | 描述 |
|---|---|---|
id | 是 | 在代码版本的所有 cartridge 中唯一的 ID |
expireAfterSeconds | 否 | 条目保留的最大秒数 |
| 类 | 描述 |
|---|---|
dw.system.CacheMgr | 访问已定义缓存的入口点 |
dw.system.Cache | 用于存储和检索条目的缓存实例 |
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');
使用 get(key, loader) 在缓存未命中时自动填充缓存:
var CacheMgr = require('dw/system/CacheMgr');
var Site = require('dw/system/Site');
var cache = CacheMgr.getCache('SiteConfigCache');
// 加载器函数仅在缓存未命中时调用
var config = cache.get(Site.current.ID + '_config', function() {
// 昂贵的操作 - 仅在未缓存时运行
return loadConfigurationFromFile(Site.current);
});
在键中包含作用域标识符,以按上下文分隔条目:
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);
| 方法 | 描述 |
|---|---|
get(key) | 返回缓存的值或 undefined |
get(key, loader) | 返回缓存的值或调用加载器,存储结果 |
put(key, value) | 直接存储值(覆盖已存在的) |
invalidate(key) | 移除指定键的条目 |
get(key, loader) 模式实现自动填充expireAfterSecondsundefined 值(改用 null)缓存会在以下情况下自动清除:
手动失效仅影响当前应用服务器:
var cache = CacheMgr.getCache('MyCache');
// 使单个条目失效(仅当前应用服务器)
cache.invalidate('myKey');
// 存储 undefined 与调用 invalidate 效果相同
cache.put('myKey', undefined);
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;
});
}
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;
});
}
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);
});
}
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 缓存未找到异常 | 在任何 caches.json 中都未定义缓存 ID | 将缓存定义添加到 caches.json |
| 重复的缓存 ID 错误 | 在多个 cartridge 中使用了相同的 ID | 在所有 cartridge 中使用唯一的 ID |
| 条目未存储 | 值超过 128 KB 限制 | 减少数据大小或缓存子集 |
| 条目未存储 | 值包含 Script API 对象 | 仅使用基本类型和普通对象 |
| 意外的缓存未命中 | 不同的应用服务器或缓存被清除 | 始终优雅地处理未命中 |
检查自定义错误日志和自定义警告日志以获取与缓存相关的消息。
每周安装次数
68
代码仓库
GitHub 星标数
34
首次出现
2026年2月17日
安全审计
安装于
github-copilot62
codex59
cursor59
opencode58
amp57
kimi-cli57
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.
| 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 |
| 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, null (not undefined) |
| Cross-server sync | None (caches are per-application-server) |
my_cartridge/
├── package.json # References caches.json
└── caches.json # Cache definitions
Add a caches entry pointing to the cache definition file:
{
"name": "my_cartridge",
"caches": "./caches.json"
}
Define caches with unique IDs and optional expiration:
{
"caches": [
{
"id": "ProductAttributeCache"
},
{
"id": "ExternalPriceCache",
"expireAfterSeconds": 300
},
{
"id": "SiteConfigCache",
"expireAfterSeconds": 60
}
]
}
| Property | Required | Description |
|---|---|---|
id | Yes | Unique ID across all cartridges in code version |
expireAfterSeconds | No | Maximum seconds an entry is retained |
| Class | Description |
|---|---|
dw.system.CacheMgr | Entry point for accessing defined caches |
dw.system.Cache | Cache instance for storing and retrieving entries |
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');
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);
});
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);
| Method | Description |
|---|---|
get(key) | Returns cached value or undefined |
get(key, loader) | Returns cached value or calls loader, stores result |
put(key, value) | Stores value directly (overwrites existing) |
invalidate(key) | Removes entry for key |
get(key, loader) pattern for automatic populationexpireAfterSeconds for time-sensitive dataundefined values (use null instead)Caches are automatically cleared when:
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);
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;
});
}
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;
});
}
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);
});
}
| 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.
Weekly Installs
68
Repository
GitHub Stars
34
First Seen
Feb 17, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot62
codex59
cursor59
opencode58
amp57
kimi-cli57
Supabase Postgres 最佳实践指南 - 8大类别性能优化规则与SQL示例
81,400 周安装