Broadleaf Microservices

Caching

Overview

Out of the box, Broadleaf leverages Apache Ignite to handle caching concerns throughout our services. By default, cache is not distributed, and with a few exceptions, leverages TTL alone for cache eviction. Based on need, caches can be evicted explicitly based on application events by introducing event handlers to interact with the cache API. Apache Ignite can also be configured to operate as a distributed cache for more consistent and/or timely reaction to persistence changes, usually at the cost of performance because of additional blocking at the cache layer. Furthermore, the Apache Ignite support may be swapped out for another Spring Cache compatible caching implementation.

This guide describes:

  1. How to enable this caching.

  2. How to identify the cache instances that are relevant to a given service & override the default configuration if necessary.

  3. How enabling this caching will affect the memory footprint of your deployment.

How to Enable Caching

The out-of-box caching is enabled by simply removing the following property from your Spring application configuration:

spring:
  cache:
    type: none

Adjusting Default TTL

Review the following property classes for default TTL settings. You may declare properties in your own implementation config to override these values.

com.broadleafcommerce.catalog.cache.CatalogCacheProperties
com.broadleafcommerce.asset.cache.AssetCacheProperties
com.broadleafcommerce.auth.cache.AuthCacheProperties
com.broadleafcommerce.data.tracking.core.cache.DataTrackingCacheProperties
com.broadleafcommerce.search.index.core.cache.IndexCacheProperties
com.broadleafcommerce.menu.cache.MenuCacheProperties
com.broadleafcommerce.metadata.cache.MetadataCacheProperties
com.broadleafcommerce.promotion.offer.cache.OfferCacheProperties
com.broadleafcommerce.personalization.cache.PersonalizationCacheProperties
com.broadleafcommerce.pricing.cache.PricingCacheProperties
com.broadleafcommerce.search.core.cache.SearchCacheProperties
com.broadleafcommerce.translation.cache.TranslationCacheProperties

Deeper Configuration Changes

The easiest way to identify the relevant cache configuration for your applications, regardless of how you’ve composed your Broadleaf services, is to download the Broadleaf source code via Maven & use your IDE to search for usages of @Conditional(OnEnabledCacheCondition.class).

Each of these *AutoConfiguration classes registers the relevant cache instances. It’s worth scanning through these configurations to determine whether or not Broadleaf’s defaults meet your needs.

Overriding the default configuration is as simple as defining your own CacheManagerCustomizer<SpringCacheManager> bean that replaces the relevant Broadleaf bean.

The following is an example of the cache configuration for MenuServices:

@Configuration
@Conditional(OnEnabledCacheCondition.class)
@ConditionalOnClass(SpringCacheManager.class)
@ConditionalOnProperty(value = "com.broadleafcommerce.cache.activeCacheManagerImplementation",
        havingValue = "com.broadleafcommerce.common.extension.autoconfigure.IgniteCacheAutoConfiguration",
        matchIfMissing = true)
@EnableConfigurationProperties(MenuCacheProperties.class)
public class MenuCacheAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "menuCacheManagerCustomizer")
    public CacheManagerCustomizer<SpringCacheManager> menuCacheManagerCustomizer(
            IgniteConfigurers.BasicIgniteConfigurer configurer,
            MenuCacheProperties cacheProperties) {
        // This cache is managed directly, but setup a fallback TTL at a reasonable timeframe
        return cacheManager -> cacheManager.getConfiguration()
                .setCacheConfiguration(ArrayUtils.addAll(
                        cacheManager.getConfiguration().getCacheConfiguration(),
                        configurer.basicInitialize(new CacheConfiguration<>(),
                                CACHE_BY_MENU,
                                (int) cacheProperties.getByMenuTtl().getSeconds())));
    }

    @Bean
    @ConditionalOnMissingBean(name = CACHE_BY_MENU)
    public KeyGenerator menuCacheByMenu() {
        return DataTrackingKeyGen.of(APPLICATION_WITH_LOCALE);
    }

}

Memory Footprint Implications

For each application, Ignite is configured to consume up to 2GB of RAM outside of the application heap - i.e. the RAM allocated to the application should remain the same.

Note
If needed, the max memory allocation can be tweaked by overriding the IgniteConfiguration bean for the application.
@Configuration
@Conditional(OnEnabledCacheCondition.class)
@ConditionalOnClass(IgniteConfiguration.class)
@ConditionalOnProperty(value = "com.broadleafcommerce.cache.activeCacheManagerImplementation",
        havingValue = "com.broadleafcommerce.common.extension.autoconfigure.IgniteCacheAutoConfiguration",
        matchIfMissing = true)
@EnableCaching
public class IgniteCacheAutoConfiguration extends BaseCacheConfiguration {

    @Bean
    @ConditionalOnMissingBean
    @SuppressWarnings("squid:S3878")
    public CacheManager cacheManager(CacheManagerCustomizers cacheManagerCustomizers) {
        SpringCacheManager cacheManager = new SpringCacheManager();
        cacheManager.setConfiguration(igniteConfiguration());
        cacheManager = cacheManagerCustomizers.customize(cacheManager);
        cacheManager.onApplicationEvent(null);
        return cacheManager;
    }

    @Bean
    @ConditionalOnMissingBean
    public IgniteConfiguration igniteConfiguration() {
        IgniteConfiguration configuration = new IgniteConfiguration();
        configuration.setGridLogger(new Slf4jLogger());
        configuration.setDiscoverySpi(
                new TcpDiscoverySpi().setIpFinder(new TcpDiscoveryVmIpFinder(true)));
        configuration.setMetricsLogFrequency(0L);
        DataStorageConfiguration storageCfg = new DataStorageConfiguration();
        // Setting the size of the default memory region to 2GB (off-heap)
        storageCfg.getDefaultDataRegionConfiguration()
                .setMaxSize(2L * 1024 * 1024 * 1024)
                .setPageEvictionMode(DataPageEvictionMode.RANDOM_2_LRU);
        configuration.setDataStorageConfiguration(storageCfg);
        return configuration;
    }

    @Bean
    @ConditionalOnMissingBean
    public IgniteConfigurers.BasicIgniteConfigurer basicIgniteConfigurer() {
        return new IgniteConfigurers.BasicIgniteConfigurer();
    }
}

Replacing Apache Ignite

Review the code sample above for how Apache Ignite is declared as the Spring Cache implementation. The most important part is the CacheManager instantiation (in this case, Ignite’s SpringCacheManager). To replace, introduce your own configuration class that establishes your desired cache manager. Then, declare the fully qualified name of your new configuration class as the value for the property com.broadleafcommerce.cache.activeCacheManagerImplementation. At this point, all Broadleaf out-of-the-box cache will be disabled. You can implement as many CacheManagerCustomizer instances as you like to support replacing the out-of-the-box customizers that are no longer being loaded by Broadleaf, or introduce new customizers to support new caches that you develop.