
net.sf.ehcache.store.cachingtier.CountBasedBackEnd Maven / Gradle / Ivy
The newest version!
/**
* 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.store.LruPolicy;
import net.sf.ehcache.store.Policy;
import net.sf.ehcache.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
*
* A backend to a OnHeapCachingTier that will be cap'ed based on the amount of entries
*
* @param the key type
* @param the value type
*
* @author Alex Snaps
*/
public class CountBasedBackEnd extends ConcurrentHashMap implements HeapCacheBackEnd {
private static final Logger LOG = LoggerFactory.getLogger(CountBasedBackEnd.class.getName());
private static final int MAX_EVICTIONS = 5;
private static final int SAMPLING_SIZE = 30;
private volatile long maxEntriesLocalHeap;
private volatile Policy policy;
private volatile RemovalCallback callback;
/**
* Constructs a cap'ed backend
* @param maxEntriesLocalHeap amount of mappings this should hold before it starts evicting
*/
public CountBasedBackEnd(final long maxEntriesLocalHeap) {
this(maxEntriesLocalHeap, new LruPolicy());
}
/**
* Constructs a cap'ed backend
* @param maxEntriesLocalHeap amount of mappings this should hold before it starts evicting
* @param policy the policy it'll use to decide what to evict
*/
public CountBasedBackEnd(final long maxEntriesLocalHeap, final Policy policy) {
this.maxEntriesLocalHeap = maxEntriesLocalHeap;
setPolicy(policy);
}
/**
* 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;
}
/**
* {@inheritDoc}
*/
@Override
public V putIfAbsent(final K key, final V value) {
final V v = super.putIfAbsent(key, value);
if (v == null) {
try {
evictIfRequired(key, value);
} catch (Throwable e) {
LOG.warn("Caught throwable while evicting", e);
}
}
return v;
}
/**
* {@inheritDoc}
*/
@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 void recalculateSize(final K key) {
// NO OP!
}
@Override
public V remove(final Object key) {
return super.removeAndNotify(key, callback);
}
@Override
public Policy getPolicy() {
return policy;
}
@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() {
return maxEntriesLocalHeap == 0 || maxEntriesLocalHeap > mappingCount();
}
private void evictIfRequired(final K key, final V value) {
if (maxEntriesLocalHeap == 0) {
return;
}
int evictions = MAX_EVICTIONS;
while (maxEntriesLocalHeap < mappingCount() && evictions-- > 0) {
final Element evictionCandidate = findEvictionCandidate(key, value);
if (evictionCandidate != null) {
remove(evictionCandidate.getObjectKey(), evictionCandidate, callback);
}
}
}
private Element findEvictionCandidate(final K key, final V value) {
List values = getRandomValues(SAMPLING_SIZE);
// 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 && !((Element)v).getObjectKey().equals(key)) {
elements.add((Element)v);
}
}
return policy.selectedBasedOnPolicy(elements.toArray(new Element[elements.size()]),
value instanceof Element ? (Element)value : null);
}
/**
* Sets the capacity limit
* @param maxEntriesLocalHeap the new limit
*/
public void setMaxEntriesLocalHeap(final long maxEntriesLocalHeap) {
this.maxEntriesLocalHeap = maxEntriesLocalHeap;
}
/**
* Reads the current capacity limit
* @return the capacity limit
*/
public long getMaxEntriesLocalHeap() {
return maxEntriesLocalHeap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy