/*
* 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.map.impl;
import com.hazelcast.concurrent.lock.LockService;
import com.hazelcast.config.MapConfig;
import com.hazelcast.map.impl.recordstore.RecordStore;
import com.hazelcast.spi.DistributedObjectNamespace;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.OperationService;
import com.hazelcast.spi.partition.IPartitionService;
import com.hazelcast.spi.properties.GroupProperty;
import com.hazelcast.spi.properties.HazelcastProperties;
import com.hazelcast.util.ConcurrencyUtil;
import com.hazelcast.util.ConstructorFunction;
import com.hazelcast.util.ContextMutexFactory;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.hazelcast.map.impl.MapKeyLoaderUtil.getMaxSizePerNode;
public class PartitionContainer {
final MapService mapService;
final int partitionId;
final ConcurrentMap<String, RecordStore> maps = new ConcurrentHashMap<String, RecordStore>(1000);
final ConstructorFunction<String, RecordStore> recordStoreConstructor
= new ConstructorFunction<String, RecordStore>() {
@Override
public RecordStore createNew(String name) {
RecordStore recordStore = createRecordStore(name);
recordStore.startLoading();
return recordStore;
}
};
final ConstructorFunction<String, RecordStore> recordStoreConstructorSkipLoading
= new ConstructorFunction<String, RecordStore>() {
@Override
public RecordStore createNew(String name) {
return createRecordStore(name);
}
};
final ConstructorFunction<String, RecordStore> recordStoreConstructorForHotRestart
= new ConstructorFunction<String, RecordStore>() {
@Override
public RecordStore createNew(String name) {
return createRecordStore(name);
}
};
/**
* Flag to check if there is a {@link com.hazelcast.map.impl.operation.ClearExpiredOperation}
* is running on this partition at this moment or not.
*/
volatile boolean hasRunningCleanup;
volatile long lastCleanupTime;
/**
* Used when sorting partition containers in {@link com.hazelcast.map.impl.eviction.ExpirationManager}
* A non-volatile copy of lastCleanupTime is used with two reasons.
* <p/>
* 1. We need an un-modified field during sorting.
* 2. Decrease number of volatile reads.
*/
long lastCleanupTimeCopy;
private final ContextMutexFactory contextMutexFactory = new ContextMutexFactory();
public PartitionContainer(final MapService mapService, final int partitionId) {
this.mapService = mapService;
this.partitionId = partitionId;
}
private RecordStore createRecordStore(String name) {
MapServiceContext serviceContext = mapService.getMapServiceContext();
MapContainer mapContainer = serviceContext.getMapContainer(name);
MapConfig mapConfig = mapContainer.getMapConfig();
NodeEngine nodeEngine = serviceContext.getNodeEngine();
IPartitionService ps = nodeEngine.getPartitionService();
OperationService opService = nodeEngine.getOperationService();
ExecutionService execService = nodeEngine.getExecutionService();
HazelcastProperties hazelcastProperties = nodeEngine.getProperties();
MapKeyLoader keyLoader = new MapKeyLoader(name, opService, ps, nodeEngine.getClusterService(),
execService, mapContainer.toData());
keyLoader.setMaxBatch(hazelcastProperties.getInteger(GroupProperty.MAP_LOAD_CHUNK_SIZE));
keyLoader.setMaxSize(getMaxSizePerNode(mapConfig.getMaxSizeConfig()));
keyLoader.setHasBackup(mapConfig.getTotalBackupCount() > 0);
keyLoader.setMapOperationProvider(serviceContext.getMapOperationProvider(name));
RecordStore recordStore = serviceContext.createRecordStore(mapContainer, partitionId, keyLoader);
recordStore.init();
return recordStore;
}
public ConcurrentMap<String, RecordStore> getMaps() {
return maps;
}
public Collection<RecordStore> getAllRecordStores() {
return maps.values();
}
public int getPartitionId() {
return partitionId;
}
public MapService getMapService() {
return mapService;
}
public RecordStore getRecordStore(String name) {
return ConcurrencyUtil.getOrPutSynchronized(maps, name, contextMutexFactory, recordStoreConstructor);
}
public RecordStore getRecordStore(String name, boolean skipLoadingOnCreate) {
return ConcurrencyUtil.getOrPutSynchronized(maps, name, this, skipLoadingOnCreate
? recordStoreConstructorSkipLoading : recordStoreConstructor);
}
public RecordStore getRecordStoreForHotRestart(String name) {
return ConcurrencyUtil.getOrPutSynchronized(maps, name, contextMutexFactory, recordStoreConstructorForHotRestart);
}
public RecordStore getExistingRecordStore(String mapName) {
return maps.get(mapName);
}
public void destroyMap(MapContainer mapContainer) {
String name = mapContainer.getName();
RecordStore recordStore = maps.remove(name);
if (recordStore != null) {
recordStore.destroy();
} else {
// It can be that, map is used only for locking,
// because of that RecordStore is not created.
// We will try to remove/clear LockStore belonging to
// this IMap partition.
clearLockStore(name);
}
MapServiceContext mapServiceContext = mapService.getMapServiceContext();
if (mapServiceContext.removeMapContainer(mapContainer)) {
mapContainer.onDestroy();
}
mapServiceContext.removePartitioningStrategyFromCache(mapContainer.getName());
}
private void clearLockStore(String name) {
final NodeEngine nodeEngine = mapService.getMapServiceContext().getNodeEngine();
final LockService lockService = nodeEngine.getSharedService(LockService.SERVICE_NAME);
if (lockService != null) {
final DistributedObjectNamespace namespace = new DistributedObjectNamespace(MapService.SERVICE_NAME, name);
lockService.clearLockStore(partitionId, namespace);
}
}
public void clear(boolean onShutdown) {
for (RecordStore recordStore : maps.values()) {
recordStore.clearPartition(onShutdown);
}
maps.clear();
}
public boolean hasRunningCleanup() {
return hasRunningCleanup;
}
public void setHasRunningCleanup(boolean hasRunningCleanup) {
this.hasRunningCleanup = hasRunningCleanup;
}
public long getLastCleanupTime() {
return lastCleanupTime;
}
public void setLastCleanupTime(long lastCleanupTime) {
this.lastCleanupTime = lastCleanupTime;
}
public long getLastCleanupTimeCopy() {
return lastCleanupTimeCopy;
}
public void setLastCleanupTimeCopy(long lastCleanupTimeCopy) {
this.lastCleanupTimeCopy = lastCleanupTimeCopy;
}
}