diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/CacheManager.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/CacheManager.java index 319cf75685..8ec9764f66 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/CacheManager.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/CacheManager.java @@ -112,6 +112,28 @@ public Cache cache(String name, long capacity) { return cache; } + public Cache threadLocalCache(String name, long capacity) { + if (!this.caches.containsKey(name)) { + this.caches.putIfAbsent(name, new ThreadLocalCache<>(capacity)); + } + @SuppressWarnings("unchecked") + Cache cache = (Cache) this.caches.get(name); + E.checkArgument(cache instanceof ThreadLocalCache, + "Invalid cache implement: %s", cache.getClass()); + return cache; + } + + public Cache optimisticCache(String name, long capacity) { + if (!this.caches.containsKey(name)) { + this.caches.putIfAbsent(name, new OptimisticCache<>(capacity)); + } + @SuppressWarnings("unchecked") + Cache cache = (Cache) this.caches.get(name); + E.checkArgument(cache instanceof OptimisticCache, + "Invalid cache implement: %s", cache.getClass()); + return cache; + } + public Cache offheapCache(HugeGraph graph, String name, long capacity, long avgElemSize) { if (!this.caches.containsKey(name)) { diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/OptimisticCache.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/OptimisticCache.java new file mode 100644 index 0000000000..8eede10ff2 --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/OptimisticCache.java @@ -0,0 +1,149 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.backend.cache; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.locks.StampedLock; +import java.util.function.Consumer; + +import com.baidu.hugegraph.iterator.MapperIterator; +import com.baidu.hugegraph.util.E; + +public class OptimisticCache extends AbstractCache { + + private final StampedLock stampedLock; + private final Map cache; + + public OptimisticCache(long capacity) { + super(capacity); + this.stampedLock = new StampedLock(); + this.cache = new HashMap<>(); + } + + @Override + protected V access(K id) { + final StampedLock stampedLock = this.stampedLock; + long stamp = stampedLock.tryOptimisticRead(); + V value = this.cache.get(id); + if (!stampedLock.validate(stamp)) { + stamp = stampedLock.readLock(); + try { + value = this.cache.get(id); + } finally { + stampedLock.unlockRead(stamp); + } + } + return value; + } + + @Override + protected boolean write(K id, V value) { + final StampedLock stampedLock = this.stampedLock; + final long stamp = stampedLock.writeLock(); + try { + this.cache.put(id, value); + } finally { + stampedLock.unlockWrite(stamp); + } + return true; + } + + @Override + protected void remove(K id) { + final StampedLock stampedLock = this.stampedLock; + final long stamp = stampedLock.writeLock(); + try { + this.cache.remove(id); + } finally { + stampedLock.unlockWrite(stamp); + } + } + + @Override + protected Iterator> nodes() { + final StampedLock stampedLock = this.stampedLock; + final long stamp = stampedLock.readLock(); + try { + Iterator> iter = this.cache.entrySet().iterator(); + return new MapperIterator<>(iter, kvEntry -> { + return new CacheNode<>(kvEntry.getKey(), kvEntry.getValue()); + }); + } finally { + stampedLock.unlockRead(stamp); + } + } + + @Override + public boolean containsKey(K id) { + final StampedLock stampedLock = this.stampedLock; + long stamp = stampedLock.tryOptimisticRead(); + boolean existed = this.cache.containsKey(id); + if (!stampedLock.validate(stamp)) { + stamp = stampedLock.readLock(); + try { + existed = this.cache.containsKey(id); + } finally { + stampedLock.unlockRead(stamp); + } + } + return existed; + } + + @Override + public void traverse(Consumer consumer) { + E.checkNotNull(consumer, "consumer"); + final StampedLock stampedLock = this.stampedLock; + final long stamp = stampedLock.readLock(); + try { + this.cache.values().forEach(consumer); + } finally { + stampedLock.unlockRead(stamp); + } + } + + @Override + public void clear() { + final StampedLock stampedLock = this.stampedLock; + final long stamp = stampedLock.writeLock(); + try { + this.cache.clear(); + } finally { + stampedLock.unlockWrite(stamp); + } + } + + @Override + public long size() { + final StampedLock stampedLock = this.stampedLock; + long stamp = stampedLock.tryOptimisticRead(); + int size = this.cache.size(); + if (!stampedLock.validate(stamp)) { + stamp = stampedLock.readLock(); + try { + size = this.cache.size(); + } finally { + stampedLock.unlockRead(stamp); + } + } + return size; + } +} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/ThreadLocalCache.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/ThreadLocalCache.java new file mode 100644 index 0000000000..6666207dba --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/cache/ThreadLocalCache.java @@ -0,0 +1,103 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.backend.cache; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; + +import com.baidu.hugegraph.iterator.MapperIterator; +import com.baidu.hugegraph.util.E; + +public class ThreadLocalCache extends AbstractCache { + + private final List> allCaches; + private final ThreadLocal> localCache; + + public ThreadLocalCache(long capacity) { + super(capacity); + this.allCaches = new CopyOnWriteArrayList<>(); + this.localCache = new ThreadLocal<>(); + } + + @Override + public boolean containsKey(K id) { + return this.getOrNewCache().containsKey(id); + } + + @Override + public void traverse(Consumer consumer) { + E.checkNotNull(consumer, "consumer"); + this.getOrNewCache().values().forEach(consumer); + } + + @Override + public void clear() { + this.allCaches.forEach(HashMap::clear); + } + + @Override + protected V access(K id) { + return this.getOrNewCache().get(id); + } + + @Override + protected boolean write(K id, V value) { + this.getOrNewCache(); + this.allCaches.forEach(cache -> cache.put(id, value)); + return true; + } + + @Override + protected void remove(K id) { + this.allCaches.forEach(cache -> cache.remove(id)); + } + + @Override + protected Iterator> nodes() { + Iterator> iter = this.getOrNewCache().entrySet().iterator(); + return new MapperIterator<>(iter, kvEntry -> { + return new CacheNode<>(kvEntry.getKey(), kvEntry.getValue()); + }); + } + + @Override + public long size() { + return this.getOrNewCache().size(); + } + + private Cache getOrNewCache() { + Cache cache = this.localCache.get(); + if (cache == null) { + cache = new Cache<>(); + this.localCache.set(cache); + this.allCaches.add(cache); + } + return cache; + } + + private static class Cache extends HashMap { + + private static final long serialVersionUID = -3426955734239657957L; + } +}