Created
April 24, 2015 19:01
-
-
Save wezell/4c8994ce9bea9c1ee03a to your computer and use it in GitHub Desktop.
Off Heap dotCMS Cache Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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