/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * 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 com.hazelcast.internal.nearcache.impl.store; import com.hazelcast.config.EvictionConfig; import com.hazelcast.config.EvictionConfig.MaxSizePolicy; import com.hazelcast.config.NearCacheConfig; import com.hazelcast.config.NearCachePreloaderConfig; import com.hazelcast.core.IBiFunction; import com.hazelcast.internal.adapter.DataStructureAdapter; import com.hazelcast.internal.eviction.EvictionChecker; import com.hazelcast.internal.nearcache.NearCacheRecord; import com.hazelcast.internal.nearcache.impl.maxsize.EntryCountNearCacheEvictionChecker; import com.hazelcast.internal.nearcache.impl.preloader.NearCachePreloader; import com.hazelcast.nio.serialization.Data; import com.hazelcast.spi.serialization.SerializationService; import java.util.Map; import static com.hazelcast.internal.nearcache.NearCacheRecord.READ_PERMITTED; import static java.lang.String.format; /** * Base implementation of {@link AbstractNearCacheRecordStore} for on-heap Near Caches. * * @param <K> the type of the key stored in Near Cache * @param <V> the type of the value stored in Near Cache * @param <R> the type of the value of the underlying {@link com.hazelcast.internal.nearcache.impl.NearCacheRecordMap} */ public abstract class BaseHeapNearCacheRecordStore<K, V, R extends NearCacheRecord> extends AbstractNearCacheRecordStore<K, V, K, R, HeapNearCacheRecordMap<K, R>> { private static final int DEFAULT_INITIAL_CAPACITY = 1000; private final NearCachePreloader<K> nearCachePreloader; BaseHeapNearCacheRecordStore(String name, NearCacheConfig nearCacheConfig, SerializationService serializationService, ClassLoader classLoader) { super(nearCacheConfig, serializationService, classLoader); NearCachePreloaderConfig preloaderConfig = nearCacheConfig.getPreloaderConfig(); this.nearCachePreloader = preloaderConfig.isEnabled() ? new NearCachePreloader<K>(name, preloaderConfig, nearCacheStats, serializationService) : null; } @Override protected EvictionChecker createNearCacheEvictionChecker(EvictionConfig evictionConfig, NearCacheConfig nearCacheConfig) { MaxSizePolicy maxSizePolicy = evictionConfig.getMaximumSizePolicy(); if (maxSizePolicy != MaxSizePolicy.ENTRY_COUNT) { throw new IllegalArgumentException(format("Invalid max-size policy (%s) for %s! Only %s is supported.", maxSizePolicy, getClass().getName(), MaxSizePolicy.ENTRY_COUNT)); } return new EntryCountNearCacheEvictionChecker(evictionConfig.getSize(), records); } @Override protected HeapNearCacheRecordMap<K, R> createNearCacheRecordMap(NearCacheConfig nearCacheConfig) { return new HeapNearCacheRecordMap<K, R>(serializationService, DEFAULT_INITIAL_CAPACITY); } @Override public R getRecord(K key) { return records.get(key); } @Override protected R putRecord(K key, R record) { R oldRecord = records.put(key, record); nearCacheStats.incrementOwnedEntryMemoryCost(getTotalStorageMemoryCost(key, record)); if (oldRecord != null) { nearCacheStats.decrementOwnedEntryMemoryCost(getTotalStorageMemoryCost(key, oldRecord)); } return oldRecord; } @Override protected R removeRecord(K key) { R removedRecord = records.remove(key); if (removedRecord != null && removedRecord.getRecordState() == READ_PERMITTED) { nearCacheStats.decrementOwnedEntryMemoryCost(getTotalStorageMemoryCost(key, removedRecord)); } return removedRecord; } @Override protected boolean containsRecordKey(K key) { return records.containsKey(key); } @Override public void onEvict(K key, R record, boolean wasExpired) { super.onEvict(key, record, wasExpired); nearCacheStats.decrementOwnedEntryMemoryCost(getTotalStorageMemoryCost(key, record)); } @Override public void doExpiration() { for (Map.Entry<K, R> entry : records.entrySet()) { K key = entry.getKey(); R value = entry.getValue(); if (isRecordExpired(value)) { remove(key); onExpire(key, value); } } } @Override public void loadKeys(DataStructureAdapter<Data, ?> adapter) { if (nearCachePreloader != null) { nearCachePreloader.loadKeys(adapter); } } @Override public void storeKeys() { if (nearCachePreloader != null) { nearCachePreloader.storeKeys(records.keySet().iterator()); } } @Override public void destroy() { super.destroy(); if (nearCachePreloader != null) { nearCachePreloader.destroy(); } } @Override protected R getOrCreateToReserve(K key) { return records.applyIfAbsent(key, reserveForUpdate); } @Override protected V updateAndGetReserved(K key, final V value, final long reservationId, boolean deserialize) { R existingRecord = records.applyIfPresent(key, new IBiFunction<K, R, R>() { @Override public R apply(K key, R reservedRecord) { return updateReservedRecordInternal(key, value, reservedRecord, reservationId); } }); if (existingRecord == null || !deserialize) { return null; } Object cachedValue = existingRecord.getValue(); return cachedValue instanceof Data ? toValue(cachedValue) : (V) existingRecord.getValue(); } }