MicroStream as Spring Cache provider

With Spring Cache functionality, you can add in a transparent way caching functionality to your Spring application. We the help of some annotations, you can cache the result of some expensive method calls so that the next time the cached values are used instead of exec using the method again. This is very useful in the case the data do not change often or when caring the data improves performance at the cost of some memory usage.

The Spring Cache feature is designed as an abstract layer so that the developer can decide which implementation will be used to provide the actual caching functionality. MicroStream implements the Java EE JCache specification which is also supported by Spring Cache

Benefits

What are the benefits of using MicroStream as a Spring Cache provider? Within JCache, you have the optional functionality to store the cached values to an external resource. MicroStream implements this functionality so that you can configure that the MicroStream functionality is used to serialise the Cache to a data storage.

This means, that when you restart your application, it loads the cache data from the previous run. And thus data is already available on the first cache hit. But besides this performance benefit, you can also make use of the Data Model Evolution functionality of MicroStream.

If your class structure that holds the cached data is changed, you can still load the old data. The automatic mapping will be able to resolve the structure in many cases. If the changes are larger, you can define a mapping yourself so that you can still load the Cached data at application startup, even if the structure isn’t identical.

Setup

Let us review how you can configure your Spring Boot application to make use of MicroStream as a Cache provider.

First of all, you need to add the dependency of Spring itself to your application. This can be done by adding the spring-boot-starter-cache dependency.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

 

You also need to activate the Caching functionality in Spring by using the @EnableCaching annotation.

@SpringBootApplication
@EnableCaching
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

The next step is adding the required MicroStream dependencies.

        <dependency>
            <groupId>one.microstream</groupId>
            <artifactId>microstream-cache</artifactId>
            <version>${microstream.version}</version>
        </dependency>

        <dependency>
            <groupId>one.microstream</groupId>
            <artifactId>microstream-integrations-spring-boot</artifactId>
            <version>${microstream.version}</version>
        </dependency>

 

The MicroStream integration dependency for Spring Boot is optional. You only need it when you want to configure the StorageManager through the Spring configuration options. For more information about the integration options, have a look at this blog.

As a last step, we can define the different caches that we need for our application. We mainly need to define the expiry policy, after which time cache entries are evicted automatically, and specify the StorageManager instance.

@Component
public class CacheSetup implements JCacheManagerCustomizer {


    private final EmbeddedStorageManager storageManager;

    public CacheSetup(EmbeddedStorageManager storageManager) {
        // Retrieve the MicroStream Manager as Spring Bean
        this.storageManager = storageManager;
    }


    @Override
    public void customize(CacheManager cacheManager) {
        // Define the caches and their Expiration time
        defineCache(cacheManager, "countries", Duration.ONE_MINUTE);
    }


    private void defineCache(CacheManager cacheManager, String cacheName, Duration duration) {
        CacheConfiguration<?, ?> configuration = CacheConfiguration
                .Builder(Object.class, Object.class, cacheName, storageManager)
                .expiryPolicyFactory(CreatedExpiryPolicy.factoryOf(duration))
                .build();

        cacheManager.createCache(cacheName, configuration);
    }
}

 

Custom Key generation

Spring Cache provides a default Key generation algorithm for the cases where you use the @Cacheable and @CacheEvict annotations (see further on). It creates instances of the org.springframework.cache.interceptor.SimpleKey class that will be used as a Key value in the cache. But it has a problem concerning MicroStream serialisation.

The hash value, used to compare the key values and select them, is stored in a transient field by the constructor. But these transient fields are not persisted by MicroStream and thus when loaded from the storage, the hash value is lost, and thus keys no longer match.

Luckily, creating a custom key generation algorithm is simple. You can also opt for a custom PersistenceFieldEvaluator within MicroStream as described on this documentation page about transient fields.

public class CustomKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        // Key should only depend on parameters so that @Cacheable and @CacheEvict annotated methods result in same key
        return "Key" + StringUtils.arrayToDelimitedString(params, "_");
    }
}

 

A custom key algorithm should only be dependent on the parameters of your methods as shown in the above example. You have access to the Method and Class in case you want to implement a very complex algorithm that needs access to annotations.

This generator can be registered through a Spring Configuration bean that implements CachingConfigurer.

@Configuration
public class CacheConfig implements CachingConfigurer {

    public KeyGenerator keyGenerator() {
        return new CustomKeyGenerator();
    }
}

 

Usage

Now, we have all pieces in place to make use of MicroStream as a Spring Cache provider. When we make use of @Cacheable and @CacheEvict, MicroStream is responsible for keeping the cached data in a MicroStream-maintained data storage. It serialises the cache data to disk for examples that it can be used the next time, even after a process restart.

The only limitation is that a StorageManager that is used for this caching functionality can’t be used at the same time to define your object graph and use it as your persistence solution for your application. The Cache functionality defines its own root for this functionality.
But you can define and use multiple Storage Managers in the same application. And with the planned changes for version 8 of the integration code, you can define multiple managers more easily.

The complete code of this example can be found at this project on GitHub.

Conclusion

MicroStream can be used as a Spring Cache provider. It not only implements the caching functionality but can also store the cache to a data storage, just like any other Object Graph that is maintained by MicroStream.

This has the added benefit that cache data from a previous run of your application is still available and thus cache data are already available for the first request. The end-user experience and performance are improved as a result.

 

 

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
MicroStream joins the Eclipse Foundation

MicroStream is now an Eclipse Foundation Member

Next Post

A peek into upcoming version 8.0

Related Posts
Secured By miniOrange