The cache system

We have included a cache system in our parallel server to avoid the search of data that have been recently been made. Our cache system has three elements:

  • The CacheItem class: This class represents every element stored in the cache. It has four attributes:
    • The command stored in the cache. We will store the query and report commands in the cache.
    • The response generated by that command.
    • The creation date of the item in the cache.
    • The last time this item was accessed in the cache.
  • The CleanCacheTask class: If we store all the commands in the cache but never delete the elements stored in it, the cache will increase its size indefinitely. To avoid this situation, we can have a task that deletes elements in the cache. We are going to implement this task as a Thread object. There are two options:
    • You can have the maximum size in the cache. If the cache has more elements than the maximum size, you can delete the elements that have been accessed less recently.
    • You can delete the elements that haven't been accessed for a predefined period of time from the cache. We are going to use this approach.
  • The ParallelCache class: This class implements the operations to store and retrieve elements in the cache. To store the data in the cache, we have used a ConcurrentHashMap data structure. As the cache will be shared between all the tasks of the server, we have to use a synchronization mechanism to protect the access to the cache, avoiding data race conditions. We have three options:
    • We can use a non-synchronized data structure (for example, a HashMap) and add the necessary code to synchronize accesses to this data structure, for example, with a lock. You can also convert a HashMap into a synchronized structure using the synchronizedMap() method of the Collections class.
    • Use a synchronized data structure, for example, Hashtable. In this case, we don't have data race conditions, but the performance can be better.
    • Use a concurrent data structure, for example, a ConcurrentHashMap class, which eliminates the possibility of data race conditions and it's optimized to work in a high concurrent environment. This is the option we're going to implement using an object of the ConcurrentHashMap class.

The code of the CleanCacheTask class is as follows:

  public class CleanCacheTask implements Runnable { 
 
    private final ParallelCache cache; 
 
    public CleanCacheTask(ParallelCache cache) { 
      this.cache = cache; 
    } 
 
    @Override 
    public void run() { 
      try { 
        while (!Thread.currentThread().interrupted()) { 
          TimeUnit.SECONDS.sleep(10); 
          cache.cleanCache(); 
        } 
      } catch (InterruptedException e) { 
 
    } 
  } 
 
} 

The class has a ParallelCache object. Every 10 seconds, it executes the cleanCache() method of the ParallelCache instance.

The ParallelCache class has five different methods. First, the constructor of the class that initializes the elements of the cache. It creates the ConcurrentHashMap object and starts a thread that will execute the CleanCacheTask class:

public class ParallelCache { 
 
  private final ConcurrentHashMap<String, CacheItem> cache; 
  private final CleanCacheTask task; 
  private final Thread thread; 
  public static intMAX_LIVING_TIME_MILLIS = 600_000; 
 
  public ParallelCache() { 
    cache=new ConcurrentHashMap<>(); 
    task=new CleanCacheTask(this); 
    thread=new Thread(task); 
    thread.start(); 
  } 

Then, there are two methods to store and retrieve an element in the cache. We use the put() method to insert the element in the HashMap and the get() method to retrieve the element from the HashMap:

public void put(String command, String response) { 
  CacheItem item = new CacheItem(command, response); 
  cache.put(command, item); 
} 
 
public String get (String command) { 
  CacheItem item=cache.get(command); 
  if (item==null) { 
    return null; 
  } 
  item.setAccessDate(new Date()); 
  return item.getResponse(); 
} 

Then, the method to clean the cache used by the CleanCacheTask class is:

public void cleanCache() { 
  Date revisionDate = new Date(); 
  Iterator<CacheItem> iterator = cache.values().iterator(); 
 
  while (iterator.hasNext()) { 
    CacheItem item = iterator.next(); 
    if (revisionDate.getTime() - item.getAccessDate().getTime()
>MAX_LIVING_TIME_MILLIS) { iterator.remove(); } } }

Finally, the method to shut down the cache that interrupts the thread executing the CleanCacheTask class and the method that returns the number of elements stored in the cache are:

public void shutdown() { 
  thread.interrupt(); 
} 
 
public intgetItemCount() { 
  return cache.size(); 
}