blob: c7a43e228eaeab80ee4146a08ca3f1e4e0daa77f [file] [log] [blame]
/*
* 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 org.apache.hugegraph.backend.cache;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.hugegraph.HugeGraph;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.util.E;
import org.apache.hugegraph.util.Log;
import org.slf4j.Logger;
public class CacheManager {
private static final Logger LOG = Log.logger(CacheManager.class);
private static final CacheManager INSTANCE = new CacheManager();
// Check the cache expiration every 30s by default
private static final long TIMER_TICK_PERIOD = 30;
// Log if tick cost time > 1000ms
private static final long LOG_TICK_COST_TIME = 1000L;
private final Map<String, Cache<Id, ?>> caches;
private final Timer timer;
public static CacheManager instance() {
return INSTANCE;
}
public static boolean cacheEnableMetrics(String name, boolean enabled) {
Cache<Id, ?> cache = INSTANCE.caches.get(name);
E.checkArgument(cache != null,
"Not found cache named '%s'", name);
return cache.enableMetrics(enabled);
}
private CacheManager() {
this.caches = new ConcurrentHashMap<>();
this.timer = new Timer("cache-expirer", true);
this.scheduleTimer(TIMER_TICK_PERIOD);
}
private TimerTask scheduleTimer(float period) {
TimerTask task = new TimerTask() {
@Override
public void run() {
try {
for (Entry<String, Cache<Id, Object>> entry :
caches().entrySet()) {
this.tick(entry.getKey(), entry.getValue());
}
} catch (Throwable e) {
LOG.warn("An exception occurred when running tick", e);
}
}
private void tick(String name, Cache<Id, Object> cache) {
long start = System.currentTimeMillis();
long items = cache.tick();
long cost = System.currentTimeMillis() - start;
if (cost > LOG_TICK_COST_TIME) {
LOG.info("Cache '{}' expired {} items cost {}ms > {}ms " +
"(size {}, expire {}ms)", name, items, cost,
LOG_TICK_COST_TIME, cache.size(), cache.expire());
}
LOG.debug("Cache '{}' expiration tick cost {}ms", name, cost);
}
};
// Schedule task with the period in seconds
this.timer.schedule(task, 0, (long) (period * 1000.0));
return task;
}
public <V> Map<String, Cache<Id, V>> caches() {
@SuppressWarnings({"rawtypes", "unchecked"})
Map<String, Cache<Id, V>> caches = (Map) this.caches;
return Collections.unmodifiableMap(caches);
}
public <V> Cache<Id, V> cache(String name) {
return this.cache(name, RamCache.DEFAULT_SIZE);
}
public <V> Cache<Id, V> cache(String name, long capacity) {
if (!this.caches.containsKey(name)) {
this.caches.putIfAbsent(name, new RamCache(capacity));
LOG.info("Init RamCache for '{}' with capacity {}",
name, capacity);
}
@SuppressWarnings("unchecked")
Cache<Id, V> cache = (Cache<Id, V>) this.caches.get(name);
E.checkArgument(cache instanceof RamCache,
"Invalid cache implement: %s", cache.getClass());
return cache;
}
public <V> Cache<Id, V> offheapCache(HugeGraph graph, String name,
long capacity, long avgElemSize) {
if (!this.caches.containsKey(name)) {
OffheapCache cache = new OffheapCache(graph, capacity, avgElemSize);
this.caches.putIfAbsent(name, cache);
LOG.info("Init OffheapCache for '{}' with capacity {}",
name, capacity);
}
@SuppressWarnings("unchecked")
Cache<Id, V> cache = (Cache<Id, V>) this.caches.get(name);
E.checkArgument(cache instanceof OffheapCache,
"Invalid cache implement: %s", cache.getClass());
return cache;
}
public <V> Cache<Id, V> levelCache(HugeGraph graph, String name,
long capacity1, long capacity2,
long avgElemSize) {
if (!this.caches.containsKey(name)) {
RamCache cache1 = new RamCache(capacity1);
OffheapCache cache2 = new OffheapCache(graph, capacity2,
avgElemSize);
this.caches.putIfAbsent(name, new LevelCache(cache1, cache2));
LOG.info("Init LevelCache for '{}' with capacity {}:{}",
name, capacity1, capacity2);
}
@SuppressWarnings("unchecked")
Cache<Id, V> cache = (Cache<Id, V>) this.caches.get(name);
E.checkArgument(cache instanceof LevelCache,
"Invalid cache implement: %s", cache.getClass());
return cache;
}
}