net.sf.ehcache.store.cachingtier.PooledBasedBackEnd Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/**
* Copyright Terracotta, Inc.
*
* Licensed 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 net.sf.ehcache.store.cachingtier;
import net.sf.ehcache.Element;
import net.sf.ehcache.pool.PoolAccessor;
import net.sf.ehcache.store.Policy;
import net.sf.ehcache.util.concurrent.ConcurrentHashMap;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terracotta.statistics.observer.OperationObserver;
import net.sf.ehcache.store.StoreOperationOutcomes.GetOutcome;
import org.terracotta.statistics.OperationStatistic;
import org.terracotta.statistics.StatisticsManager;
import org.terracotta.statistics.derived.EventRateSimpleMovingAverage;
import org.terracotta.statistics.derived.OperationResultFilter;
import static net.sf.ehcache.statistics.StatisticBuilder.operation;
/**
* A backend to a OnHeapCachingTier that will be cap'ed using a pool
*
* @param the key type
* @param the value type
*
* @author Alex Snaps
*/
public class PooledBasedBackEnd extends ConcurrentHashMap implements HeapCacheBackEnd {
private static final Logger LOG = LoggerFactory.getLogger(CountBasedBackEnd.class.getName());
private static final int MAX_EVICTIONS = 5;
private static final float PUT_LOAD_THRESHOLD = 0.9f;
private volatile Policy policy;
private volatile RemovalCallback callback;
private final AtomicReference poolAccessor = new AtomicReference();
private final OperationObserver getObserver = operation(GetOutcome.class).named("arc-get").of(this).tag("private").build();
/**
* Constructs a Pooled backend
* @param memoryEvictionPolicy the policy it'll use to decide what to evict
*/
public PooledBasedBackEnd(final Policy memoryEvictionPolicy) {
setPolicy(memoryEvictionPolicy);
}
@Override
public V putIfAbsent(final K key, final V value) {
long delta = poolAccessor.get().add(key, value, FAKE_TREE_NODE, false);
if (delta > -1) {
final V previous = (V)super.internalPutIfAbsent(key, value, delta > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)delta);
if (previous != null) {
poolAccessor.get().delete(delta);
}
return previous;
} else {
final RemovalCallback cb = callback;
if (cb != null) {
cb.removed(key, value);
}
return null;
}
}
@Override
public V get(final Object key) {
getObserver.begin();
final V value = super.get(key);
if (value != null) {
getObserver.end(GetOutcome.HIT);
} else {
getObserver.end(GetOutcome.MISS);
}
return value;
}
@Override
public void putAll(final Map m) {
throw new UnsupportedOperationException();
}
@Override
public V put(final K key, final V value) {
throw new UnsupportedOperationException();
}
@Override
public V remove(final Object key) {
return super.removeAndNotify(key, callback);
}
@Override
public boolean remove(final Object key, final Object value) {
return super.remove(key, value);
}
@Override
public boolean replace(final K key, final V oldValue, final V newValue) {
return super.replace(key, oldValue, newValue);
}
@Override
public V replace(final K key, final V value) {
throw new UnsupportedOperationException();
}
@Override
public void clear(final boolean notify) {
if (notify) {
for (Map.Entry entry : entrySet()) {
if (entry.getValue() instanceof Element) {
removeAndNotify(entry.getKey(), entry.getValue(), callback);
}
}
} else {
super.clear();
}
}
@Override
public boolean hasSpace() {
PoolAccessor accessor = poolAccessor.get();
return accessor.getPoolOccupancy() < (PUT_LOAD_THRESHOLD * accessor.getPoolSize());
}
/**
* tries to evict as many entries as specified
* @param evictions amount of entries to be evicted
* @return return true if exactly the right amount of evictions could happen, false otherwise
*/
public boolean evict(int evictions) {
while (evictions-- > 0) {
final Element evictionCandidate = findEvictionCandidate();
if (evictionCandidate != null) {
remove(evictionCandidate.getObjectKey(), evictionCandidate, callback);
} else {
return false;
}
}
return true;
}
private Element findEvictionCandidate() {
List values = getRandomValues(MAX_EVICTIONS);
// this can return null. Let the cache get bigger by one.
List elements = new ArrayList(values.size() * 2);
for (V v : values) {
if (v instanceof Element) {
elements.add((Element)v);
}
}
return policy.selectedBasedOnPolicy(elements.toArray(new Element[elements.size()]), null);
}
/**
* Dynamic property to switch the policy out
* @param policy the new eviction Policy to use
*/
public void setPolicy(final Policy policy) {
if (policy == null) {
throw new NullPointerException("We need a Policy passed in here, null won't cut it!");
}
this.policy = policy;
}
@Override
public void registerEvictionCallback(final EvictionCallback evictionCallback) {
this.callback = evictionCallback == null ? null : new RemovalCallback() {
@Override
public void removed(final Object key, final Object value) {
evictionCallback.evicted((K)key, (V)value);
}
};
}
@Override
public Policy getPolicy() {
return policy;
}
/**
* Registers the accessor with the backend. This can only happen once!
* @param poolAccessor the pool accessor to use
*/
public void registerAccessor(final PoolAccessor poolAccessor) {
if (poolAccessor == null) {
throw new NullPointerException("No null poolAccessor allowed here!");
}
if (!this.poolAccessor.compareAndSet(null, poolAccessor)) {
throw new IllegalStateException("Can't set the poolAccessor multiple times!");
}
super.setPoolAccessor(poolAccessor);
}
/**
* Returns the size in bytes
* @return the amount of bytes for this backend
*/
@Deprecated
public long getSizeInBytes() {
return poolAccessor.get().getSize();
}
/**
* A pool participant to use with this Backend
*/
public static class PoolParticipant implements net.sf.ehcache.pool.PoolParticipant {
private final EventRateSimpleMovingAverage hitRate = new EventRateSimpleMovingAverage(1, TimeUnit.SECONDS);
private final EventRateSimpleMovingAverage missRate = new EventRateSimpleMovingAverage(1, TimeUnit.SECONDS);
private final PooledBasedBackEnd