All Skills

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.

S
$npx skills add SalesforceCommerceCloud/b2c-developer-tooling --skill b2c-custom-caches

B2C 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 CaseExample
Expensive calculationsCheck if any variation product is on sale for base product display
External system responsesCache in-store availability or prices from external APIs
Configuration settingsStore configuration data from JSON files or external sources
Frequently accessed dataProduct attributes, category data, site preferences

Limitations

ConstraintValue
Total memory per app server~20 MB for all custom caches
Max caches per code version100
Max entry size128 KB
Supported value typesPrimitives, arrays, plain objects, null (not undefined)
Cross-server syncNone (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
    }
  ]
}
PropertyRequiredDescription
idYesUnique ID across all cartridges in code version
expireAfterSecondsNoMaximum seconds an entry is retained

Using Custom Caches

Script API Classes

ClassDescription
dw.system.CacheMgrEntry point for accessing defined caches
dw.system.CacheCache 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');

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

MethodDescription
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

Best Practices

Do

  • Use get(key, loader) pattern for automatic population
  • Include scope (site, catalog, locale) in cache keys
  • Set appropriate expireAfterSeconds for time-sensitive data
  • 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 undefined values (use null instead)

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

IssueCauseSolution
Cache not found exceptionCache ID not defined in any caches.jsonAdd cache definition to caches.json
Duplicate cache ID errorSame ID used in multiple cartridgesUse unique IDs across all cartridges
Entry not storedValue exceeds 128 KB limitReduce data size or cache subsets
Entry not storedValue contains Script API objectsUse only primitives and plain objects
Unexpected cache missesDifferent app server or cache clearedAlways handle misses gracefully

Check the custom error log and custom warn log for cache-related messages.