Skip to Content
Technical Articles
Author's profile photo Alexander Duemont

How to Use Redis with SAP Cloud SDK

Note: For a complete overview of our blog post series visit the SAP Cloud SDK Overview.

 

For an overview of new features of the SAP Cloud SDK, please look at our recent announcement post.

Introduction

With the new version 3 of SAP Cloud SDK, it’s possible to integrate custom caching frameworks. The application developer can now take the library of their choice, as long as it supports JCache (JSR-107). By loading dependencies into the Maven project, the caching framework is enabled automatically due to the Service Provider Interface (SPI).

By default the Caffeine library is recommended for easy to use, efficient caching. But certain use cases require a distributed caching setup, which allows the storing of data far beyond the application container scope.

Redis

One of the most popular tools to store in-memory data is Redis. The open source tool can be used as database, message broker and cache. It features built-in functionality for data replication, transactions and high availability. Even using on-disk persistence is possible. And there is much more to discover with this software.

Run locally

There are multiple ways to start a Redis instance. Probably the easiest way is by running a local docker container:

// for running a instance as daemon service
docker run --name my-redis -d -p 6379:6379 redis

// for temporarily running the instance in the current terminal
docker run --name my-redis -it --rm -p 6379:6379 redis

The default port for Redis is 6379 but it can be changed to any open port number.

Caching with the SAP Cloud SDK

To use caching with SAP Cloud SDK it’s recommended to use the ResilienceDecorator. This utility class allows to decorate a given lambda (e.g. external service call) with resilient mechanisms:

  • timeout
  • circuit breaker
  • bulkhead
  • fallback
  • caching

Add Maven Dependency

By default the resilience strategy featuring Resilience4j is chosen for Cloud SDK. But for caching there is no default dependency provided, the application developer is required to decide on a module for caching.

Usually the following dependency would be recommended for Caffeine:

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>jcache</artifactId>
    <version>2.8.0</version>
</dependency>

But for enabling Redis you should choose the following dependency instead:

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson</artifactId>
  <version>3.11.2</version>
</dependency>

Note: Please take a (new) version of your choice.

The Redisson library is a Java client for connecting and using Redis. It also includes a JCache implementation for the SPI.

Add Redisson Configuration

At runtime Redisson requires a configuration file, from which it reads the current configuration. Please consider other configuration mechanisms as well. The following file features the most basic setup:

./application/src/main/resources/redisson-jcache.yaml
singleServerConfig:
  address: "redis://127.0.0.1:6379"

By having the file in the “resource” folder we make sure it will be available at runtime. Please change the configuration with regards to your application setup.

Usage with SAP Cloud SDK

Now consider you have the following computation heavy operation, for which you want to apply a resilience and caching strategy:

Supplier<T> busyLambda;

Now when using the ResilienceDecorator you can enable caching to the lambda with the following code:

import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration;

ResilienceConfiguration.CacheConfiguration cacheConfiguration =
  ResilienceConfiguration.CacheConfiguration.of(Duration.ofSeconds(10)).withoutParameters();

ResilienceConfiguration resilienceConfiguration =
  ResilienceConfiguration.of(YourServiceClass.class).cacheConfiguration(cacheConfiguration);

T result = ResilienceDecorator.executeSupplier(busyLambda, resilienceConfiguration);

The ResilienceDecorator is instantiated with the reference of your service class. You can use any other type of identifier heres as well. Also the decorator utility class features many other methods to decorate (and execute) a lambda. Please find the JavaDoc for more information.

Here the resilience configuration only features a dedicated cache configuration. Settings for timeout, circuit breaker and bulkhead are implicitly taken from default values.

The configuration for cache declares a lifetime of ten seconds after which the next lambda invocation triggers a cache miss and the underlying method will be called – and it’s response persisted in the cache again. Additional a set of parameters could be provided in order to differentiate between requests depending on arguments which may have been used to define the busySupplier. But to keep to keep the example simple, we declare “withoutParameters”.

That’s it.

Now Redis is being used by default in your application with the SAP Cloud SDK.

Assigned Tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Kavitha Sivakumar
      Kavitha Sivakumar

      If you are not able to provide a yaml file for configuring Redisson, for e.g. in case you are using an SCP service like for e.g. Redis on hyperscaler, you can configure Redisson programmatically by following the below steps:

      1. Create a new Decorator that extends the DefaultCachingDecorator
        /**
         * Redisson caching decorator
         */
        @Slf4j
        @RequiredArgsConstructor
        public class RedissonCachingDecorator extends DefaultCachingDecorator
        {
            @Nonnull final Config redissonCfg;
        
            @Override
            @Nonnull
            protected <T> Configuration<GenericCacheKey<?, ?>, T> createCacheConfiguration(
                    @Nonnull final ResilienceConfiguration.CacheConfiguration configuration )
            {
                final Configuration<GenericCacheKey<?, ?>, T> result = super.createCacheConfiguration(configuration);
                return RedissonConfiguration.fromConfig(redissonCfg, result);
            }
        }​
      2. Create a Resilience decoration strategy with the newly created Decorator
        final Resilience4jDecorationStrategy redissonStrategy =
                        new Resilience4jDecorationStrategy(
                                new DefaultThreadContextProvider(),
                                new DefaultTimeLimiterProvider(),
                                new DefaultBulkheadProvider(),
                                new DefaultCircuitBreakerProvider(),
                                new RedissonCachingDecorator(redissonCfg),
                                new DefaultRetryProvider());​
      3. Populate Redisson's config object
        final ScpCfCloudPlatform scpCfCloudPlatform = new ScpCfCloudPlatform();
        final JsonObject serviceCredentials = scpCfCloudPlatform.getServiceCredentials("redis-cache");
        String redisUri = serviceCredentials.get("uri").getAsString();
        String password = serviceCredentials.get("password").getAsString();
        final Config redissonCfg = new Config();
        redissonCfg.useSingleServer().setPassword(password).setAddress(redisUri);​

        For our example, we are populating redisson's configuration object with values read from VCAP_SERVICES in Cloud Foundry.(Assumption is that we have an app deployed in CF and bound to the Redis on hyperscaler SCP service)

      4. Supply the decoration strategy to the Resilience API
        //Setting the decoration strategy
        ResilienceDecorator.setDecorationStrategy(redissonStrategy);
        //executing an operation in a resilient manner 
        ResilienceDecorator.executeSupplier(() -> operation(),ResilienceConfiguration.of("configuration"));​
      Author's profile photo Alexandre CODJOVI
      Alexandre CODJOVI

      Do you confirm cached object must implement Serializable?

      It's not mentioned in the blog.

       

      But default Redisson Serializers require classes to implement the Serializable interface which makes a caffeine to redis transition tricky

       

      Supplier<T> busyLambda;
      Author's profile photo Alexander Duemont
      Alexander Duemont
      Blog Post Author

      Correct, as of today by default Redisson requires T to be Serializable.

      Unfortunately we cannot just add the interface to all of our (internal) classes because it results in additional, permanent API contract that we would need to respect and maintain indefinitely.

      Hence your current options is to either choose a different Redisson codec, e.g. Jackson...

      singleServerConfig:
        address: "redis://127.0.0.1:6379"
      codec: !<org.redisson.codec.JsonJacksonCodec> {}
      
      

      ... or alternatively find a custom transformation in your application from T to Serializable.

      Author's profile photo Gaurav Singh
      Gaurav Singh

      Thank you very much !! Very valuable blog.

      I have some doubts

      Where do we use this logic in our application , and How do we start using it .

       

      Do we have some more sample/example on its usage?

       

      import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator;
      import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration;
      
      ResilienceConfiguration.CacheConfiguration cacheConfiguration =
        ResilienceConfiguration.CacheConfiguration.of(Duration.ofSeconds(10)).withoutParameters();
      
      ResilienceConfiguration resilienceConfiguration =
        ResilienceConfiguration.of(YourServiceClass.class).cacheConfiguration(cacheConfiguration);
      
      T result = ResilienceDecorator.executeSupplier(busyLambda, resilienceConfiguration);

      Thank you in advance!!

      Author's profile photo Johannes Schneider
      Johannes Schneider

      Hi Gaurav,

      thanks for reaching out to us with your question.

      For this integration to work in your application, all you have to do is to include the org.redisson:redisson dependency instead of the usual com.github.ben-manes.caffeine:jcache dependency - the entire rest, such as loading the correct classes, will be handled automatically under the hood without any input required from your side.
      (Of course, you also have to supply the configuration file for Redis as described in the post)

      Regarding the question about how to use our resilience APIs, please take a look at our documentation.

      Best regards,
      Johannes