com.baidu.hugegraph.backend.cache.RamCache Maven / Gradle / Ivy
The newest version!
/*
* 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.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.function.Consumer;
import com.baidu.hugegraph.backend.id.Id;
import com.baidu.hugegraph.concurrent.KeyLock;
import com.baidu.hugegraph.perf.PerfUtil.Watched;
import com.baidu.hugegraph.util.E;
public class RamCache extends AbstractCache {
// Implement LRU cache
private final ConcurrentMap> map;
private final LinkedQueueNonBigLock queue;
private final KeyLock keyLock;
public RamCache() {
this(DEFAULT_SIZE);
}
public RamCache(long capacity) {
super(capacity);
this.keyLock = new KeyLock();
if (capacity < 0L) {
capacity = 0L;
}
long initialCapacity = capacity >= MB ? capacity >> 10 : 256;
if (initialCapacity > MAX_INIT_CAP) {
initialCapacity = MAX_INIT_CAP;
}
this.map = new ConcurrentHashMap<>((int) initialCapacity);
this.queue = new LinkedQueueNonBigLock<>();
}
@Override
@Watched(prefix = "ramcache")
protected final Object access(Id id) {
assert id != null;
long halfCapacity = this.halfCapacity();
if (this.map.size() <= halfCapacity) {
LinkNode node = this.map.get(id);
if (node == null) {
return null;
}
assert id.equals(node.key());
return node.value();
}
final Lock lock = this.keyLock.lock(id);
try {
LinkNode node = this.map.get(id);
if (node == null) {
return null;
}
// NOTE: update the queue only if the size > capacity/2
if (this.map.size() > halfCapacity) {
// Move the node from mid to tail
if (this.queue.remove(node) == null) {
// The node may be removed by others through dequeue()
return null;
}
this.queue.enqueue(node);
}
assert id.equals(node.key());
return node.value();
} finally {
lock.unlock();
}
}
@Override
@Watched(prefix = "ramcache")
protected final boolean write(Id id, Object value) {
assert id != null;
long capacity = this.capacity();
assert capacity > 0;
final Lock lock = this.keyLock.lock(id);
try {
// The cache is full
while (this.map.size() >= capacity) {
/*
* Remove the oldest from the queue
* NOTE: it maybe return null if someone else (that's other
* threads) are doing dequeue() and the queue may be empty.
*/
LinkNode removed = this.queue.dequeue();
if (removed == null) {
/*
* If at this time someone add some new items, these will
* be cleared in the map, but still stay in the queue, so
* the queue will have some more nodes than the map.
*/
this.map.clear();
break;
}
/*
* Remove the oldest from the map
* NOTE: it maybe return null if other threads are doing remove
*/
this.map.remove(removed.key());
if (LOG.isDebugEnabled()) {
LOG.debug("RamCache replaced '{}' with '{}' (capacity={})",
removed.key(), id, capacity);
}
/*
* Release the object
* NOTE: we can't reuse the removed node due to someone else
* may access the node (will do remove() -> enqueue())
*/
removed = null;
}
// Remove the old node if exists
LinkNode node = this.map.get(id);
if (node != null) {
this.queue.remove(node);
}
// Add the new item to tail of the queue, then map it
this.map.put(id, this.queue.enqueue(id, value));
return true;
} finally {
lock.unlock();
}
}
@Override
@Watched(prefix = "ramcache")
protected final void remove(Id id) {
if (id == null) {
return;
}
assert id != null;
final Lock lock = this.keyLock.lock(id);
try {
/*
* Remove the id from map and queue
* NOTE: it maybe return null if other threads have removed the id
*/
LinkNode node = this.map.remove(id);
if (node != null) {
this.queue.remove(node);
}
} finally {
lock.unlock();
}
}
@Override
protected Iterator> nodes() {
Iterator> iter = this.map.values().iterator();
@SuppressWarnings({ "unchecked", "rawtypes" })
Iterator> iterSuper = (Iterator) iter;
return iterSuper;
}
@Override
public boolean containsKey(Id id) {
return this.map.containsKey(id);
}
@Watched(prefix = "ramcache")
@Override
public void traverse(Consumer
© 2015 - 2025 Weber Informatics LLC | Privacy Policy