<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tugdual Grall</title>
    <description>The latest articles on DEV Community by Tugdual Grall (@tgrall).</description>
    <link>https://dev.to/tgrall</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F56891%2F56cac7c0-f0be-4de4-b2da-677dca35133e.jpg</url>
      <title>DEV Community: Tugdual Grall</title>
      <link>https://dev.to/tgrall</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tgrall"/>
    <language>en</language>
    <item>
      <title>Simple caching service with Redis</title>
      <dc:creator>Tugdual Grall</dc:creator>
      <pubDate>Sun, 17 May 2020 04:36:19 +0000</pubDate>
      <link>https://dev.to/tgrall/simple-caching-service-with-redis-1jbj</link>
      <guid>https://dev.to/tgrall/simple-caching-service-with-redis-1jbj</guid>
      <description>&lt;p&gt;One of the most common use cases for Redis is to use it the database as a caching layer for your data, but Redis can do a lot more &lt;em&gt;(I will publish new articles later)&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;In this article, you will learn using a straightforward service, how to cache the result on some REST API calls to accelerate the data access, and also reduce the number of calls to external services.&lt;/p&gt;

&lt;p&gt;For this example, I am using the "Redis Movie Database" application, a microservice-based application that I created to showcase and explain various features of Redis and Redis Enterprise. &lt;/p&gt;

&lt;p&gt;You can see the caching service in action in this video:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2X6hmXGbLbg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Overview
&lt;/h3&gt;

&lt;p&gt;The application uses a third party API provided by the "&lt;a href="http://www.omdbapi.com/"&gt;OMDb API&lt;/a&gt;" to retrieve the ratings of the movie using its IMDb identifier. The frontend application call the &lt;code&gt;/caching/rating/&lt;/code&gt; service to get the rating information from OMDB.&lt;/p&gt;

&lt;p&gt;This service is doing the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if the rating data is already cached retrieve from the cache&lt;/li&gt;
&lt;li&gt;If the information is not cached, the system calls the OMDB API with the proper key and Movie ID&lt;/li&gt;
&lt;li&gt;The result is cached in Redis with a time to live of 120 seconds&lt;/li&gt;
&lt;li&gt;The ratings are returned to the client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis 5.x or later.&lt;/li&gt;
&lt;li&gt;Java 8 or later&lt;/li&gt;
&lt;li&gt;Apache Maven 3.6&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;In the microservice demonstration project, you can find the caching service in the project below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tgrall/redis-microservices-demo/tree/master/caching-service"&gt;Caching Service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Spring Boot application exposes a REST endpoint &lt;a href="https://github.com/tgrall/redis-microservices-demo/blob/master/caching-service/src/main/java/io/redis/demos/services/caching/RestStatusController.java"&gt;RestStatusController.java&lt;/a&gt; with the following key features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/1.0/caching/configuration/omdb_api&lt;/code&gt; : to save the OMDb API key in Redis.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/1.0/caching/ratings/{id}&lt;/code&gt; : to retrieve the IMDB Rating information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Data Structure
&lt;/h4&gt;

&lt;p&gt;The Caching Service is pretty simple and using two sets of keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ms:config&lt;/code&gt;: is a Redis Hash that will be used to store all global configuration; for the caching service, the entry &lt;code&gt;OMDB_API_KEY&lt;/code&gt; will contain the OMDb API key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ms:cache:ws:*&lt;/code&gt; : one entry for each movie viewed by a user, where the IMDb movie id replaces the &lt;code&gt;*&lt;/code&gt;. The hash contains the ratings, and each of the Movie Rating sites is a key in the hash ("Internet Movie Database",  "Rotten Tomatoes", "Metacritic"), and the value is the rating itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Code
&lt;/h4&gt;

&lt;p&gt;The implementation of the caching layer is simple and located in the class below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/tgrall/redis-microservices-demo/blob/master/caching-service/src/main/java/io/redis/demos/services/caching/service/WebServiceCachingService.java"&gt;&lt;code&gt;WebServiceCachingService&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Getting the Redis Connection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this project, I have not used Redis/Spring integration &lt;em&gt;by choice&lt;/em&gt;, I am only using Spring injection and Spring Boot Web features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the &lt;code&gt;afterConstruct()&lt;/code&gt; method creates the JedisPool from the Spring Configuration&lt;/li&gt;
&lt;li&gt;then each time a connection is needed, the application calls &lt;code&gt;Jedis jedis = jedisPool.getResource()&lt;/code&gt; to get a connection from the pool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Setting &amp;amp; Getting the OMDb API Key
&lt;/h4&gt;

&lt;p&gt;As you have seen earlier, the OMDb API Key is stored in a hash associated with the Redis key &lt;code&gt;ms:config&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The method &lt;code&gt;saveOMDBAPIKey&lt;/code&gt; is used to store the configuring with a  &lt;code&gt;hset&lt;/code&gt; call.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="n"&gt;jedis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jedisPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResource&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hset&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;KEY_CONFIG&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;OMDB_API_KEY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;omdbAPIKEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;line 1: get the connection from the pool&lt;/li&gt;
&lt;li&gt;line 2: set the key in the hash *(as you can see the application uses static variables (&lt;code&gt;KEY_CONFIG&lt;/code&gt; and &lt;code&gt;OMDB_API_KEY&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;line 3: the key received as a method parameter is set to a class member &lt;code&gt;omdbAPIKEY&lt;/code&gt; to avoid calling the hash each time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Calling OMDb API and Caching (or not) the result
&lt;/h4&gt;

&lt;p&gt;The method &lt;code&gt;getRatings&lt;/code&gt; receives two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;imdbId&lt;/code&gt; the IMDb id&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;withCache&lt;/code&gt; a boolean value to use or not Redis. The goal here is to show as a demonstration of the benefits of using Redis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's now look at the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;restCallKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;KEY_PREFIX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;imdbId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Jedis&lt;/span&gt; &lt;span class="n"&gt;jedis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jedisPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResource&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

            &lt;span class="c1"&gt;// Look in the map to see if the value has been cached&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withCache&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;returnValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hgetAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restCallKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"imdb_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imdbId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="nc"&gt;CloseableHttpClient&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpClientBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="nc"&gt;HttpGet&lt;/span&gt; &lt;span class="n"&gt;getRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpGet&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;getRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"accept"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="nc"&gt;ResponseHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;responseHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BasicResponseHandler&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

                &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nc"&gt;WsCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getRequest&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;responseHandler&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WsCall&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ratings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;)&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ratings"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ratingAsMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ratings&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;ratingAsMap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Source"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Value"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratingAsMap&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                &lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hset&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restCallKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;jedis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;expire&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;restCallKey&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;TTL&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpResponseException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
            &lt;span class="c1"&gt;// Small hack to keep it simple&lt;/span&gt;
            &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Metacritic"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;p style='color:red'&amp;gt;Error: OMDBAPI Key is invalid -- see services page&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;omdbAPIKEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Small hack to keep it simple&lt;/span&gt;
        &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Metacritic"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;p style='color:red'&amp;gt;Error: OMDBAPI Key is not set, please configure it -- see services page&amp;lt;/p&amp;gt;"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"elapsedTimeMs"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;returnValue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So if you look in the code carefully you see that only a few lines are related to the cache itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Line 1, the key is created from a prefix and the IMDb identifier.&lt;/li&gt;
&lt;li&gt;Line 3, the application retrieves a connection from the Jedis Pool.&lt;/li&gt;
&lt;li&gt;Line 7, if the cache is enabled,  the connection to get the value from Redis &lt;code&gt;returnValue = jedis.hgetAll(restCallKey)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If a value is present in the cache, the value is returned to the caller&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;returnValue&lt;/code&gt; is empty,  the OMDB REST service must be called (lines 11 to 25)&lt;/li&gt;
&lt;li&gt;The result of the Web service call is stored in the &lt;code&gt;returnValue&lt;/code&gt; variable, and save into Redis with a time to live (TTL) of 120 seconds (Line 27 to 29)&lt;/li&gt;
&lt;li&gt;Finally, the value is returned to the caller (Line 47).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quite simple, no?&lt;/p&gt;

&lt;p&gt;It is possible to optimize a little bit the application/code with few additions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make the TTL configurable by adding a new entry in the &lt;code&gt;ms:config&lt;/code&gt; cache&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://redis.io/topics/pipelining"&gt;pipelining&lt;/a&gt; to reduce the round trip time (RTT)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the application
&lt;/h3&gt;

&lt;p&gt;In the project, the service connects to a &lt;code&gt;local&lt;/code&gt; instance of Redis on port &lt;code&gt;6379&lt;/code&gt;. If you want to use a different instance or configure a password/user, you have to edit the &lt;code&gt;/redis-microservices-demo/caching-service/application.properties&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cloning and Building
&lt;/h4&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; git clone https://github.com/tgrall/redis-microservices-demo.git

&amp;gt; cd redis-microservices-demo/caching-service

&amp;gt; mvn clean package

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Running the application
&lt;/h4&gt;

&lt;p&gt;The application is a Spring Boot application, run the following command to start it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; mvn spring-boot:run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then you should save your &lt;a href="https://www.patreon.com/join/omdb"&gt;OMDB API key&lt;/a&gt; in Redis using the following call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -X POST http://localhost:8084/api/1.0/caching/configuration/omdb_api\?key\=[YOUR_KEY]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you can call the service itself to retrieve the ratings of the movie "WarGames"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -X GET http://localhost:8084/api/1.0/caching/ratings/tt0086567
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Call it multiple times, and you will see that the first call is *slow" (100ms or more). Then subsequent requests will be a lot faster, as the data are coming out of Redis. After tow minutes, the data is removed from the cache automatically (expiration), and the OMDB service will be called again.&lt;/p&gt;

&lt;p&gt;You can also force the service to no use the cache using the following call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; curl -X GET http://localhost:8084/api/1.0/caching/ratings/tt0086567?cache=0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The pattern used here is called "Cache-Aside"; and usually pretty easy to implement. It is interesting to notice that many libraries such as Spring provide built-in features to implement such caches.&lt;/p&gt;

&lt;p&gt;That said, this is not a silver bullet, you still have to look at the following points when you are implementing such caching service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading the cache: in the example, the cache is populated when the service is called. This lazy loading approach is excellent since the cache is only filled with data that are used by the application. However, the first call is paying the price of higher latency, so on your application, you may require to load the data at startup to avoid any hit miss.&lt;/li&gt;
&lt;li&gt;Cache Invalidation and Lifetime: When caching data, it is essential to look at the invalidation strategy, when and how I can update the data in the cache, but also how long the data will stay in the cache. In the example above, each data will remain for two minutes. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So now you are all set to implement a simple cache and have consistent fast access to your application data, independently of the backend.&lt;/p&gt;

</description>
      <category>nosql</category>
      <category>redis</category>
      <category>microservices</category>
      <category>java</category>
    </item>
  </channel>
</rss>
