How to create cache of caches using google guava cache ?

Updated: Sep 18, 2021

Creating a plain cache is straight forward but if the data structure of data is complex, we need a cache of caches.


In cloud native application development, microservices are dependent on each other for certain master data. Fetching information means sending HTTP requests, when data is needed very frequently by a client microservice and the data source is another microservice, very frequent HTTP requests have to be made. This causes delay in processing in the client micro service. For these reasons, it makes sense to cache the master data in the client microservice that does not change frequently. Though this implementation is not only applicable for microservices in cloud native development and can be used in any type of application.


In this post I will show how to implement a cache of caches. The cache of caches is a generic guava cache where the keys are strings and the values are other guava caches. These guava caches as values also have key value pairs. The keys and values can be of any type. Here the cache of caches is the cacheMap in the CacheManager. The inner cache is defined as Cache<K,V>. This is why the cache manager is generic and can be used to keep any kind of key value pairs. The getEntityCache() method in the CacheManager retrieves the cache from the cacheMap. If a cache does not exist for a given entityId, it is created and added to the cacheMap and then returned.


CacheManager<K,V> Class

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

/** manages a set of caches where each instance of cache belongs to exactly one entity=manager
*   @param <K> - type of key
*   @param <V> - type of cache object
*/
public class CacheManager<K,V> {
  public static final int maxSize = 1024;
  protected final Cache<String , Cache<K,V>> cacheMap;
  private boolean isWeekKeys;

  public CacheManager() { this(false);}
  public CacheManager(boolean isWeekKeys) { this. isWeekKeys = isWeekKeys ;}

  /** returns a user/manager specific instance of cache
  *  @param entityId user/manager
  */
  public Cache<K,V> getEntityCache(String entityId) {
    Cache<K,V> entityCache = cacheMap.getIfpresent( entityId );
    if( entityCache == null) {
       entityId =  entityId.intern() //do not remove inturn()
       synchronized(entityId) {
           entityCache = cacheMap.getIfPresent(entityId);
           if( entityCache == null ) {
              final CacheBuilder builder =  CacheBuilder.newbuilder().maximumsize(256);
              if(isWeekKeys) {
                  builder.weakKeys();
              }
              entityCache = builder.build();
              cacheMap.put(entityId, entityCache); 
           }
       }
    }
    return  entityCache;
  } 
}

Now we will use this generic CacheManager to implement an EmployeeCacheManager. The EmployeeCacheManager represents a list of managers in an organization. Each manager in the cache holds a list of employees working for them. The managers are represented by the key of the cache of caches which are strings and the employees are the value of the cache of caches. Each element in the inner cache(the employee) is represented by a Sting key which is the employeeId and the value of the inner cache is the employee object.


EmployeeCacheManager

import com.google.common.cache.Cache;
import EmployeeCacheManager.Employee;
public class EmployeeCacheManager extends CacheManager<String, Employee> {
 
  public String getEmployeeAddress(String managerId, String employeeId) {
 
    Cache< String, Employee > employeeCache = getEntityCache(managerId);
    Employee employee = getEmployee(employeeCache, employeeId);
    String address = getEmployeeAddress(employee);
    return address; 
  }
  public void setEmployeeAddress(String managerId, String employeeId, String address) {
 
    Cache< String, Employee > employeeCache = getEntityCache(managerId);
    Employee employee = getEmployee(employeeCache, employeeId);
    setEmployeeAddress(employee, address);
  }
  public Employee getEmployee(Cache< String, Employee > employeeCache, 
     String employeeId) {
    Employee employee =  employeeCache.getIfPresent( employeeId );
    if( employee == null) {
      synchronized( employee ) {
         employee =  employeeCache.getIfPresent( employeeId );
          if( employee == null) {
            employee = new Employee(employeeId);
            employeeCache.put( employeeId , employee);
         }
      }
     }
     return employee;
  } 
  public String getEmployeeAddress(Employee employee) { 
return employee.getAddress();
  }
  public void setEmployeeAddress( Employee employee, String address ) {
employee.setAddress(address);
}
 
  public class Employee {
    String name;
    String address;
    String id;
    public class Employee(id) { this.id = id;}
     public String getName() {return this.name;}
     public String getAddress() {return this.address;}
     public String getId() {this.id;}
     public void setName(String name) {this.name = name;}
     public void setAddress(String address) {this.address = address;}
  }
}

EmployeeCachecreator


This class creates an instance of EmployeeCacheManager.

public class EmployeeCachecreator {
 
  public static void main(String[] args) {
 
    EmployeeCacheManager  cacheManager = new  EmployeeCacheManager();
    cacheManager.setEmployeeAddres("M111", "E111", "adress");
    String address =  cacheManager.getEmployeeAddres("M111", "E111");
  }
}




13 views0 comments

Recent Posts

See All