From aca7c98cc929fa2aa2f97eb04c4385d94226fbd2 Mon Sep 17 00:00:00 2001
From: Yves Noirjean <yves.noirjean@id.ethz.ch>
Date: Fri, 4 May 2018 14:47:06 +0200
Subject: [PATCH] SSDM-6373: added cache for data set files - these would be
 reloaded many times when a data set with many files is downloaded

---
 .../store/AbstractFileServiceHandler.java     | 71 ++++++++++++++++---
 1 file changed, 63 insertions(+), 8 deletions(-)

diff --git a/microservice-server/src/main/java/ch/ethz/sis/microservices/server/services/store/AbstractFileServiceHandler.java b/microservice-server/src/main/java/ch/ethz/sis/microservices/server/services/store/AbstractFileServiceHandler.java
index 6a7ade53e68..7c28850e592 100644
--- a/microservice-server/src/main/java/ch/ethz/sis/microservices/server/services/store/AbstractFileServiceHandler.java
+++ b/microservice-server/src/main/java/ch/ethz/sis/microservices/server/services/store/AbstractFileServiceHandler.java
@@ -5,7 +5,11 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -15,7 +19,6 @@ import ch.ethz.sis.microservices.server.logging.LogManager;
 import ch.ethz.sis.microservices.server.logging.Logger;
 import ch.ethz.sis.microservices.server.services.Service;
 import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.ContentCopy;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
@@ -26,6 +29,7 @@ import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.fetchoptions.DataSetFileFetchOptions;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.search.DataSetFileSearchCriteria;
 import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+import lombok.Value;
 
 public abstract class AbstractFileServiceHandler extends Service
 {
@@ -106,17 +110,68 @@ public abstract class AbstractFileServiceHandler extends Service
         }
         return null;
     }
+    
+    /*
+     * Less recently used cache
+     * Keeps maxSize items
+     */
+    @Value
+    private static class LRUCache<KEY, VALUE> {
+
+        int maxSize;
+        Map<KEY, VALUE> cacheByKey = new ConcurrentHashMap<>();
+        Deque<KEY> cachePriority = new ConcurrentLinkedDeque<>();
+
+        public LRUCache(int maxSize) {
+            this.maxSize = maxSize;
+        }
+
+        public void add(KEY key, VALUE value) {
+            // Remove if existing
+            if(cacheByKey.containsKey(key)) {
+                cacheByKey.remove(key);
+                cachePriority.remove(key);
+            }
+
+            // Add again
+            cacheByKey.put(key, value);
+            cachePriority.addFirst(key);
+
+            // Remove oldest if the list grow too much
+            if(cachePriority.size() > maxSize) {
+                cacheByKey.remove(cachePriority.removeLast());
+            }
+        }
+
+        public VALUE get(KEY key) {
+            // Refresh Prio
+            if(cacheByKey.containsKey(key)) {
+                cachePriority.remove(key);
+                cachePriority.addFirst(key);
+            }
+
+            return cacheByKey.get(key);
+        }
+
+    }
+
+    private static LRUCache<String, List<DataSetFile>> cache = new LRUCache<>(1000);
 
     private DataSetFile getDatasetFile(String datastoreURL, int datastoreTimeout, String sessionToken, String datasetPermId,
             String datasetPathToFile)
     {
-        IDataStoreServerApi v3Dss = HttpInvokerUtils.createServiceStub(IDataStoreServerApi.class, datastoreURL, datastoreTimeout);
-
-        DataSetFileSearchCriteria searchCriteria = new DataSetFileSearchCriteria();
-        searchCriteria.withDataSet().withCode().thatEquals(datasetPermId);
-        DataSetFileFetchOptions fetchOptions = new DataSetFileFetchOptions();
-        SearchResult<DataSetFile> files = v3Dss.searchFiles(sessionToken, searchCriteria, fetchOptions);
-        for (DataSetFile file : files.getObjects())
+        List<DataSetFile> files = cache.get(datasetPermId);
+        
+        if(files == null) {
+            IDataStoreServerApi v3Dss = HttpInvokerUtils.createServiceStub(IDataStoreServerApi.class, datastoreURL, datastoreTimeout);
+            DataSetFileSearchCriteria searchCriteria = new DataSetFileSearchCriteria();
+            searchCriteria.withDataSet().withCode().thatEquals(datasetPermId);
+            DataSetFileFetchOptions fetchOptions = new DataSetFileFetchOptions();
+            files = v3Dss.searchFiles(sessionToken, searchCriteria, fetchOptions).getObjects();
+            cache.add(datasetPermId, files);
+        }
+        
+        for (DataSetFile file : files)
         {
             if (file.getPath().equals(datasetPathToFile))
             {
-- 
GitLab