Skip to content

Instantly share code, notes, and snippets.

@wezell
Created April 24, 2015 19:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wezell/4c8994ce9bea9c1ee03a to your computer and use it in GitHub Desktop.
Save wezell/4c8994ce9bea9c1ee03a to your computer and use it in GitHub Desktop.
Off Heap dotCMS Cache Implementation
package com.dotmarketing.business;
import java.io.StringWriter;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.HTreeMap;
import org.mapdb.Serializer;
import org.mapdb.Store;
import com.dotcms.repackage.com.google.common.cache.Cache;
import com.dotmarketing.db.DbConnectionFactory;
import com.dotmarketing.db.HibernateUtil;
import com.dotmarketing.util.Config;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UtilMethods;
public class MapDBCacheAdministratorImpl extends DotGuavaCacheAdministratorImpl {
private final ConcurrentHashMap<String, Cache<String, Object>> groups = new ConcurrentHashMap<String, Cache<String, Object>>();
long lastError = 0;
final double cacheSizeInGB = Config.getFloatProperty("OFF_HEAP_MEMORY_CACHE_SIZE_IN_GB", 1.0f);
HTreeMap<String, Object> cache;
private Map<String, Object> cacheStatus;
Store store ;
@Override
public List<Map<String, Object>> getCacheStatsList() {
System.out.println("Map size: " + cache.sizeLong());
System.out.println("Store size: " + UtilMethods.prettyMemory(store.getCurrSize()));
System.out.println("Free size: " + UtilMethods.prettyMemory(store.getFreeSize()));
return super.getCacheStatsList();
}
DB db = null;
@Override
public String getCacheStats() {
return super.getCacheStats();
}
final char delimit = ';';
public MapDBCacheAdministratorImpl() {
//first create store
db = DBMaker
.newMemoryDirectDB()
.transactionDisable()
//some additional options for DB
// .asyncWriteEnable()
// .cacheSize(100000)
.make();
cache = db
.createHashMap("cache")
.expireStoreSize(cacheSizeInGB)
.counterEnable() //disable this if cache.size() is not used
//use proper serializers to and improve performance
.keySerializer(Serializer.STRING)
.make();
store = Store.forDB(db);
}
/*
* (non-Javadoc)
*
* @see com.dotmarketing.business.DotCacheAdministrator#flushAll()
*/
public void flushAll() {
cache.clear();
}
/*
* (non-Javadoc)
*
* @see
* com.dotmarketing.business.DotCacheAdministrator#flushGroup(java.lang.
* String)
*/
public void flushGroup(String group) {
Set<String> keys = this.getKeys(group);
for(String key : keys){
cache.remove(key);
}
}
/*
* (non-Javadoc)
*
* @see com.dotmarketing.business.DotCacheAdministrator#flushAll()
*/
public void flushAlLocalOnlyl() {
cache.clear();
}
public void flushGroupLocalOnly(String group) {
Logger.warn(this.getClass(), "clearing all acahe. Should only be clearing "+ group);
cache.clear();
}
/**
* Gets from Memory, if not in memory, tries disk
*/
public Object get(String key, String group) throws DotCacheException {
if (key == null || group == null) {
return null;
}
StringWriter k = new StringWriter();
k.append(key.toLowerCase());
k.append(delimit);
k.append(group.toLowerCase());
return cache.get(k.toString());
}
/*
* (non-Javadoc)
*
* @see
* com.dotmarketing.business.DotCacheAdministrator#put(java.lang.String,
* java.lang.Object, java.lang.String[])
*/
public void put(String key, final Object content, String group) {
if (key == null || group == null) {
return;
}
// velocity macros cannot be cached
if(group.equals("VelocityCache") && key.endsWith(".vm")){
return;
}
StringWriter k = new StringWriter();
k.append(key.toLowerCase());
k.append(delimit);
k.append(group.toLowerCase());
cache.put(k.toString(), content);
Object x = cache.get(k.toString());
}
private void slowLogger(Exception e, boolean stack) {
if (System.currentTimeMillis() - lastError > 10000) {
lastError = System.currentTimeMillis();
Logger.error(this.getClass(), "---- Redis is deadis:--- vvvvvv");
Logger.error(this.getClass(), "---- Redis is deadis:" + Thread.currentThread().getStackTrace()[2]);
if (stack) {
Logger.error(this.getClass(), "---- Redis is deadis:" + e.getMessage(), e);
} else {
Logger.error(this.getClass(), "---- Redis is deadis:" + e.getMessage());
}
Logger.error(this.getClass(), "---- Redis is deadis:--- ^^^^^^");
}
}
/*
* (non-Javadoc)
*
* @see
* com.dotmarketing.business.DotCacheAdministrator#remove(java.lang.String)
*/
public void remove(final String key, final String group) {
if (key == null || group == null) {
return;
}
Runnable cacheRemoveRunnable = new Runnable() {
public void run() {
String k = key.toLowerCase();
String g = group.toLowerCase();
removeLocalOnly(k, g);
}
};
try {
if(DbConnectionFactory.inTransaction()){
HibernateUtil.addCommitListener(cacheRemoveRunnable);
}
} catch (Exception e) {
Logger.error(DotGuavaCacheAdministratorImpl.class,e.getMessage(),e);
}
cacheRemoveRunnable.run();
}
/*
* This method should only be called by Jgroups because it doesn't handle
* any local transaction as the remove does.
*/
public void removeLocalOnly(final String key, final String group) {
if (key == null || group == null) {
return;
}
Runnable cacheRemoveRunnable = new Runnable() {
public void run() {
StringWriter k = new StringWriter();
k.append(key.toLowerCase());
k.append(delimit);
k.append(group.toLowerCase());
cache.remove(k);
}
};
cacheRemoveRunnable.run();
}
public Set<String> getKeys(String group) {
group = group.toLowerCase();
Set<String> keys = new HashSet();
for(String x : cache.keySet()){
if(x.contains(group)){
keys.add(x);
}
}
return keys;
}
private class CacheComparator implements Comparator<Map<String, Object>> {
static final String LIVE_CACHE_PREFIX = "livecache";
static final String WORKING_CACHE_PREFIX = "workingcache";
public int compare(Map<String, Object> o1, Map<String, Object> o2) {
if (o1 == null && o2 != null)
return 1;
if (o1 != null && o2 == null)
return -1;
if (o1 == null && o2 == null)
return 0;
String group1 = (String) o1.get("region");
String group2 = (String) o2.get("region");
if (!UtilMethods.isSet(group1) && !UtilMethods.isSet(group2)) {
return 0;
} else if (UtilMethods.isSet(group1) && !UtilMethods.isSet(group2)) {
return -1;
} else if (!UtilMethods.isSet(group1) && UtilMethods.isSet(group2)) {
return 1;
} else if (group1.equals(group2)) {
return 0;
} else if (group1.startsWith(WORKING_CACHE_PREFIX) && group2.startsWith(LIVE_CACHE_PREFIX)) {
return 1;
} else if (group1.startsWith(LIVE_CACHE_PREFIX) && group2.startsWith(WORKING_CACHE_PREFIX)) {
return -1;
} else if (!group1.startsWith(LIVE_CACHE_PREFIX) && !group1.startsWith(WORKING_CACHE_PREFIX)
&& (group2.startsWith(LIVE_CACHE_PREFIX) || group2.startsWith(WORKING_CACHE_PREFIX))) {
return -1;
} else if ((group1.startsWith(LIVE_CACHE_PREFIX) || group1.startsWith(WORKING_CACHE_PREFIX))
&& !group2.startsWith(LIVE_CACHE_PREFIX) && !group2.startsWith(WORKING_CACHE_PREFIX)) {
return 1;
} else { // neither group1 nor group2 are live or working
return group1.compareToIgnoreCase(group2);
}
}
}
public void shutdown() {
if(db !=null) db.close();
}
@Override
public Class getImplementationClass() {
return this.getClass();
}
@Override
public DotCacheAdministrator getImplementationObject() {
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment