<?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: iamsid</title>
    <description>The latest articles on DEV Community by iamsid (@sidhantasamantaray).</description>
    <link>https://dev.to/sidhantasamantaray</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%2F473731%2Ffe8eecf4-91f7-4f8a-af33-722715ad3307.jpeg</url>
      <title>DEV Community: iamsid</title>
      <link>https://dev.to/sidhantasamantaray</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sidhantasamantaray"/>
    <language>en</language>
    <item>
      <title>Multi-Tenant Cache Store using Custom RedisCacheManager</title>
      <dc:creator>iamsid</dc:creator>
      <pubDate>Fri, 25 Sep 2020 03:25:04 +0000</pubDate>
      <link>https://dev.to/sidhantasamantaray/multi-tenant-cache-store-using-custom-rediscachemanager-ehm</link>
      <guid>https://dev.to/sidhantasamantaray/multi-tenant-cache-store-using-custom-rediscachemanager-ehm</guid>
      <description>&lt;p&gt;Idea behind this post is to demonstrate how easily tenant specific cache-store in a &lt;em&gt;multi-tenant&lt;/em&gt; application can be built using Spring annotations,Custom &lt;strong&gt;RedisCacheManager&lt;/strong&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Why Caching ?
&lt;/h5&gt;

&lt;p&gt;There are multiple benefits of caching.One key benefit is to reduce database access thus making access to data much faster and less expensive.&lt;br&gt;
For more details on &lt;em&gt;Redis cache use case&lt;/em&gt; refer &lt;a href="https://redislabs.com/solutions/use-cases/caching/" rel="noopener noreferrer"&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;h5&gt;
  
  
  Multi-Tenant Application Design
&lt;/h5&gt;

&lt;p&gt;As per Wikipedia,&lt;a href="https://en.wikipedia.org/wiki/Multitenancy" rel="noopener noreferrer"&gt;Multitenancy&lt;/a&gt; refers to a software architecture in which a single instance of software runs on a server and serves multiple tenants.&lt;br&gt;
Here we will follow shared database(all tenants share same &lt;code&gt;database-&amp;gt;schema-&amp;gt;table&lt;/code&gt;) approach. We will use just one entity as follow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"customers"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Getter&lt;/span&gt;
&lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@DynamicInsert&lt;/span&gt;
&lt;span class="nd"&gt;@DynamicUpdate&lt;/span&gt;
&lt;span class="nd"&gt;@FilterDef&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenantFilter"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nd"&gt;@ParamDef&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenantKey"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;)})&lt;/span&gt;
&lt;span class="nd"&gt;@Filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenantFilter"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;condition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenant = :tenantKey"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//Identifier to distinguish each tenant&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;While invoking any APIs mandatory http header parameter &lt;br&gt;
&lt;strong&gt;x-tenant-id&lt;/strong&gt; will be supplied.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft82pemzfs0oecs4exfwq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft82pemzfs0oecs4exfwq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;For demo purpose we are using simple tenantKey.&lt;/li&gt;
&lt;li&gt;Recommended to use an alphanumeric id which is unique and difficult to guess(&lt;em&gt;e.g. 64b2a7b330614cd8804bcd93b72a069e&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h5&gt;
  
  
  Enable Caching
&lt;/h5&gt;

&lt;p&gt;spring boot provides simple annotation based configuration to enable caching(&lt;a href="https://www.baeldung.com/spring-cache-tutorial" rel="noopener noreferrer"&gt;more details&lt;/a&gt;).For Starting Redis in Local System&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;docker run --name local-redis -p 6379:6379 -d redis&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now cache the result of below repository method.Just by adding &lt;code&gt;@Cacheable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Repository&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;CustomerRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Customer&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="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="nd"&gt;@Cacheable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"customers"&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;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Implementation(No custom manager)
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7ay9dgm0tk4rjd496uaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7ay9dgm0tk4rjd496uaz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading data from database into cache store.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="s2"&gt;"customers"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;1-1 Mapping between database table to cache store.

&lt;ul&gt;
&lt;li&gt;Fetch data from cache, then filter it based on the supplied 
tenantId and return the response.&lt;/li&gt;
&lt;li&gt;Additional custom processing logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Assume a scenario where few tenants didn't access the application for some hours, but due to above implementation data will be available in cache store.&lt;/li&gt;
&lt;/ul&gt;


&lt;h5&gt;
  
  
  Now Lets See The Implementation(With custom manager)
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fymk7e00pqsgvq34ii26j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fymk7e00pqsgvq34ii26j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Idea is to prefix all the cache stores with the tenantId.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="s2"&gt;"tenant1_customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="s2"&gt;"tenant2_customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="s2"&gt;"tenant3_customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="s2"&gt;"tenant4_customers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="s2"&gt;"tenant5_customers"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;How Can we Implement This ???&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CustomCacheManger Extending RedisCacheManager&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomCacheManager&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheManager&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomCacheManager&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisCacheWriter&lt;/span&gt; &lt;span class="n"&gt;cacheWriter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheConfiguration&lt;/span&gt; &lt;span class="n"&gt;defaultCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheWriter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;RedisCacheManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cacheWriter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheWriter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cacheDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;)&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="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * @param name
     * @return Prefix the cache store name with the TENANT KEY
     * For SUPER ADMIN no prefix applied
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Cache&lt;/span&gt; &lt;span class="nf"&gt;getCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Inside getCache:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TenantContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTenant&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SUPER_ADMIN_TENANT&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="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&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="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCache&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Configuration class to create CacheManger Bean&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableCaching&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisConfigurationCustom&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheConfiguration&lt;/span&gt; &lt;span class="nf"&gt;redisCacheConfiguration&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="nc"&gt;RedisCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultCacheConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serializeKeysWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisSerializationContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SerializationPair&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromSerializer&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;StringRedisSerializer&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;serializeValuesWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisSerializationContext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SerializationPair&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromSerializer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisSerializer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="c1"&gt;//Configure in Property file as per use case, hardcoded just for demo&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entryTtl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheWriter&lt;/span&gt; &lt;span class="nf"&gt;redisCacheWriter&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="nc"&gt;RedisCacheWriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lockingRedisCacheWriter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redisConnectionFactory&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LettuceConnectionFactory&lt;/span&gt; &lt;span class="nf"&gt;redisConnectionFactory&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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LettuceConnectionFactory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RedisCacheManager&lt;/span&gt; &lt;span class="nf"&gt;redisCacheManager&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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CustomCacheManager&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redisCacheWriter&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;redisCacheConfiguration&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Congratulations now the basic implementation is complete&lt;/p&gt;
&lt;/blockquote&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Let's see what is going on&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;HandlerInterceptor&lt;/code&gt; , tenant value is fetched from Request Headers and set in ThreadLocal.&lt;/li&gt;
&lt;li&gt;We have created a &lt;code&gt;CustomCacheManager&lt;/code&gt; by extending &lt;code&gt;RedisCacheManager&lt;/code&gt;,then override &lt;code&gt;getCache(String name)&lt;/code&gt; implementation to manipulate the name.&lt;/li&gt;
&lt;li&gt;As you can see we are storing the supplied TenantId in &lt;a href="https://www.baeldung.com/java-threadlocal" rel="noopener noreferrer"&gt;ThreadLocal&lt;/a&gt; ,hence we can use it inside &lt;em&gt;getCache()&lt;/em&gt;,the one we are overriding to manipulate the cache name.&lt;/li&gt;
&lt;li&gt;Anyplace where &lt;em&gt;cache name(e.g. customers)&lt;/em&gt; is specified within &lt;code&gt;@cachable&lt;/code&gt; &lt;em&gt;getCache()&lt;/em&gt; method will be invoked and return name with tenantId prefixed to it for further use within the application.&lt;/li&gt;
&lt;li&gt;For some use cases we might need to call the getCache method explicitly using the &lt;code&gt;CustomCacheManager&lt;/code&gt;, hence to avoid multiple prefixing additional checks are used.

&lt;ul&gt;
&lt;li&gt;One such use case is having a scheduled process as super admin to clean cache stores for all the tenant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This doesn't have all the answers you might want, but I &lt;strong&gt;hope this is a helpful starting point&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Sidhanta-Samantaray" rel="noopener noreferrer"&gt;
        Sidhanta-Samantaray
      &lt;/a&gt; / &lt;a href="https://github.com/Sidhanta-Samantaray/multitenant-redis-caching" rel="noopener noreferrer"&gt;
        multitenant-redis-caching
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Multi-Tenant Cache Store using Custom RedisCacheManager
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Getting Started&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h6 class="heading-element"&gt;Start Redis in Local System&lt;/h6&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;docker run --name local-redis -p 6379:6379 -d redis&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h6 class="heading-element"&gt;To start the Application&lt;/h6&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Provide PostgreSQL details in application.properties file&lt;/li&gt;
&lt;li&gt;Run below command
&lt;ul&gt;
&lt;li&gt;clean spring-boot:run&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Swagger URL

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://localhost:8080/vibes/demo/api/swagger-ui/index.html" rel="nofollow noopener noreferrer"&gt;http://localhost:8080/vibes/demo/api/swagger-ui/index.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;
&lt;br&gt;


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;a rel="noopener noreferrer" href="https://github.com/Sidhanta-Samantaray/multitenant-redis-caching./docs/diagram.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FSidhanta-Samantaray%2Fmultitenant-redis-caching.%2Fdocs%2Fdiagram.png" alt="image"&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Goal of Application&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Demonstrate tenant specific cache-store in a &lt;em&gt;multi-tenant&lt;/em&gt; application,
using Spring annotations and custom &lt;strong&gt;RedisCacheManager&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Idea is to prefix all applicable cache stores with the tenantId.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;  [
     &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;tenant1_customers&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
     &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;tenant2_customers&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
     &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;tenant3_customers&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
     &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;tenant4_customers&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;,
     &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;tenant5_customers&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;
   ]&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Use &lt;a href="https://github.com/Sidhanta-Samantaray/multitenant-redis-caching./docs/sample-data.xlsx" rel="noopener noreferrer"&gt;sample data&lt;/a&gt; for populating the database table&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h6 class="heading-element"&gt;Intercepting Requests&lt;/h6&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-java notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;@&lt;/span&gt;&lt;span class="pl-c1"&gt;Configuration&lt;/span&gt;
&lt;span class="pl-c1"&gt;@&lt;/span&gt;&lt;span class="pl-c1"&gt;EnableWebMvc&lt;/span&gt;
&lt;span class="pl-k"&gt;public&lt;/span&gt; &lt;span class="pl-k"&gt;class&lt;/span&gt; &lt;span class="pl-smi"&gt;InterceptorConfig&lt;/span&gt; &lt;span class="pl-k"&gt;implements&lt;/span&gt; &lt;span class="pl-smi"&gt;WebMvcConfigurer&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;@&lt;/span&gt;&lt;span class="pl-c1"&gt;Override&lt;/span&gt;
    &lt;span class="pl-k"&gt;public&lt;/span&gt; &lt;span class="pl-smi"&gt;void&lt;/span&gt; &lt;span class="pl-en"&gt;addInterceptors&lt;/span&gt;(&lt;span class="pl-smi"&gt;InterceptorRegistry&lt;/span&gt; &lt;span class="pl-s1"&gt;registry&lt;/span&gt;) {
        &lt;span class="pl-c"&gt;//All the APIs which requires tenant level isolation&lt;/span&gt;
        &lt;span class="pl-s1"&gt;registry&lt;/span&gt;.&lt;span class="pl-en"&gt;addInterceptor&lt;/span&gt;(&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-smi"&gt;TenantInterceptor&lt;/span&gt;())
                .&lt;span class="pl-en"&gt;addPathPatterns&lt;/span&gt;(&lt;span class="pl-s"&gt;"/tenant/**"&lt;/span&gt;); 
        &lt;span class="pl-s1"&gt;registry&lt;/span&gt;.&lt;span class="pl-en"&gt;addInterceptor&lt;/span&gt;(&lt;span class="pl-k"&gt;new&lt;/span&gt; &lt;span class="pl-smi"&gt;AdminTenantInterceptor&lt;/span&gt;())
                .&lt;span class="pl-en"&gt;addPathPatterns&lt;/span&gt;(&lt;span class="pl-s"&gt;"/admin/**"&lt;/span&gt;);&lt;span class="pl-c"&gt;//For Super Admin&lt;/span&gt;
    }
}&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Sidhanta-Samantaray/multitenant-redis-caching" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Reference Documents
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://d0.awsstatic.com/whitepapers/Database/database-caching-strategies-using-redis.pdf" rel="noopener noreferrer"&gt;Database Caching Strategies Using Redis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/java-redis-lettuce" rel="noopener noreferrer"&gt;Introduction to Lettuce – the Java Redis Client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/spring-data-redis-properties" rel="noopener noreferrer"&gt;Spring Data Redis’s Property-Based Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/redis" rel="noopener noreferrer"&gt;Redis using Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redis</category>
      <category>springboot</category>
      <category>multitenant</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
