Sidhanta-Samantaray / multitenant-redis-caching

Multi-Tenant Cache Store using Custom RedisCacheManager

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Getting Started

Start Redis in Local System

docker run --name local-redis -p 6379:6379 -d redis

To start the Application

image

Goal of Application

Demonstrate tenant specific cache-store in a multi-tenant application, using Spring annotations and custom RedisCacheManager.

Idea is to prefix all applicable cache stores with the tenantId.

  [
     "tenant1_customers",
     "tenant2_customers",
     "tenant3_customers",
     "tenant4_customers",
     "tenant5_customers"
   ]

Use sample data for populating the database table

Intercepting Requests
@Configuration
@EnableWebMvc
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //All the APIs which requires tenant level isolation
        registry.addInterceptor(new TenantInterceptor())
                .addPathPatterns("/tenant/**"); 
        registry.addInterceptor(new AdminTenantInterceptor())
                .addPathPatterns("/admin/**");//For Super Admin
    }
}
Interceptor Usage for Setting Tenant
@Slf4j
public class TenantInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info(request.getRequestURI());
        ThreadLocal<String> tenantId = new ThreadLocal<>();
        tenantId.set(request.getHeader(Constants.TENANT_HTTP_HEADER));
        if (tenantId.get() == null) {
            log.error(Constants.TENANT_HTTP_HEADER + " Can not Be Blank");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
                    "Missing Header",
                    new Throwable(String.format("%s Missing", Constants.TENANT_HTTP_HEADER)));
        }
        /*
         * Perform Tenant Validation here
         *If Valid TENANT ID supplied
         *If User & Tenant Combination is valid
         */
        TenantContext.setTenant(tenantId);
        return true;
    }

}
Custom RedisCacheManager(Important Method)
@Slf4j
public class CustomCacheManager extends RedisCacheManager {
    //Constructor  code removed for brevity
    
    /**
     * @param name
     * @return Prefix the cache store name with the TENANT KEY
     * For SUPER ADMIN no prefix applied
     */
    @Override
    public Cache getCache(String name) {
        log.info("Inside getCache:" + name);
        //Getting Current Tenant
        String tenantId = TenantContext.getTenant().get();
        //If its Super ADMIN do not append
        if (tenantId.equals(Constants.SUPER_ADMIN_TENANT)) {
            return super.getCache(name);
        } else if (name.startsWith(tenantId)) {
           //when tenant already added
            return super.getCache(name);
        }
        return super.getCache(tenantId + "_" + name);
    }

}
  • API Spec

       swagger: '2.0'
       info:
         description: Api Documentation
         version: '1.0'
         title: Api Documentation
         termsOfService: 'urn:tos'
         contact: {}
         license:
           name: Apache 2.0
           url: 'http://www.apache.org/licenses/LICENSE-2.0'
       host: 'localhost:8080'
       tags:
         - name: cache-controller
           description: Cache Controller
         - name: customer-controller
           description: Customer Controller
       paths:
         /vibes/demo/api/admin/caches:
           get:
             tags:
               - cache-controller
             summary: cacheNames
             operationId: cacheNamesUsingGET
             produces:
               - application/json
             responses:
               '200':
                 description: OK
                 schema:
                   type: array
                   items:
                     type: string
               '401':
                 description: Unauthorized
               '403':
                 description: Forbidden
               '404':
                 description: Not Found
         /vibes/demo/api/admin/caches/evict:
           delete:
             tags:
               - cache-controller
             summary: evictAll
             operationId: evictAllUsingDELETE
             produces:
               - application/json
             responses:
               '200':
                 description: OK
               '204':
                 description: No Content
               '401':
                 description: Unauthorized
               '403':
                 description: Forbidden
         /vibes/demo/api/tenant/customer:
           get:
             tags:
               - customer-controller
             summary: getCustomers
             operationId: getCustomersUsingGET
             produces:
               - application/json
             responses:
               '200':
                 description: OK
                 schema:
                   type: array
                   items:
                     $ref: '#/definitions/Customer'
               '401':
                 description: Unauthorized
               '403':
                 description: Forbidden
               '404':
                 description: Not Found
         /vibes/demo/api/tenant/customer/evict:
           delete:
             tags:
               - customer-controller
             summary: evictCaches
             operationId: evictCachesUsingDELETE
             produces:
               - application/json
             responses:
               '200':
                 description: OK
               '204':
                 description: No Content
               '401':
                 description: Unauthorized
               '403':
                 description: Forbidden
       definitions:
         Customer:
           type: object
           properties:
             id:
               type: string
             name:
               type: string
             tenant:
               type: string
           title: Customer
    

Reference Documentation

For further reference, please consider the following sections:

Guides

The following guides illustrate how to use some features concretely:

About

Multi-Tenant Cache Store using Custom RedisCacheManager


Languages

Language:Java 100.0%