blob: 28ce085608d7c92a103b4e7547cf451529fb57ab [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.processors.cache.persistence;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.InstanceNotFoundException;
import org.apache.ignite.DataRegionMetrics;
import org.apache.ignite.DataStorageMetrics;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.configuration.DataPageEvictionMode;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.mem.DirectMemoryProvider;
import org.apache.ignite.internal.mem.DirectMemoryRegion;
import org.apache.ignite.internal.mem.file.MappedFileMemoryProvider;
import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl;
import org.apache.ignite.internal.pagemem.wal.WALPointer;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
import org.apache.ignite.internal.processors.cache.GridCacheSharedManagerAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
import org.apache.ignite.internal.processors.cache.persistence.evict.FairFifoPageEvictionTracker;
import org.apache.ignite.internal.processors.cache.persistence.evict.NoOpPageEvictionTracker;
import org.apache.ignite.internal.processors.cache.persistence.evict.PageEvictionTracker;
import org.apache.ignite.internal.processors.cache.persistence.evict.Random2LruPageEvictionTracker;
import org.apache.ignite.internal.processors.cache.persistence.evict.RandomLruPageEvictionTracker;
import org.apache.ignite.internal.processors.cache.persistence.filename.PdsFolderSettings;
import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeListImpl;
import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList;
import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage;
import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetastorageLifecycleListener;
import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.LT;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteOutClosure;
import org.apache.ignite.mxbean.DataRegionMetricsMXBean;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_REUSE_MEMORY_ON_DEACTIVATE;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_DATA_REG_DEFAULT_NAME;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_PAGE_SIZE;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_WAL_ARCHIVE_MAX_SIZE;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_WAL_HISTORY_SIZE;
/**
*
*/
public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdapter
implements IgniteChangeGlobalStateSupport, CheckpointLockStateChecker {
/** DataRegionConfiguration name reserved for internal caches. */
public static final String SYSTEM_DATA_REGION_NAME = "sysMemPlc";
/** Minimum size of memory chunk */
private static final long MIN_PAGE_MEMORY_SIZE = 10L * 1024 * 1024;
/** Maximum initial size on 32-bit JVM */
private static final long MAX_PAGE_MEMORY_INIT_SIZE_32_BIT = 2L * 1024 * 1024 * 1024;
/** {@code True} to reuse memory on deactive. */
private final boolean reuseMemory = IgniteSystemProperties.getBoolean(IGNITE_REUSE_MEMORY_ON_DEACTIVATE);
/** */
protected volatile Map<String, DataRegion> dataRegionMap;
/** */
private volatile boolean dataRegionsInitialized;
/** */
protected Map<String, DataRegionMetrics> memMetricsMap;
/** */
protected DataRegion dfltDataRegion;
/** */
protected Map<String, CacheFreeListImpl> freeListMap;
/** */
private CacheFreeListImpl dfltFreeList;
/** Page size from memory configuration, may be set only for fake(standalone) IgniteCacheDataBaseSharedManager */
private int pageSize;
/** First eviction was warned flag. */
private volatile boolean firstEvictWarn;
/** Stores memory providers eligible for reuse. */
private Map<String, DirectMemoryProvider> memProviderMap;
/** {@inheritDoc} */
@Override protected void start0() throws IgniteCheckedException {
if (cctx.kernalContext().clientNode() && cctx.kernalContext().config().getDataStorageConfiguration() == null)
return;
DataStorageConfiguration memCfg = cctx.kernalContext().config().getDataStorageConfiguration();
assert memCfg != null;
validateConfiguration(memCfg);
pageSize = memCfg.getPageSize();
initDataRegions(memCfg);
}
/**
* Registers MBeans for all DataRegionMetrics configured in this instance.
*/
private void registerMetricsMBeans() {
if(U.IGNITE_MBEANS_DISABLED)
return;
IgniteConfiguration cfg = cctx.gridConfig();
for (DataRegionMetrics memMetrics : memMetricsMap.values()) {
DataRegionConfiguration memPlcCfg = dataRegionMap.get(memMetrics.getName()).config();
registerMetricsMBean((DataRegionMetricsImpl)memMetrics, memPlcCfg, cfg);
}
}
/**
* @param memMetrics Memory metrics.
* @param dataRegionCfg Data region configuration.
* @param cfg Ignite configuration.
*/
private void registerMetricsMBean(
DataRegionMetricsImpl memMetrics,
DataRegionConfiguration dataRegionCfg,
IgniteConfiguration cfg
) {
assert !U.IGNITE_MBEANS_DISABLED;
try {
U.registerMBean(
cfg.getMBeanServer(),
cfg.getIgniteInstanceName(),
"DataRegionMetrics",
dataRegionCfg.getName(),
new DataRegionMetricsMXBeanImpl(memMetrics, dataRegionCfg),
DataRegionMetricsMXBean.class);
}
catch (Throwable e) {
U.error(log, "Failed to register MBean for DataRegionMetrics with name: '" + memMetrics.getName() + "'", e);
}
}
/**
* @param dbCfg Database config.
* @throws IgniteCheckedException If failed.
*/
protected void initPageMemoryDataStructures(DataStorageConfiguration dbCfg) throws IgniteCheckedException {
freeListMap = U.newHashMap(dataRegionMap.size());
String dfltMemPlcName = dbCfg.getDefaultDataRegionConfiguration().getName();
for (DataRegion memPlc : dataRegionMap.values()) {
DataRegionConfiguration memPlcCfg = memPlc.config();
DataRegionMetricsImpl memMetrics = (DataRegionMetricsImpl) memMetricsMap.get(memPlcCfg.getName());
boolean persistenceEnabled = memPlcCfg.isPersistenceEnabled();
CacheFreeListImpl freeList = new CacheFreeListImpl(0,
cctx.igniteInstanceName(),
memMetrics,
memPlc,
null,
persistenceEnabled ? cctx.wal() : null,
0L,
true);
freeListMap.put(memPlcCfg.getName(), freeList);
}
dfltFreeList = freeListMap.get(dfltMemPlcName);
}
/**
* @return Size of page used for PageMemory regions.
*/
public int pageSize() {
return pageSize;
}
/**
*
*/
private void startMemoryPolicies() {
for (DataRegion memPlc : dataRegionMap.values()) {
memPlc.pageMemory().start();
memPlc.evictionTracker().start();
}
}
/**
* @param memCfg Database config.
* @throws IgniteCheckedException If failed to initialize swap path.
*/
protected void initDataRegions(DataStorageConfiguration memCfg) throws IgniteCheckedException {
if (dataRegionsInitialized)
return;
initDataRegions0(memCfg);
dataRegionsInitialized = true;
}
/**
* @param memCfg Database config.
* @throws IgniteCheckedException If failed to initialize swap path.
*/
protected void initDataRegions0(DataStorageConfiguration memCfg) throws IgniteCheckedException {
DataRegionConfiguration[] dataRegionCfgs = memCfg.getDataRegionConfigurations();
int dataRegions = dataRegionCfgs == null ? 0 : dataRegionCfgs.length;
dataRegionMap = U.newHashMap(3 + dataRegions);
memMetricsMap = U.newHashMap(3 + dataRegions);
memProviderMap = reuseMemory ? U.newHashMap(3 + dataRegions) : null;
if (dataRegionCfgs != null) {
for (DataRegionConfiguration dataRegionCfg : dataRegionCfgs)
addDataRegion(memCfg, dataRegionCfg, dataRegionCfg.isPersistenceEnabled());
}
addDataRegion(
memCfg,
memCfg.getDefaultDataRegionConfiguration(),
memCfg.getDefaultDataRegionConfiguration().isPersistenceEnabled()
);
addDataRegion(
memCfg,
createSystemDataRegion(
memCfg.getSystemRegionInitialSize(),
memCfg.getSystemRegionMaxSize(),
CU.isPersistenceEnabled(memCfg)
),
CU.isPersistenceEnabled(memCfg)
);
for (DatabaseLifecycleListener lsnr : getDatabaseListeners(cctx.kernalContext()))
lsnr.onInitDataRegions(this);
}
/**
* @param kctx Kernal context.
* @return Database lifecycle listeners.
*/
protected List<DatabaseLifecycleListener> getDatabaseListeners(GridKernalContext kctx) {
return kctx.internalSubscriptionProcessor().getDatabaseListeners();
}
/**
* @param dataStorageCfg Database config.
* @param dataRegionCfg Data region config.
* @throws IgniteCheckedException If failed to initialize swap path.
*/
public void addDataRegion(
DataStorageConfiguration dataStorageCfg,
DataRegionConfiguration dataRegionCfg,
boolean trackable
) throws IgniteCheckedException {
String dataRegionName = dataRegionCfg.getName();
String dfltMemPlcName = dataStorageCfg.getDefaultDataRegionConfiguration().getName();
if (dfltMemPlcName == null)
dfltMemPlcName = DFLT_DATA_REG_DEFAULT_NAME;
DataRegionMetricsImpl memMetrics = new DataRegionMetricsImpl(dataRegionCfg, freeSpaceProvider(dataRegionCfg));
DataRegion memPlc = initMemory(dataStorageCfg, dataRegionCfg, memMetrics, trackable);
dataRegionMap.put(dataRegionName, memPlc);
memMetricsMap.put(dataRegionName, memMetrics);
if (dataRegionName.equals(dfltMemPlcName))
dfltDataRegion = memPlc;
else if (dataRegionName.equals(DFLT_DATA_REG_DEFAULT_NAME))
U.warn(log, "Data Region with name 'default' isn't used as a default. " +
"Please check Memory Policies configuration.");
}
/**
* Closure that can be used to compute fill factor for provided data region.
*
* @param dataRegCfg Data region configuration.
* @return Closure.
*/
protected IgniteOutClosure<Long> freeSpaceProvider(final DataRegionConfiguration dataRegCfg) {
final String dataRegName = dataRegCfg.getName();
return new IgniteOutClosure<Long>() {
private CacheFreeListImpl freeList;
@Override public Long apply() {
if (freeList == null) {
CacheFreeListImpl freeList0 = freeListMap.get(dataRegName);
if (freeList0 == null)
return 0L;
freeList = freeList0;
}
return freeList.freeSpace();
}
};
}
/**
* @param memPlcsCfgs User-defined data region configurations.
*/
private boolean hasCustomDefaultDataRegion(DataRegionConfiguration[] memPlcsCfgs) {
for (DataRegionConfiguration memPlcsCfg : memPlcsCfgs) {
if (DFLT_DATA_REG_DEFAULT_NAME.equals(memPlcsCfg.getName()))
return true;
}
return false;
}
/**
* @param sysCacheInitSize Initial size of PageMemory to be created for system cache.
* @param sysCacheMaxSize Maximum size of PageMemory to be created for system cache.
* @param persistenceEnabled Persistence enabled flag.
*
* @return {@link DataRegionConfiguration configuration} of DataRegion for system cache.
*/
private DataRegionConfiguration createSystemDataRegion(
long sysCacheInitSize,
long sysCacheMaxSize,
boolean persistenceEnabled
) {
DataRegionConfiguration res = new DataRegionConfiguration();
res.setName(SYSTEM_DATA_REGION_NAME);
res.setInitialSize(sysCacheInitSize);
res.setMaxSize(sysCacheMaxSize);
res.setPersistenceEnabled(persistenceEnabled);
return res;
}
/**
* @param memCfg configuration to validate.
*/
private void validateConfiguration(DataStorageConfiguration memCfg) throws IgniteCheckedException {
checkPageSize(memCfg);
DataRegionConfiguration[] regCfgs = memCfg.getDataRegionConfigurations();
Set<String> regNames = (regCfgs != null) ? U.<String>newHashSet(regCfgs.length) : new HashSet<String>(0);
checkSystemDataRegionSizeConfiguration(
memCfg.getSystemRegionInitialSize(),
memCfg.getSystemRegionMaxSize()
);
if (regCfgs != null) {
for (DataRegionConfiguration regCfg : regCfgs)
checkDataRegionConfiguration(memCfg, regNames, regCfg);
}
checkDataRegionConfiguration(memCfg, regNames, memCfg.getDefaultDataRegionConfiguration());
checkWalArchiveSizeConfiguration(memCfg);
}
/**
* Check wal archive size configuration for correctness.
*
* @param memCfg durable memory configuration for an Apache Ignite node.
*/
private void checkWalArchiveSizeConfiguration(DataStorageConfiguration memCfg) throws IgniteCheckedException {
if (memCfg.getWalHistorySize() == DFLT_WAL_HISTORY_SIZE || memCfg.getWalHistorySize() == Integer.MAX_VALUE)
LT.warn(log, "DataRegionConfiguration.maxWalArchiveSize instead DataRegionConfiguration.walHistorySize " +
"would be used for removing old archive wal files");
else if(memCfg.getMaxWalArchiveSize() == DFLT_WAL_ARCHIVE_MAX_SIZE)
LT.warn(log, "walHistorySize was deprecated. maxWalArchiveSize should be used instead");
else
throw new IgniteCheckedException("Should be used only one of wal history size or max wal archive size." +
"(use DataRegionConfiguration.maxWalArchiveSize because DataRegionConfiguration.walHistorySize was deprecated)"
);
if(memCfg.getMaxWalArchiveSize() < memCfg.getWalSegmentSize())
throw new IgniteCheckedException(
"DataRegionConfiguration.maxWalArchiveSize should be greater than DataRegionConfiguration.walSegmentSize"
);
}
/**
* @param memCfg Mem config.
* @param regNames Region names.
* @param regCfg Reg config.
*/
private void checkDataRegionConfiguration(DataStorageConfiguration memCfg, Set<String> regNames,
DataRegionConfiguration regCfg) throws IgniteCheckedException {
assert regCfg != null;
checkDataRegionName(regCfg.getName(), regNames);
checkDataRegionSize(regCfg);
checkMetricsProperties(regCfg);
checkRegionEvictionProperties(regCfg, memCfg);
checkRegionMemoryStorageType(regCfg);
}
/**
* @param memCfg Memory config.
*/
protected void checkPageSize(DataStorageConfiguration memCfg) {
if (memCfg.getPageSize() == 0)
memCfg.setPageSize(DFLT_PAGE_SIZE);
}
/**
* @param regCfg data region config.
*
* @throws IgniteCheckedException if validation of memory metrics properties fails.
*/
private static void checkMetricsProperties(DataRegionConfiguration regCfg) throws IgniteCheckedException {
if (regCfg.getMetricsRateTimeInterval() <= 0)
throw new IgniteCheckedException("Rate time interval must be greater than zero " +
"(use DataRegionConfiguration.rateTimeInterval property to adjust the interval) " +
"[name=" + regCfg.getName() +
", rateTimeInterval=" + regCfg.getMetricsRateTimeInterval() + "]"
);
if (regCfg.getMetricsSubIntervalCount() <= 0)
throw new IgniteCheckedException("Sub intervals must be greater than zero " +
"(use DataRegionConfiguration.subIntervals property to adjust the sub intervals) " +
"[name=" + regCfg.getName() +
", subIntervals=" + regCfg.getMetricsSubIntervalCount() + "]"
);
if (regCfg.getMetricsRateTimeInterval() < 1_000)
throw new IgniteCheckedException("Rate time interval must be longer that 1 second (1_000 milliseconds) " +
"(use DataRegionConfiguration.rateTimeInterval property to adjust the interval) " +
"[name=" + regCfg.getName() +
", rateTimeInterval=" + regCfg.getMetricsRateTimeInterval() + "]");
}
/**
* @param sysCacheInitSize System cache initial size.
* @param sysCacheMaxSize System cache max size.
*
* @throws IgniteCheckedException In case of validation violation.
*/
private static void checkSystemDataRegionSizeConfiguration(
long sysCacheInitSize,
long sysCacheMaxSize
) throws IgniteCheckedException {
if (sysCacheInitSize < MIN_PAGE_MEMORY_SIZE)
throw new IgniteCheckedException("Initial size for system cache must have size more than 10MB (use " +
"DataStorageConfiguration.systemCacheInitialSize property to set correct size in bytes) " +
"[size=" + U.readableSize(sysCacheInitSize, true) + ']'
);
if (U.jvm32Bit() && sysCacheInitSize > MAX_PAGE_MEMORY_INIT_SIZE_32_BIT)
throw new IgniteCheckedException("Initial size for system cache exceeds 2GB on 32-bit JVM (use " +
"DataRegionConfiguration.systemCacheInitialSize property to set correct size in bytes " +
"or use 64-bit JVM) [size=" + U.readableSize(sysCacheInitSize, true) + ']'
);
if (sysCacheMaxSize < sysCacheInitSize)
throw new IgniteCheckedException("MaxSize of system cache must not be smaller than " +
"initialSize [initSize=" + U.readableSize(sysCacheInitSize, true) +
", maxSize=" + U.readableSize(sysCacheMaxSize, true) + "]. " +
"Use DataStorageConfiguration.systemCacheInitialSize/DataStorageConfiguration.systemCacheMaxSize " +
"properties to set correct sizes in bytes."
);
}
/**
* @param regCfg DataRegionConfiguration to validate.
* @throws IgniteCheckedException If config is invalid.
*/
private void checkDataRegionSize(DataRegionConfiguration regCfg) throws IgniteCheckedException {
if (regCfg.getInitialSize() < MIN_PAGE_MEMORY_SIZE || regCfg.getMaxSize() < MIN_PAGE_MEMORY_SIZE)
throw new IgniteCheckedException("DataRegion must have size more than 10MB (use " +
"DataRegionConfiguration.initialSize and .maxSize properties to set correct size in bytes) " +
"[name=" + regCfg.getName() + ", initialSize=" + U.readableSize(regCfg.getInitialSize(), true) +
", maxSize=" + U.readableSize(regCfg.getMaxSize(), true) + "]"
);
if (regCfg.getMaxSize() < regCfg.getInitialSize()) {
if (regCfg.getInitialSize() != Math.min(DataStorageConfiguration.DFLT_DATA_REGION_MAX_SIZE,
DataStorageConfiguration.DFLT_DATA_REGION_INITIAL_SIZE)) {
throw new IgniteCheckedException("DataRegion maxSize must not be smaller than initialSize" +
"[name=" + regCfg.getName() + ", initialSize=" + U.readableSize(regCfg.getInitialSize(), true) +
", maxSize=" + U.readableSize(regCfg.getMaxSize(), true) + "]");
}
regCfg.setInitialSize(regCfg.getMaxSize());
LT.warn(log, "DataRegion maxSize=" + U.readableSize(regCfg.getMaxSize(), true) +
" is smaller than defaultInitialSize=" +
U.readableSize(DataStorageConfiguration.DFLT_DATA_REGION_INITIAL_SIZE, true) +
", setting initialSize to " + U.readableSize(regCfg.getMaxSize(), true));
}
if (U.jvm32Bit() && regCfg.getInitialSize() > MAX_PAGE_MEMORY_INIT_SIZE_32_BIT)
throw new IgniteCheckedException("DataRegion initialSize exceeds 2GB on 32-bit JVM (use " +
"DataRegionConfiguration.initialSize property to set correct size in bytes or use 64-bit JVM) " +
"[name=" + regCfg.getName() +
", size=" + U.readableSize(regCfg.getInitialSize(), true) + "]");
}
/**
* @param regCfg DataRegionConfiguration to validate.
* @throws IgniteCheckedException If config is invalid.
*/
private void checkRegionMemoryStorageType(DataRegionConfiguration regCfg) throws IgniteCheckedException {
if (regCfg.isPersistenceEnabled() && regCfg.getSwapPath() != null)
throw new IgniteCheckedException("DataRegionConfiguration must not have both persistence " +
"storage and swap space enabled at the same time (Use DataRegionConfiguration.setSwapPath(null) " +
"to disable the swap space usage or DataRegionConfiguration.setPersistenceEnabled(false) " +
"to disable the persistence) [name=" + regCfg.getName() + ", swapPath=" + regCfg.getSwapPath() +
", persistenceEnabled=" + regCfg.isPersistenceEnabled() + "]"
);
}
/**
* @param regCfg DataRegionConfiguration to validate.
* @param dbCfg Memory configuration.
* @throws IgniteCheckedException If config is invalid.
*/
protected void checkRegionEvictionProperties(DataRegionConfiguration regCfg, DataStorageConfiguration dbCfg)
throws IgniteCheckedException {
if (regCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED)
return;
if (regCfg.getEvictionThreshold() < 0.5 || regCfg.getEvictionThreshold() > 0.999) {
throw new IgniteCheckedException("Page eviction threshold must be between 0.5 and 0.999: " +
regCfg.getName());
}
if (regCfg.getEmptyPagesPoolSize() <= 10)
throw new IgniteCheckedException("Evicted pages pool size should be greater than 10: " + regCfg.getName());
long maxPoolSize = regCfg.getMaxSize() / dbCfg.getPageSize() / 10;
if (regCfg.getEmptyPagesPoolSize() >= maxPoolSize) {
throw new IgniteCheckedException("Evicted pages pool size should be lesser than " + maxPoolSize +
": " + regCfg.getName());
}
}
/**
* @param regName DataRegion name to validate.
* @param observedNames Names of MemoryPolicies observed before.
* @throws IgniteCheckedException If config is invalid.
*/
private static void checkDataRegionName(String regName, Collection<String> observedNames)
throws IgniteCheckedException {
if (regName == null || regName.isEmpty())
throw new IgniteCheckedException("User-defined DataRegionConfiguration must have non-null and " +
"non-empty name.");
if (observedNames.contains(regName))
throw new IgniteCheckedException("Two MemoryPolicies have the same name: " + regName);
if (SYSTEM_DATA_REGION_NAME.equals(regName))
throw new IgniteCheckedException("'" + SYSTEM_DATA_REGION_NAME + "' policy name is reserved for internal use.");
observedNames.add(regName);
}
/**
* @param log Logger.
*/
public void dumpStatistics(IgniteLogger log) {
if (freeListMap != null) {
for (CacheFreeListImpl freeList : freeListMap.values())
freeList.dumpStatistics(log);
}
}
/**
* @return collection of all configured {@link DataRegion policies}.
*/
public Collection<DataRegion> dataRegions() {
return dataRegionMap != null ? dataRegionMap.values() : null;
}
/**
* @return DataRegionMetrics for all MemoryPolicies configured in Ignite instance.
*/
public Collection<DataRegionMetrics> memoryMetrics() {
if (!F.isEmpty(memMetricsMap)) {
// Intentionally return a collection copy to make it explicitly serializable.
Collection<DataRegionMetrics> res = new ArrayList<>(memMetricsMap.size());
for (DataRegionMetrics metrics : memMetricsMap.values())
res.add(new DataRegionMetricsSnapshot(metrics));
return res;
}
else
return Collections.emptyList();
}
/**
* @return DataStorageMetrics if persistence is enabled or {@code null} otherwise.
*/
public DataStorageMetrics persistentStoreMetrics() {
return null;
}
/**
* @param cachesToStart Started caches.
* @throws IgniteCheckedException If failed.
*/
public void readCheckpointAndRestoreMemory(List<DynamicCacheDescriptor> cachesToStart) throws IgniteCheckedException {
// No-op.
}
/**
* @param memPlcName Name of {@link DataRegion} to obtain {@link DataRegionMetrics} for.
* @return {@link DataRegionMetrics} snapshot for specified {@link DataRegion} or {@code null} if
* no {@link DataRegion} is configured for specified name.
*/
@Nullable public DataRegionMetrics memoryMetrics(String memPlcName) {
if (!F.isEmpty(memMetricsMap)) {
DataRegionMetrics memMetrics = memMetricsMap.get(memPlcName);
return memMetrics == null ? null : new DataRegionMetricsSnapshot(memMetrics);
}
else
return null;
}
/**
* @param memPlcName data region name.
* @return {@link DataRegion} instance associated with a given {@link DataRegionConfiguration}.
* @throws IgniteCheckedException in case of request for unknown DataRegion.
*/
public DataRegion dataRegion(String memPlcName) throws IgniteCheckedException {
if (memPlcName == null)
return dfltDataRegion;
if (dataRegionMap == null)
return null;
DataRegion plc;
if ((plc = dataRegionMap.get(memPlcName)) == null)
throw new IgniteCheckedException("Requested DataRegion is not configured: " + memPlcName);
return plc;
}
/**
* @param memPlcName DataRegionConfiguration name.
* @return {@link FreeList} instance associated with a given {@link DataRegionConfiguration}.
*/
public FreeList freeList(String memPlcName) {
if (memPlcName == null)
return dfltFreeList;
return freeListMap != null ? freeListMap.get(memPlcName) : null;
}
/**
* @param memPlcName DataRegionConfiguration name.
* @return {@link ReuseList} instance associated with a given {@link DataRegionConfiguration}.
*/
public ReuseList reuseList(String memPlcName) {
if (memPlcName == null)
return dfltFreeList;
return freeListMap != null ? freeListMap.get(memPlcName) : null;
}
/** {@inheritDoc} */
@Override protected void stop0(boolean cancel) {
onDeActivate(true);
}
/**
* Unregister MBean.
* @param name Name of mbean.
*/
private void unregisterMBean(String name) {
if(U.IGNITE_MBEANS_DISABLED)
return;
IgniteConfiguration cfg = cctx.gridConfig();
try {
cfg.getMBeanServer().unregisterMBean(
U.makeMBeanName(
cfg.getIgniteInstanceName(),
"DataRegionMetrics", name
));
}
catch (InstanceNotFoundException ignored) {
// We tried to unregister a non-existing MBean, not a big deal.
}
catch (Throwable e) {
U.error(log, "Failed to unregister MBean for memory metrics: " +
name, e);
}
}
/** {@inheritDoc} */
@Override public boolean checkpointLockIsHeldByThread() {
return true;
}
/**
* No-op for non-persistent storage.
*/
public void checkpointReadLock() {
// No-op.
}
/**
* No-op for non-persistent storage.
*/
public void checkpointReadUnlock() {
// No-op.
}
/**
* @return {@code 0} for non-persistent storage.
*/
public long checkpointReadLockTimeout() {
return 0;
}
/**
* No-op for non-persistent storage.
*/
public void checkpointReadLockTimeout(long val) {
// No-op.
}
/**
* No-op for non-persistent storage.
*/
public void cleanupCheckpointDirectory() throws IgniteCheckedException {
// No-op.
}
/**
* No-op for non-persistent storage.
*/
public void cleanupTempCheckpointDirectory() throws IgniteCheckedException{
// No-op.
}
/**
*
*/
@Nullable public IgniteInternalFuture wakeupForCheckpoint(String reason) {
return null;
}
/**
* @return Last checkpoint mark WAL pointer.
*/
public WALPointer lastCheckpointMarkWalPointer() {
return null;
}
/**
* Allows to wait checkpoint finished.
*
* @param reason Reason.
*/
@Nullable public CheckpointFuture forceCheckpoint(String reason) {
return null;
}
/**
* Waits until current state is checkpointed.
*
* @throws IgniteCheckedException If failed.
*/
public void waitForCheckpoint(String reason) throws IgniteCheckedException {
// No-op
}
/**
* @param discoEvt Before exchange for the given discovery event.
*
* @return {@code True} if partitions have been restored from persistent storage.
*/
public boolean beforeExchange(GridDhtPartitionsExchangeFuture discoEvt) throws IgniteCheckedException {
return false;
}
/**
* Called when all partitions have been fully restored and pre-created on node start.
*
* @throws IgniteCheckedException If failed.
*/
public void onStateRestored() throws IgniteCheckedException {
// No-op.
}
/**
* @param fut Partition exchange future.
*/
public void rebuildIndexesIfNeeded(GridDhtPartitionsExchangeFuture fut) {
// No-op.
}
/**
* Needed action before any cache will stop
*/
public void prepareCachesStop() {
// No-op.
}
/**
* @param stoppedGrps A collection of tuples (cache group, destroy flag).
*/
public void onCacheGroupsStopped(Collection<IgniteBiTuple<CacheGroupContext, Boolean>> stoppedGrps) {
// No-op.
}
/**
* @return Future that will be completed when indexes for given cache are restored.
*/
@Nullable public IgniteInternalFuture indexRebuildFuture(int cacheId) {
return null;
}
/**
* Reserve update history for exchange.
*
* @return Reserved update counters per cache and partition.
*/
public Map<Integer, Map<Integer, Long>> reserveHistoryForExchange() {
return Collections.emptyMap();
}
/**
* Release reserved update history.
*/
public void releaseHistoryForExchange() {
// No-op
}
/**
* Reserve update history for preloading.
* @param grpId Cache group ID.
* @param partId Partition Id.
* @param cntr Update counter.
* @return True if successfully reserved.
*/
public boolean reserveHistoryForPreloading(int grpId, int partId, long cntr) {
return false;
}
/**
* Release reserved update history.
*/
public void releaseHistoryForPreloading() {
// No-op
}
/**
* See {@link GridCacheMapEntry#ensureFreeSpace()}
*
* @param memPlc data region.
*/
public void ensureFreeSpace(DataRegion memPlc) throws IgniteCheckedException {
if (memPlc == null)
return;
DataRegionConfiguration plcCfg = memPlc.config();
if (plcCfg.getPageEvictionMode() == DataPageEvictionMode.DISABLED || plcCfg.isPersistenceEnabled())
return;
long memorySize = plcCfg.getMaxSize();
PageMemory pageMem = memPlc.pageMemory();
int sysPageSize = pageMem.systemPageSize();
CacheFreeListImpl freeListImpl = freeListMap.get(plcCfg.getName());
for (;;) {
long allocatedPagesCnt = pageMem.loadedPages();
int emptyDataPagesCnt = freeListImpl.emptyDataPages();
boolean shouldEvict = allocatedPagesCnt > (memorySize / sysPageSize * plcCfg.getEvictionThreshold()) &&
emptyDataPagesCnt < plcCfg.getEmptyPagesPoolSize();
if (shouldEvict) {
warnFirstEvict(plcCfg);
memPlc.evictionTracker().evictDataPage();
memPlc.memoryMetrics().updateEvictionRate();
} else
break;
}
}
/**
* @param memCfg memory configuration with common parameters.
* @param plcCfg data region with PageMemory specific parameters.
* @param memMetrics {@link DataRegionMetrics} object to collect memory usage metrics.
* @return data region instance.
*
* @throws IgniteCheckedException If failed to initialize swap path.
*/
private DataRegion initMemory(
DataStorageConfiguration memCfg,
DataRegionConfiguration plcCfg,
DataRegionMetricsImpl memMetrics,
boolean trackable
) throws IgniteCheckedException {
PageMemory pageMem = createPageMemory(createOrReuseMemoryProvider(plcCfg), memCfg, plcCfg, memMetrics, trackable);
return new DataRegion(pageMem, plcCfg, memMetrics, createPageEvictionTracker(plcCfg, pageMem));
}
/**
* @param plcCfg Policy config.
* @return DirectMemoryProvider provider.
*/
private DirectMemoryProvider createOrReuseMemoryProvider(DataRegionConfiguration plcCfg)
throws IgniteCheckedException {
if (!supportsMemoryReuse(plcCfg))
return createMemoryProvider(plcCfg);
DirectMemoryProvider memProvider = memProviderMap.get(plcCfg.getName());
if (memProvider == null)
memProviderMap.put(plcCfg.getName(), (memProvider = createMemoryProvider(plcCfg)));
return memProvider;
}
/**
* @param plcCfg Policy config.
*
* @return {@code True} if policy supports memory reuse.
*/
private boolean supportsMemoryReuse(DataRegionConfiguration plcCfg) {
return reuseMemory && plcCfg.getSwapPath() == null;
}
/**
* @param plcCfg Policy config.
* @return DirectMemoryProvider provider.
*/
private DirectMemoryProvider createMemoryProvider(DataRegionConfiguration plcCfg) throws IgniteCheckedException {
File allocPath = buildAllocPath(plcCfg);
return allocPath == null ?
new UnsafeMemoryProvider(log) :
new MappedFileMemoryProvider(
log,
allocPath);
}
/**
* @param plc data region Configuration.
* @param pageMem Page memory.
*/
protected PageEvictionTracker createPageEvictionTracker(DataRegionConfiguration plc, PageMemory pageMem) {
if (plc.getPageEvictionMode() == DataPageEvictionMode.DISABLED || plc.isPersistenceEnabled())
return new NoOpPageEvictionTracker();
assert pageMem instanceof PageMemoryNoStoreImpl : pageMem.getClass();
PageMemoryNoStoreImpl pageMem0 = (PageMemoryNoStoreImpl)pageMem;
if (Boolean.getBoolean("override.fair.fifo.page.eviction.tracker"))
return new FairFifoPageEvictionTracker(pageMem0, plc, cctx);
switch (plc.getPageEvictionMode()) {
case RANDOM_LRU:
return new RandomLruPageEvictionTracker(pageMem0, plc, cctx);
case RANDOM_2_LRU:
return new Random2LruPageEvictionTracker(pageMem0, plc, cctx);
default:
return new NoOpPageEvictionTracker();
}
}
/**
* Builds allocation path for memory mapped file to be used with PageMemory.
*
* @param plc DataRegionConfiguration.
*
* @throws IgniteCheckedException If resolving swap directory fails.
*/
@Nullable protected File buildAllocPath(DataRegionConfiguration plc) throws IgniteCheckedException {
String path = plc.getSwapPath();
if (path == null)
return null;
final PdsFolderSettings folderSettings = cctx.kernalContext().pdsFolderResolver().resolveFolders();
final String folderName = folderSettings.isCompatible() ?
String.valueOf(folderSettings.consistentId()).replaceAll("[:,\\.]", "_") :
folderSettings.folderName();
return buildPath(path, folderName);
}
/**
* Creates PageMemory with given size and memory provider.
*
* @param memProvider Memory provider.
* @param memCfg Memory configuartion.
* @param memPlcCfg data region configuration.
* @param memMetrics DataRegionMetrics to collect memory usage metrics.
* @return PageMemory instance.
*/
protected PageMemory createPageMemory(
DirectMemoryProvider memProvider,
DataStorageConfiguration memCfg,
DataRegionConfiguration memPlcCfg,
DataRegionMetricsImpl memMetrics,
boolean trackable
) {
memMetrics.persistenceEnabled(false);
PageMemory pageMem = new PageMemoryNoStoreImpl(
log,
wrapMetricsMemoryProvider(memProvider, memMetrics),
cctx,
memCfg.getPageSize(),
memPlcCfg,
memMetrics,
false
);
memMetrics.pageMemory(pageMem);
return pageMem;
}
/**
* @param memoryProvider0 Memory provider.
* @param memMetrics Memory metrics.
* @return Wrapped memory provider.
*/
protected DirectMemoryProvider wrapMetricsMemoryProvider(
final DirectMemoryProvider memoryProvider0,
final DataRegionMetricsImpl memMetrics
) {
return new DirectMemoryProvider() {
/** */
private final DirectMemoryProvider memProvider = memoryProvider0;
@Override public void initialize(long[] chunkSizes) {
memProvider.initialize(chunkSizes);
}
@Override public void shutdown(boolean deallocate) {
memProvider.shutdown(deallocate);
}
@Override public DirectMemoryRegion nextRegion() {
DirectMemoryRegion nextMemoryRegion = memProvider.nextRegion();
if (nextMemoryRegion == null)
return null;
memMetrics.updateOffHeapSize(nextMemoryRegion.size());
return nextMemoryRegion;
}
};
}
/**
* @param path Path to the working directory.
* @param consId Consistent ID of the local node.
* @return DB storage path.
*
* @throws IgniteCheckedException If resolving swap directory fails.
*/
protected File buildPath(String path, String consId) throws IgniteCheckedException {
String igniteHomeStr = U.getIgniteHome();
File workDir = igniteHomeStr == null ? new File(path) : U.resolveWorkDirectory(igniteHomeStr, path, false);
return new File(workDir, consId);
}
/** {@inheritDoc} */
@Override public void onActivate(GridKernalContext kctx) throws IgniteCheckedException {
if (cctx.kernalContext().clientNode() && cctx.kernalContext().config().getDataStorageConfiguration() == null)
return;
DataStorageConfiguration memCfg = cctx.kernalContext().config().getDataStorageConfiguration();
assert memCfg != null;
initDataRegions(memCfg);
registerMetricsMBeans();
startMemoryPolicies();
initPageMemoryDataStructures(memCfg);
for (DatabaseLifecycleListener lsnr : getDatabaseListeners(kctx))
lsnr.afterInitialise(this);
}
/** {@inheritDoc} */
@Override public void onDeActivate(GridKernalContext kctx) {
onDeActivate(!reuseMemory);
}
/**
* @param shutdown Shutdown.
*/
private void onDeActivate(boolean shutdown) {
for (DatabaseLifecycleListener lsnr : getDatabaseListeners(cctx.kernalContext()))
lsnr.beforeStop(this);
if (dataRegionMap != null) {
for (DataRegion memPlc : dataRegionMap.values()) {
memPlc.pageMemory().stop(shutdown);
memPlc.evictionTracker().stop();
unregisterMBean(memPlc.memoryMetrics().getName());
}
dataRegionMap.clear();
dataRegionMap = null;
if (shutdown && memProviderMap != null) {
memProviderMap.clear();
memProviderMap = null;
}
dataRegionsInitialized = false;
}
}
/**
* @return Name of DataRegionConfiguration for internal caches.
*/
public String systemDateRegionName() {
return SYSTEM_DATA_REGION_NAME;
}
/**
* Method for fake (standalone) context initialization. Not to be called in production code
* @param pageSize configured page size
*/
protected void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
/**
* @return MetaStorage
*/
public MetaStorage metaStorage() {
return null;
}
/**
* Notifies {@link MetastorageLifecycleListener} that {@link MetaStorage} is ready for read.
* This method is called when all processors and managers have already started and right before discovery manager.
*
* @throws IgniteCheckedException If failed.
*/
public void notifyMetaStorageSubscribersOnReadyForRead() throws IgniteCheckedException {
// No-op.
}
/**
* @param grpId Group ID.
* @return WAL enabled flag.
*/
public boolean walEnabled(int grpId, boolean local) {
return false;
}
/**
* Marks cache group as with disabled WAL.
*
* @param grpId Group id.
* @param enabled flag.
*/
public void walEnabled(int grpId, boolean enabled, boolean local) {
// No-op.
}
/**
* Marks last checkpoint as inapplicable for WAL rebalance for given group {@code grpId}.
*
* @param grpId Group id.
*/
public void lastCheckpointInapplicableForWalRebalance(int grpId) {
// No-op.
}
/**
* Warns on first eviction.
* @param regCfg data region configuration.
*/
private void warnFirstEvict(DataRegionConfiguration regCfg) {
if (firstEvictWarn)
return;
// Do not move warning output to synchronized block (it causes warning in IDE).
synchronized (this) {
if (firstEvictWarn)
return;
firstEvictWarn = true;
}
U.warn(log, "Page-based evictions started." +
" Consider increasing 'maxSize' on Data Region configuration: " + regCfg.getName());
}
}