package com.thinkaurelius.titan.graphdb.vertices;

import java.util.*;
import java.util.concurrent.Callable;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.thinkaurelius.titan.diskstorage.StaticBuffer;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.Entry;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.SliceQuery;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.StaticBufferEntry;
import com.thinkaurelius.titan.graphdb.transaction.StandardTitanTx;
import com.thinkaurelius.titan.util.datastructures.Retriever;

/**
 * @author Matthias Broecheler (me@matthiasb.com)
 */

public class CacheVertex extends StandardVertex {
    // We don't try to be smart and match with previous queries
    // because that would waste more cycles on lookup than save actual memory
    private final Cache<SliceQuery, List<Entry>> queryCache;

    public CacheVertex(StandardTitanTx tx, long id, byte lifecycle) {
        super(tx, id, lifecycle);
        // TODO: add MemoryMeter agent to Titan to use memory measurement instead of size
        queryCache = CacheBuilder.newBuilder()
                .concurrencyLevel(1)
                .maximumSize(32)
                .build();
    }

    @Override
    public Collection<Entry> loadRelations(final SliceQuery query, final Retriever<SliceQuery, List<Entry>> lookup) {
        if (isNew())
            return Collections.EMPTY_SET;

        try {
            return queryCache.get(query, new Callable<List<Entry>>() {
                @Override
                public List<Entry> call() throws Exception {
                    Map.Entry<SliceQuery, List<Entry>> superset = getSuperResultSet(query);
                    if (superset == null) {
                        return lookup.get(query);
                    } else {
                        return query.getSubset(superset.getKey(), superset.getValue());
                    }
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean hasLoadedRelations(final SliceQuery query) {
        return queryCache.getIfPresent(query) != null || getSuperResultSet(query) != null;
    }

    private Map.Entry<SliceQuery, List<Entry>> getSuperResultSet(final SliceQuery query) {
        if (queryCache.size() > 0) {
            for (Map.Entry<SliceQuery, List<Entry>> entry : queryCache.asMap().entrySet()) {
                if (entry.getKey().subsumes(query)) return entry;
            }
        }
        return null;
    }

}

@@ -845,27 +845,152 @@
     
-private final Cache
+// We use a normal map with synchronization since the likelihood of contention%0A    // is super low in a single transaction%0A    private final Map
 %3CSli
@@ -989,33 +989,32 @@
  Map%3CSliceQuery,
- 
 List%3CEntry%3E%3E que
@@ -1139,230 +1139,58 @@
     
-// TODO: add MemoryMeter agent to Titan to use memory measurement instead of size%0A        queryCache = CacheBuilder.newBuilder()%0A                .concurrencyLevel(1)%0A                .maximumSize(32)%0A                .build(
+queryCache = new HashMap%3CSliceQuery,List%3CEntry%3E%3E(4
 );%0A 
@@ -1399,19 +1399,69 @@
         
-try
+List%3CEntry%3E result;%0A        synchronized (queryCache)
  %7B%0A     
@@ -1461,36 +1461,38 @@
 %7B%0A            re
-turn
+sult =
  queryCache.get(
@@ -1500,39 +1500,10 @@
 uery
-, new Callable%3CList%3CEntry%3E%3E() %7B
+);
 %0A   
@@ -1507,24 +1507,26 @@
         
+%7D%0A
         
 @Overrid
@@ -1521,87 +1521,64 @@
     
-@Override%0A                public List%3CEntry%3E call() throws Exception %7B%0A        
+if (result==null) %7B%0A            //First check for super%0A
     
@@ -1665,32 +1665,24 @@
             
-   
-     
 if (superset
@@ -1685,12 +1685,10 @@
 rset
- 
 ==
- 
 null
@@ -1707,30 +1707,24 @@
         
-       
- 
 re
-turn
+sult =
  lookup.
@@ -1747,24 +1747,16 @@
         
-     
-   
 %7D else %7B
@@ -1772,30 +1772,24 @@
         
-        return
+result =
  query.g
@@ -1846,32 +1846,24 @@
             
-        
 %7D%0A          
@@ -1868,102 +1868,170 @@
     
- 
-   %7D%0A            %7D);%0A        %7D
+synchronized (queryCache) %7B%0A                //TODO: become smarter about what to
  ca
-t
 ch
- (Exception e) %7B%0A            throw new RuntimeException(e
+e and when (e.g. memory pressure)%0A                queryCache.put(query,result
 );%0A 
@@ -2033,25 +2033,62 @@
 t);%0A        
+  
-%7D
+  %7D%0A        %7D%0A        return result;
 %0A    %7D%0A%0A    
@@ -2153,32 +2153,72 @@
 eQuery query) %7B%0A
+        synchronized (queryCache) %7B%0A    
         return q
@@ -2234,17 +2234,8 @@
 .get
-IfPresent
 (que
@@ -2279,24 +2279,34 @@
 y) != null;%0A
+        %7D%0A
     %7D%0A%0A    p
@@ -2424,24 +2424,68 @@
 ze() %3E 0) %7B%0A
+            synchronized (queryCache) %7B%0A    
             
@@ -2547,16 +2547,8 @@
 che.
-asMap().
 entr
@@ -2553,24 +2553,28 @@
 trySet()) %7B%0A
+    
             
@@ -2627,16 +2627,34 @@
  entry;%0A
+                %7D%0A
         

Use simple hashmap for caching in CacheVertex and made sure all access to it is synchronized. This a single vertex in a single transaction it not contended a lot, this should be a lot more space and memory efficient than a heavy Guava cache. In the future, we should investigate how to release memory heuristically on such this "cache".