@Override public String getName() { if (refreshAheadConfig.getName() != null) { return refreshAheadConfig.getName(); } return super.getName(); }
private void initSupportCache() { // create the support cache // make this cache clustered in the same way as the underlying cache, this.supportConfig = new CacheConfiguration(); supportConfig.name( underlyingCache.getName() + "_" + getClass().getName() + "_refreshAheadSupport"); supportConfig = supportConfig.persistence(new PersistenceConfiguration().strategy(Strategy.NONE)); int activeSize = 2 * refreshAheadConfig.getBatchSize() * refreshAheadConfig.getNumberOfThreads(); supportConfig = supportConfig.maxEntriesLocalHeap(activeSize); supportConfig = supportConfig.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU); supportConfig = supportConfig.timeToLiveSeconds(DEFAULT_SUPPORT_TTL_SECONDS); // TC stuff if (underlyingCache.getCacheConfiguration().isTerracottaClustered()) { supportConfig = supportConfig.persistence(new PersistenceConfiguration().strategy(Strategy.DISTRIBUTED)); TerracottaConfiguration newTerracottaConfig = new TerracottaConfiguration().clustered(true); newTerracottaConfig.consistency(Consistency.STRONG); supportConfig.addTerracotta(newTerracottaConfig); } else { supportConfig.setMaxElementsOnDisk(activeSize); } // here we try to create the support cache. this.supportCache = new Cache(supportConfig); Ehcache prior = underlyingCache.getCacheManager().addCacheIfAbsent(supportCache); if (prior != supportCache) { throw new IllegalStateException( "Unable to add refresh ahead support cache due to name collision: " + refreshAheadConfig.getName()); } // wipe it on startup. might wobble in a clustered case, but clears out orphans. prior.removeAll(); // catch the dispose. not sure this is the best way to do it at all. // we could register a listener alternatively underlyingCache.registerCacheExtension( new CacheExtension() { @Override public void init() {} @Override public Status getStatus() { return underlyingCache.getStatus(); } @Override public void dispose() throws CacheException { RefreshAheadCache.this.localDispose(); } @Override public CacheExtension clone(Ehcache cache) throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }); }
@Override public Element get(Serializable key) throws IllegalStateException, CacheException { Element elem = super.get(key); possiblyTriggerRefresh(elem, refreshAheadConfig.getTimeToRefreshMillis()); return elem; }
private void initWorkQueue() { BatchWorker<Object> batchWorker = new BatchWorker<Object>() { @Override public void process(Collection<? extends Object> collection) { // only fetch this once for each process() call long accessTime = System.currentTimeMillis(); HashSet<Object> keysToProcess = new HashSet<Object>(); for (Object key : collection) { // check if it was loaded by someone else in the meantime -- does it still qualify for // refresh ahead? Element quickTest = underlyingCache.getQuiet(key); if (quickTest == null || checkForRefresh( quickTest, accessTime, refreshAheadConfig.getTimeToRefreshMillis())) { final Element ersatz = new Element(key, REFRESH_VALUE); if (supportCache.putIfAbsent(ersatz) == null) { // work, work, work keysToProcess.add(key); } } } try { // iterate through the loaders for (CacheLoader loader : underlyingCache.getRegisteredCacheLoaders()) { // if we are out of keys, punt if (keysToProcess.isEmpty()) { break; } // try and load them all Map<? extends Object, ? extends Object> values = loader.loadAll(keysToProcess); // subtract the ones that were loaded keysToProcess.removeAll(values.keySet()); try { for (Map.Entry<? extends Object, ? extends Object> entry : values.entrySet()) { Element newElement = new Element(entry.getKey(), entry.getValue()); underlyingCache.put(newElement); refreshSuccessCount.incrementAndGet(); } } finally { // subtract from the support cache supportCache.removeAll(values.keySet()); } } // assume we got here ok, now evict any that don't evict if (refreshAheadConfig.isEvictOnLoadMiss() && !keysToProcess.isEmpty()) { underlyingCache.removeAll(keysToProcess); } } finally { // this is utterly paranoid. but still. supportCache.removeAll(keysToProcess); } } }; this.refreshWorkQueue = new ThreadedWorkQueue<Object>( batchWorker, refreshAheadConfig.getNumberOfThreads(), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } }, refreshAheadConfig.getMaximumRefreshBacklogItems(), refreshAheadConfig.getBatchSize()); }