Agent skill

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.

Stars 34
Forks 10

Install this agent skill to your Project

npx add-skill https://github.com/SalesforceCommerceCloud/b2c-developer-tooling/tree/main/skills/b2c/skills/b2c-custom-caches

SKILL.md

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 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, null (not undefined)
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:

json
{
  "name": "my_cartridge",
  "caches": "./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
id Yes Unique ID across all cartridges in code version
expireAfterSeconds No Maximum seconds an entry is retained

Using Custom Caches

Script API Classes

Class Description
dw.system.CacheMgr Entry point for accessing defined caches
dw.system.Cache 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');

Recommended Pattern: get with Loader

Use get(key, loader) to automatically populate the cache on miss:

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);
});

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);

Cache Methods

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

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:

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);

Common Patterns

Caching External API Responses

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;
    });
}

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);
    });
}

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.

Expand your agent's capabilities with these related and highly-rated skills.

SalesforceCommerceCloud/b2c-developer-tooling

api-client-development

Creating typed API clients with OpenAPI specs, authentication, and OAuth scopes for SCAPI and similar APIs. Use when adding a new SCAPI client, generating types from an OpenAPI spec, setting up OAuth middleware, or integrating a new Commerce API endpoint.

34 10
Explore
SalesforceCommerceCloud/b2c-developer-tooling

testing

Writing tests for the B2C CLI project using Mocha, Chai, and MSW. Use when writing unit or integration tests, mocking HTTP requests with MSW, testing CLI commands with oclif, isolating config in tests, setting up test coverage, using sinon stubs, c8 coverage, capturing or silencing stdout, or creating MSW handlers.

34 10
Explore
SalesforceCommerceCloud/b2c-developer-tooling

documentation

Updating user guides, CLI reference, and API documentation for the B2C CLI project. Use when adding or changing CLI command docs, writing JSDoc for TypeDoc generation, updating Vitepress sidebar config, or creating new guide pages.

34 10
Explore
SalesforceCommerceCloud/b2c-developer-tooling

cli-command-development

Creating new CLI commands and topics for the B2C CLI using oclif. Use when adding a new command, creating a topic, adding flags or arguments, implementing table output, or extending BaseCommand/OAuthCommand/InstanceCommand.

34 10
Explore
SalesforceCommerceCloud/b2c-developer-tooling

sdk-module-development

Adding new modules and exports to the @salesforce/b2c-tooling-sdk package. Use when creating a new SDK module, adding barrel file exports, configuring package.json exports, or building client factory functions.

34 10
Explore
SalesforceCommerceCloud/b2c-developer-tooling

b2c-code

Deploy and manage code versions/cartridges on B2C Commerce instances/sandboxes with the b2c cli. Always reference when using the CLI to upload cartridges, deploy code, activate code versions, manage code versions, or watch for file changes during development.

34 10
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results