package com.thinkaurelius.titan.graphdb.vertices;

import com.google.common.collect.ImmutableList;
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.graphdb.vertices.querycache.ConcurrentQueryCache;
import com.thinkaurelius.titan.graphdb.vertices.querycache.QueryCache;
import com.thinkaurelius.titan.graphdb.vertices.querycache.SimpleQueryCache;
import com.thinkaurelius.titan.util.datastructures.Retriever;

import java.util.List;
import java.util.SortedSet;
import java.util.concurrent.ConcurrentSkipListSet;

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

public class CacheVertex extends StandardVertex {

    private SortedSet<Entry> relationCache=null;
    private QueryCache queryCache=null;

    public CacheVertex(StandardTitanTx tx, long id, byte lifecycle) {
        super(tx, id, lifecycle);
    }

    @Override
    public Iterable<Entry> loadRelations(SliceQuery query, Retriever<SliceQuery, List<Entry>> lookup) {
        if (isNew()) return ImmutableList.of();
        else {
            if (relationCache==null) {
                //Initialize datastructures
                if (tx().getConfiguration().isSingleThreaded()) {
                    relationCache = new ConcurrentSkipListSet<Entry>();
                    queryCache = new SimpleQueryCache();
                } else {
                    synchronized (this) {
                        if (relationCache==null) {
                            relationCache = new ConcurrentSkipListSet<Entry>();
                            queryCache = new ConcurrentQueryCache();
                        }
                    }
                }
            }
            if (queryCache.isCovered(query)) {
                SortedSet<Entry> results = relationCache.subSet(StaticBufferEntry.of(query.getSliceStart(), null),StaticBufferEntry.of(query.getSliceEnd(),null));
                return results;
            } else {
                List<Entry> results = lookup.get(query);
                relationCache.addAll(results);
                queryCache.add(query);
                return results;
            }
        }
    }

}

@@ -44,16 +44,119 @@
 tices;%0A%0A
+import java.util.List;%0Aimport java.util.SortedSet;%0Aimport java.util.concurrent.ConcurrentSkipListSet;%0A%0A
 import c
@@ -662,417 +662,747 @@
 tan.
-graphdb.vertices.querycache.SimpleQueryCache;%0Aimport com.thinkaurelius.titan.util.datastructures.Retriever;%0A%0Aimport java.util.List;%0Aimport java.util.SortedSet;%0Aimport java.util.concurrent.ConcurrentSkipListSet;%0A%0A/**%0A * @author Matthias Broecheler (me@matthiasb.com)%0A */%0A%0Apublic class CacheVertex extends StandardVertex %7B%0A%0A    private SortedSet%3CEntry%3E relationCache=null;%0A    private QueryCache queryCache=null
+util.datastructures.Retriever;%0A%0A/**%0A * @author Matthias Broecheler (me@matthiasb.com)%0A */%0A%0Apublic class CacheVertex extends StandardVertex %7B%0A%0A    /**%0A     * Holder object for two concurrent collections.%0A     * %0A     * We could forego the holder and make %7B@link State#queryCache%7D and%0A     * %7B@link State#relationCache%7D fields on this class. However, when we do%0A     * that one of the two fields must then be marked volatile for use in the%0A     * double-checked locking pattern below. Using the nonvolatile field in DCL%0A     * would be a thread safety failure. I think the holder idiom is slightly%0A     * easier to use, so I'm defaulting to it unless this proves to be a memory%0A     * or cpu problem.%0A     */%0A    private volatile State state
 ;%0A%0A 
@@ -1708,35 +1708,29 @@
     if (
-relationCache==null
+null == state
 ) %7B%0A    
@@ -1859,379 +1859,185 @@
     
-relationCache = new ConcurrentSkipListSet%3CEntry%3E();%0A                    queryCache = new SimpleQueryCache();%0A                %7D else %7B%0A                    synchronized (this) %7B%0A                        if (relationCache==null) %7B%0A                            relationCache = new ConcurrentSkipListSet%3CEntry%3E();%0A                            queryCache = new ConcurrentQueryCach
+state = new State();%0A                %7D else %7B%0A                    synchronized (this) %7B%0A                        if (null == state) %7B%0A                            state = new Stat
 e();
@@ -2133,16 +2133,22 @@
     if (
+state.
 queryCac
@@ -2213,16 +2213,22 @@
 sults = 
+state.
 relation
@@ -2465,16 +2465,22 @@
         
+state.
 relation
@@ -2518,16 +2518,22 @@
         
+state.
 queryCac
@@ -2610,10 +2610,298 @@
 %0A    %7D%0A%0A
+    private static class State %7B%0A        private SortedSet%3CEntry%3E relationCache;%0A        private QueryCache queryCache;%0A        %0A        private State() %7B%0A            relationCache = new ConcurrentSkipListSet%3CEntry%3E();%0A            queryCache = new ConcurrentQueryCache();%0A        %7D%0A    %7D%0A
 %7D%0A

fix broken double-checked locking