Skip to content

Commit

Permalink
Introduces custom eviction policy
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmetmircik committed Apr 28, 2016
1 parent d744fdd commit 81d6bb3
Show file tree
Hide file tree
Showing 27 changed files with 512 additions and 253 deletions.
Expand Up @@ -81,6 +81,7 @@
import com.hazelcast.config.WanPublisherConfig;
import com.hazelcast.config.WanReplicationConfig;
import com.hazelcast.config.WanReplicationRef;
import com.hazelcast.map.eviction.MapEvictionPolicy;
import com.hazelcast.memory.MemorySize;
import com.hazelcast.memory.MemoryUnit;
import com.hazelcast.nio.ClassLoaderUtil;
Expand All @@ -104,6 +105,7 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.hazelcast.util.Preconditions.checkHasText;
import static com.hazelcast.util.StringUtil.upperCaseInternal;

/**
Expand Down Expand Up @@ -645,11 +647,38 @@ public void handleMap(Node node) {
mapConfigBuilder.addPropertyValue("partitionLostListenerConfigs", listeners);
} else if ("hot-restart".equals(nodeName)) {
handleHotRestartConfig(mapConfigBuilder, childNode);
} else if ("map-eviction-policy".equals(nodeName)) {
handleMapEvictionPolicyConfig(mapConfigBuilder, childNode);
}
}
mapConfigManagedMap.put(name, beanDefinition);
}

private void handleMapEvictionPolicyConfig(BeanDefinitionBuilder mapConfigBuilder, Node childNode) {
NamedNodeMap attributes = childNode.getAttributes();
Node implementationNode = attributes.getNamedItem("implementation");
Node classNameNode = attributes.getNamedItem("class-name");

String implementation = implementationNode != null ? getTextContent(implementationNode) : null;
String className = classNameNode != null ? getTextContent(classNameNode) : null;

if (implementation != null) {
mapConfigBuilder.addPropertyReference("mapEvictionPolicy", implementation);
} else if (className != null) {
className = checkHasText(className, "map-eviction-policy `className` cannot be null or empty");
try {
MapEvictionPolicy mapEvictionPolicy = ClassLoaderUtil.newInstance(getClass().getClassLoader(), className);
mapConfigBuilder.addPropertyValue("mapEvictionPolicy", mapEvictionPolicy);

} catch (Exception e) {
throw ExceptionUtil.rethrow(e);
}
} else {
throw new IllegalArgumentException("One of `className` or `implementation`"
+ " attributes is required to create map-eviction-policy");
}
}

private void handleHotRestartConfig(BeanDefinitionBuilder configBuilder, Node node) {
BeanDefinitionBuilder hotRestartConfigBuilder = createBeanBuilder(HotRestartConfig.class);
fillAttributeValues(node, hotRestartConfigBuilder);
Expand Down
15 changes: 14 additions & 1 deletion hazelcast-spring/src/main/resources/hazelcast-spring-3.7.xsd
Expand Up @@ -254,6 +254,14 @@
</xs:complexType>
</xs:element>
<xs:element name="hot-restart" type="hot-restart" minOccurs="0" maxOccurs="1" />
<xs:element name="map-eviction-policy" type="map-eviction-policy" minOccurs="0" maxOccurs="1">
<xs:annotation>
<xs:documentation>
Internal eviction algorithm finds most appropriate entry
to evict from this map by using this policy.
</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
<xs:attribute name="name" use="required" type="xs:string"/>
<xs:attribute name="in-memory-format" use="optional" type="xs:string" default="BINARY">
Expand Down Expand Up @@ -284,7 +292,7 @@

When both optimize-query and cache-deserialized-values are used at the same time
Hazelcast will do its best to detect possible conflicts. Conflict detection
is done on best-effort basis and you should not rely on it.
is done on best-effort basis and you should not rely on it.

This parameter is used to increase the speed of query processes in the map.
It only works when `in-memory-format` is set as `BINARY` and performs
Expand Down Expand Up @@ -1523,6 +1531,11 @@
<xs:attribute name="implementation" type="xs:string" use="optional"/>
</xs:complexType>

<xs:complexType name="map-eviction-policy">
<xs:attribute name="class-name" type="xs:string" use="optional"/>
<xs:attribute name="implementation" type="xs:string" use="optional"/>
</xs:complexType>

<xs:complexType name="outbound-ports">
<xs:sequence>
<xs:element name="ports" minOccurs="0" maxOccurs="unbounded">
Expand Down
Expand Up @@ -14,17 +14,15 @@
* limitations under the License.
*/

package com.hazelcast.map.impl.eviction.policies;
package com.hazelcast.spring;

import com.hazelcast.core.EntryView;
import com.hazelcast.map.eviction.MapEvictionPolicy;

/**
* LFU {@link MapEvictionPolicy} implementation.
*/
public class LFUMapEvictionPolicy extends AbstractMapEvictionPolicy {
public class DummyMapEvictionPolicy extends MapEvictionPolicy {

@Override
public boolean compare(EntryView selected, EntryView candidate) {
return candidate.getHits() < selected.getHits();
public int compare(EntryView o1, EntryView o2) {
return 0;
}
}
Expand Up @@ -235,7 +235,7 @@ public void testCacheConfig() {
@Test
public void testMapConfig() {
assertNotNull(config);
assertEquals(14, config.getMapConfigs().size());
assertEquals(17, config.getMapConfigs().size());

MapConfig testMapConfig = config.getMapConfig("testMap");
assertNotNull(testMapConfig);
Expand Down Expand Up @@ -809,4 +809,29 @@ public void testHotRestart() {
assertEquals(2222, hotRestartPersistenceConfig.getDataLoadTimeoutSeconds());
}

@Test
public void testMapEvictionPolicyClassName() {
MapConfig mapConfig = config.getMapConfig("mapWithMapEvictionPolicyClassName");
String expectedComparatorClassName = "com.hazelcast.map.eviction.LRUEvictionPolicy";

assertEquals(expectedComparatorClassName, mapConfig.getMapEvictionPolicy().getClass().getName());
}

@Test
public void testMapEvictionPolicyImpl() {
MapConfig mapConfig = config.getMapConfig("mapWithMapEvictionPolicyImpl");

assertEquals(DummyMapEvictionPolicy.class, mapConfig.getMapEvictionPolicy().getClass());
}

@Test
public void testWhenBothMapEvictionPolicyClassNameAndEvictionPolicySet() {
MapConfig mapConfig = config.getMapConfig("mapBothMapEvictionPolicyClassNameAndEvictionPolicy");
String expectedComparatorClassName = "com.hazelcast.map.eviction.LRUEvictionPolicy";

assertEquals(expectedComparatorClassName, mapConfig.getMapEvictionPolicy().getClass().getName());
}



}
Expand Up @@ -260,14 +260,27 @@
<hz:map name="mapWithValueCachingSetToNever" cache-deserialized-values="NEVER"/>
<hz:map name="mapWithValueCachingSetToAlways" cache-deserialized-values="ALWAYS"/>
<hz:map name="mapWithDefaultOptimizedQueries"/>
<hz:map name="map-with-native-max-size-policy" max-size-policy="USED_NATIVE_MEMORY_PERCENTAGE" in-memory-format="NATIVE"/>
<hz:map name="map-with-native-max-size-policy" max-size-policy="USED_NATIVE_MEMORY_PERCENTAGE"
in-memory-format="NATIVE" />

<hz:map name="mapWithPartitionLostListener">
<hz:partition-lost-listeners>
<hz:partition-lost-listener class-name="DummyMapPartitionLostListenerImpl" />
</hz:partition-lost-listeners>
</hz:map>

<hz:map name="mapWithMapEvictionPolicyClassName">
<hz:map-eviction-policy class-name="com.hazelcast.map.eviction.LRUEvictionPolicy"/>
</hz:map>

<hz:map name="mapWithMapEvictionPolicyImpl">
<hz:map-eviction-policy implementation="dummyMapEvictionPolicy"/>
</hz:map>

<hz:map name="mapBothMapEvictionPolicyClassNameAndEvictionPolicy" eviction-policy="LRU">
<hz:map-eviction-policy class-name="com.hazelcast.map.eviction.LRUEvictionPolicy"/>
</hz:map>

<hz:cache name="testCache" disable-per-entry-invalidation-events="true">
<hz:wan-replication-ref name="testWan" merge-policy="PUT_IF_ABSENT" republishing-enabled="false">
<hz:filters>
Expand Down Expand Up @@ -407,6 +420,7 @@

<bean id="dummyMapStore" class="com.hazelcast.spring.DummyStore"/>
<bean id="dummyMapStoreFactory" class="com.hazelcast.spring.DummyStoreFactory"/>
<bean id="dummyMapEvictionPolicy" class="com.hazelcast.spring.DummyMapEvictionPolicy"/>
<bean id="dummyWanReplication" class="com.hazelcast.spring.DummyWanReplication"/>
<bean id="dummyMembershipListener" class="com.hazelcast.spring.DummyMembershipListener"/>
<bean id="dummyEntryListener" class="com.hazelcast.spring.DummyEntryListener"/>
Expand Down
63 changes: 58 additions & 5 deletions hazelcast/src/main/java/com/hazelcast/config/MapConfig.java
Expand Up @@ -16,6 +16,10 @@

package com.hazelcast.config;

import com.hazelcast.map.eviction.MapEvictionPolicy;
import com.hazelcast.map.eviction.LFUEvictionPolicy;
import com.hazelcast.map.eviction.LRUEvictionPolicy;
import com.hazelcast.map.eviction.RandomEvictionPolicy;
import com.hazelcast.map.merge.PutIfAbsentMapMergePolicy;
import com.hazelcast.spi.partition.IPartition;

Expand All @@ -25,6 +29,7 @@
import static com.hazelcast.util.Preconditions.checkAsyncBackupCount;
import static com.hazelcast.util.Preconditions.checkBackupCount;
import static com.hazelcast.util.Preconditions.checkFalse;
import static com.hazelcast.util.Preconditions.checkNotNull;
import static com.hazelcast.util.Preconditions.isNotNull;

/**
Expand Down Expand Up @@ -78,6 +83,7 @@ public class MapConfig {
* Default policy for eviction
*/
public static final EvictionPolicy DEFAULT_EVICTION_POLICY = EvictionPolicy.NONE;

/**
* Default policy for merging
*/
Expand Down Expand Up @@ -110,6 +116,8 @@ public class MapConfig {

private EvictionPolicy evictionPolicy = DEFAULT_EVICTION_POLICY;

private MapEvictionPolicy mapEvictionPolicy;

private MapStoreConfig mapStoreConfig = new MapStoreConfig().setEnabled(false);

private NearCacheConfig nearCacheConfig;
Expand Down Expand Up @@ -149,6 +157,7 @@ public class MapConfig {
private boolean optimizeQueryExplicitlyInvoked;
private boolean setCacheDeserializedValuesExplicitlyInvoked;


public MapConfig(String name) {
this.name = name;
}
Expand All @@ -167,6 +176,7 @@ public MapConfig(MapConfig config) {
this.maxIdleSeconds = config.maxIdleSeconds;
this.maxSizeConfig = config.maxSizeConfig != null ? new MaxSizeConfig(config.maxSizeConfig) : null;
this.evictionPolicy = config.evictionPolicy;
this.mapEvictionPolicy = config.mapEvictionPolicy;
this.inMemoryFormat = config.inMemoryFormat;
this.mapStoreConfig = config.mapStoreConfig != null ? new MapStoreConfig(config.mapStoreConfig) : null;
this.nearCacheConfig = config.nearCacheConfig != null ? new NearCacheConfig(config.nearCacheConfig) : null;
Expand Down Expand Up @@ -300,7 +310,6 @@ public int getTotalBackupCount() {
* Returns the evictionPercentage: specified percentage of the map to be evicted
*
* @return the evictionPercentage: specified percentage of the map to be evicted
*
* @deprecated As of version 3.7, eviction mechanism changed.
* It uses a probabilistic algorithm based on sampling. Please see documentation for further details.
*/
Expand All @@ -319,7 +328,6 @@ public int getEvictionPercentage() {
*
* @param evictionPercentage the evictionPercentage to set: the specified percentage of the map to be evicted
* @throws IllegalArgumentException if evictionPercentage is not in the 0-100 range.
*
* @deprecated As of version 3.7, eviction mechanism changed.
* It uses a probabilistic algorithm based on sampling. Please see documentation for further details.
*/
Expand All @@ -341,7 +349,6 @@ public MapConfig setEvictionPercentage(final int evictionPercentage) {
*
* @return number of milliseconds that should pass before asking for the next eviction.
* @since 3.3
*
* @deprecated As of version 3.7, eviction mechanism changed.
* It uses a probabilistic algorithm based on sampling. Please see documentation for further details.
*/
Expand All @@ -359,7 +366,6 @@ public long getMinEvictionCheckMillis() {
*
* @param minEvictionCheckMillis time in milliseconds that should pass before asking for the next eviction
* @since 3.3
*
* @deprecated As of version 3.7, eviction mechanism changed.
* It uses a probabilistic algorithm based on sampling. Please see documentation for further details.
*/
Expand Down Expand Up @@ -442,7 +448,46 @@ public EvictionPolicy getEvictionPolicy() {
* @param evictionPolicy the evictionPolicy to set
*/
public MapConfig setEvictionPolicy(EvictionPolicy evictionPolicy) {
this.evictionPolicy = evictionPolicy;
this.evictionPolicy = checkNotNull(evictionPolicy, "evictionPolicy cannot be null");
this.mapEvictionPolicy = findMatchingMapEvictionPolicy(evictionPolicy);
return this;
}

private static MapEvictionPolicy findMatchingMapEvictionPolicy(EvictionPolicy evictionPolicy) {
switch (evictionPolicy) {
case LRU:
return LRUEvictionPolicy.INSTANCE;
case LFU:
return LFUEvictionPolicy.INSTANCE;
case RANDOM:
return RandomEvictionPolicy.INSTANCE;
case NONE:
return null;
default:
throw new IllegalArgumentException("Not known eviction policy : " + evictionPolicy);

}
}

/**
* Returns custom eviction policy if it is set otherwise returns null.
*
* @return custom eviction policy or null.
*/
public MapEvictionPolicy getMapEvictionPolicy() {
return mapEvictionPolicy;
}

/**
* Sets custom eviction policy implementation for this map.
*
* Internal eviction algorithm finds most appropriate entry to evict from this map by using supplied policy.
*
* @param mapEvictionPolicy custom eviction policy implementation
* @return the updated map configuration
*/
public MapConfig setMapEvictionPolicy(MapEvictionPolicy mapEvictionPolicy) {
this.mapEvictionPolicy = checkNotNull(mapEvictionPolicy, "mapEvictionPolicy cannot be null");
return this;
}

Expand Down Expand Up @@ -841,6 +886,10 @@ public int hashCode() {
* result
+ ((this.evictionPolicy == null) ? 0 : this.evictionPolicy
.hashCode());
result = prime
* result
+ ((this.mapEvictionPolicy == null) ? 0 : this.mapEvictionPolicy
.hashCode());
result = prime
* result
+ ((this.mapStoreConfig == null) ? 0 : this.mapStoreConfig
Expand Down Expand Up @@ -887,6 +936,8 @@ public boolean equals(Object obj) {
: other.inMemoryFormat == null)
&& (this.evictionPolicy != null ? this.evictionPolicy.equals(other.evictionPolicy)
: other.evictionPolicy == null)
&& (this.mapEvictionPolicy != null ? this.mapEvictionPolicy.equals(other.mapEvictionPolicy)
: other.mapEvictionPolicy == null)
&& (this.mapStoreConfig != null ? this.mapStoreConfig.equals(other.mapStoreConfig)
: other.mapStoreConfig == null)
&& (this.nearCacheConfig != null ? this.nearCacheConfig.equals(other.nearCacheConfig)
Expand All @@ -903,6 +954,7 @@ public String toString() {
+ ", timeToLiveSeconds=" + timeToLiveSeconds
+ ", maxIdleSeconds=" + maxIdleSeconds
+ ", evictionPolicy='" + evictionPolicy + '\''
+ ", mapEvictionPolicy='" + mapEvictionPolicy + '\''
+ ", evictionPercentage=" + evictionPercentage
+ ", minEvictionCheckMillis=" + minEvictionCheckMillis
+ ", maxSizeConfig=" + maxSizeConfig
Expand All @@ -920,4 +972,5 @@ public String toString() {
+ ", cacheDeserializedValues=" + cacheDeserializedValues
+ '}';
}

}
Expand Up @@ -26,6 +26,7 @@
import com.hazelcast.core.HazelcastException;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.map.eviction.MapEvictionPolicy;
import com.hazelcast.mapreduce.TopologyChangedStrategy;
import com.hazelcast.nio.ClassLoaderUtil;
import com.hazelcast.nio.IOUtil;
Expand Down Expand Up @@ -88,6 +89,7 @@
import static com.hazelcast.config.XmlElements.TOPIC;
import static com.hazelcast.config.XmlElements.WAN_REPLICATION;
import static com.hazelcast.config.XmlElements.canOccurMultipleTimes;
import static com.hazelcast.util.Preconditions.checkHasText;
import static com.hazelcast.util.Preconditions.checkNotNull;
import static com.hazelcast.util.StringUtil.LINE_SEPARATOR;
import static com.hazelcast.util.StringUtil.isNullOrEmpty;
Expand Down Expand Up @@ -1047,7 +1049,9 @@ private void handleMap(Node parentNode) throws Exception {
} else if ("async-backup-count".equals(nodeName)) {
mapConfig.setAsyncBackupCount(getIntegerValue("async-backup-count", value));
} else if ("eviction-policy".equals(nodeName)) {
mapConfig.setEvictionPolicy(EvictionPolicy.valueOf(upperCaseInternal(value)));
if(mapConfig.getMapEvictionPolicy() == null) {
mapConfig.setEvictionPolicy(EvictionPolicy.valueOf(upperCaseInternal(value)));
}
} else if ("max-size".equals(nodeName)) {
MaxSizeConfig msc = mapConfig.getMaxSizeConfig();
Node maxSizePolicy = node.getAttributes().getNamedItem("policy");
Expand Down Expand Up @@ -1103,6 +1107,14 @@ private void handleMap(Node parentNode) throws Exception {
mapConfig.setQuorumName(value);
} else if ("query-caches".equals(nodeName)) {
mapQueryCacheHandler(node, mapConfig);
} else if ("map-eviction-policy-class-name".equals(nodeName)) {
String className = checkHasText(getTextContent(node), "map-eviction-policy-class-name cannot be null or empty");
try {
MapEvictionPolicy mapEvictionPolicy = ClassLoaderUtil.newInstance(config.getClassLoader(), className);
mapConfig.setMapEvictionPolicy(mapEvictionPolicy);
} catch (Exception e) {
throw ExceptionUtil.rethrow(e);
}
}
}
this.config.addMapConfig(mapConfig);
Expand Down

0 comments on commit 81d6bb3

Please sign in to comment.