Search code examples
springcachingspring-cachegemfirespring-data-gemfire

How to configure gemfire for spring cache manager without explicitly defining regions?


I have a spring application using spring caching based on Guava cache. Due to the high throughput demand and the write-behind functionality, we are now considering migration to Gemfire. I am successful in configuration Gemfire as cache and is able to read and write from the cache. In all the configuration examples, the configuration requires defining of LocalRegionFactory as below:

    @Bean
    public Region<Long,Person> myPersonRegion(LocalRegionFactoryBean<Long, Person> personRegion) throws Exception {

        return personRegion.getObject();


    }

    @Bean
    public LocalRegionFactoryBean<Long, Person> personRegion(GemFireCache cache,AsyncEventQueue gemfireQueue) {
        LocalRegionFactoryBean<Long, Person> personRegion = new LocalRegionFactoryBean<>();
        personRegion.setCache(cache);
        personRegion.setClose(false);
        personRegion.setName("person");
        personRegion.setAsyncEventQueues(new AsyncEventQueue[]{gemfireQueue});
        personRegion.setPersistent(false);
        return personRegion;

    }

After the beans are defined, we can use @Cacheable(value="person"), @CacheEvict(value="person"). If we directly use a cache name, gemfire throws error that the cache is not defined.

Our experience with Guava ( or Hazelcast, redis etc ) is that , we don't need to explicitly define the caches. It will be automatically created by spring on first occurence.

Is there any way configure gemfire to also behave in the same way ?


Solution

  • The short answer is NO; not exactly.

    I am not entirely certain your following statement is completely accurate either...

    Our experience with Guava ( or Hazelcast, redis etc ) is that , we don't need to explicitly define the caches.

    For Hazelcast, I know this is not true from recent experience (see configuration, and specifically this line). Line 78 is absolutely necessary (in some shape or form, e.g. alternatively XML); without it, Spring's Cache Abstraction will throw an Exception.

    Although I have not tested Redis as a caching provider, it does appear Redis can handle dynamic Cache creation (also this).

    It is likely true that Guava, like the ConcurrentMapCacheManager implementation, does not require pre-existing Caches be explicitly defined since the ConcurrentMapCacheManager will dynamically create the Cache( a ConcurrentHashMap) at runtime when requested if NOT explicitly "named". However, if the Caches are explicitly named in advance, then an Exception will be thrown if the Cache is not already defined (i.e. "named").

    I have examples and tests of other caching providers here that illustrate different and rather unique UCs of Spring's Cache Abstraction in practice, identified by the test class or test case names.

    However, in all Pivotal GemFire or Apache Geode test examples, you must explicitly create the Region that will serve as the "Cache" in Spring's caching infrastructure. Although, SDG's GemfireCacheManager implementation will dynamically create the Spring Cache object (backed by the underlying Region) that is required by Spring's AOP CacheInterceptor.

    This results in the following, minimal, necessary configuration to enable caching with GemFire/Geode as the provider...

    @SpringBootApplication
    @EnableCaching
    class MyCachingApplication {
    
      public static void main(String[] args) {
        SpringApplication.run(MyCachingApplication.class, args);
      }
    
      @Bean
      GemfireCacheManager cacheManager(GemFireCache gemfireCache) {
        GemfireCacheManager cacheManager = new GemfireCacheManager();
        cacheManager.setCache(gemfireCache);
        return cacheManager;
      }
    
      // define all Region beans required by the application including Regions
      // used specifically in Spring's Cache Abstraction
    }
    

    Now, having said this, I have prototyped dynamic Region creation based on the Spring Cache Abstraction annotations (e.g. @Cacheable) used throughout the declared application [service] components as can be seen starting with this test. Here is the configuration.

    As you can see, there are NO explicit bean definitions for the GemFire Regions that will serve as Caches in Spring's caching infrastructure. Yet, the application's (test's) Spring @Service component does make use of caching.

    Dynamic Region creation is accomplished by making use of a Spring BeanPostProcessor (here) and GemFire Functions (using SDG's Function annotation support) to dynamically create the Regions at runtime, during startup. The Function execution is defined here, and the actual Function implementation is defined here.

    This example is pretty crude (i.e. does not handle custom Region configuation (e.g. eviction/expiration, persistence, overflow, etc) beyond DataPolicy) and is currently setup to handle the peer cache topology (i.e. the test application is a peer member/node in the GemFire DS).

    However, it is pretty easy to extend this prototype to be used in a client/server topology, take into account all Spring and JSR-107 caching annotations and allow more custom Region configuration.

    In time, this maybe something I add to the SDG framework itself.

    Hope this helps.

    Cheers, John