/*
* 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;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import javax.cache.configuration.Factory;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheWriter;
import javax.management.JMException;
import javax.management.MBeanServer;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteSystemProperties;
import org.apache.ignite.cache.CacheExistsException;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CacheRebalanceMode;
import org.apache.ignite.cache.affinity.AffinityFunction;
import org.apache.ignite.cache.affinity.AffinityFunctionContext;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cache.store.CacheStore;
import org.apache.ignite.cache.store.CacheStoreSessionListener;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DeploymentMode;
import org.apache.ignite.configuration.FileSystemConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.NearCacheConfiguration;
import org.apache.ignite.configuration.TransactionConfiguration;
import org.apache.ignite.events.EventType;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.processors.query.QuerySchema;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.processors.query.schema.message.SchemaAbstractDiscoveryMessage;
import org.apache.ignite.internal.processors.query.schema.SchemaExchangeWorkerTask;
import org.apache.ignite.internal.processors.query.schema.SchemaNodeLeaveExchangeWorkerTask;
import org.apache.ignite.internal.processors.query.schema.message.SchemaProposeDiscoveryMessage;
import org.apache.ignite.internal.suggestions.GridPerformanceSuggestions;
import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
import org.apache.ignite.internal.IgniteComponentType;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.IgniteNodeAttributes;
import org.apache.ignite.internal.IgniteTransactionsEx;
import org.apache.ignite.internal.binary.BinaryContext;
import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.binary.GridBinaryMarshaller;
import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage;
import org.apache.ignite.internal.pagemem.snapshot.StartFullSnapshotAckDiscoveryMessage;
import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
import org.apache.ignite.internal.processors.cache.database.IgniteCacheDatabaseSharedManager;
import org.apache.ignite.internal.processors.cache.database.MemoryPolicy;
import org.apache.ignite.internal.processors.cache.database.freelist.FreeList;
import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
import org.apache.ignite.internal.processors.cache.datastructures.CacheDataStructuresManager;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCache;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheAdapter;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCache;
import org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtColocatedCache;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearAtomicCache;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTransactionalCache;
import org.apache.ignite.internal.processors.cache.dr.GridCacheDrManager;
import org.apache.ignite.internal.processors.cache.jta.CacheJtaManagerAdapter;
import org.apache.ignite.internal.processors.cache.local.GridLocalCache;
import org.apache.ignite.internal.processors.cache.local.atomic.GridLocalAtomicCache;
import org.apache.ignite.internal.processors.cache.query.GridCacheDistributedQueryManager;
import org.apache.ignite.internal.processors.cache.query.GridCacheLocalQueryManager;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
import org.apache.ignite.internal.processors.cache.query.continuous.CacheContinuousQueryManager;
import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTransactionsImpl;
import org.apache.ignite.internal.processors.cache.transactions.IgniteTxManager;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersionManager;
import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
import org.apache.ignite.internal.processors.plugin.CachePluginManager;
import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
import org.apache.ignite.internal.util.F0;
import org.apache.ignite.internal.util.future.GridCompoundFuture;
import org.apache.ignite.internal.util.future.GridFinishedFuture;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.lang.IgniteOutClosureX;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteClosure;
import org.apache.ignite.lang.IgniteFuture;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.lifecycle.LifecycleAware;
import org.apache.ignite.marshaller.Marshaller;
import org.apache.ignite.marshaller.MarshallerUtils;
import org.apache.ignite.mxbean.IgniteMBeanAware;
import org.apache.ignite.spi.IgniteNodeValidationResult;
import org.apache.ignite.spi.discovery.DiscoveryDataBag;
import org.apache.ignite.spi.discovery.DiscoveryDataBag.GridDiscoveryData;
import org.apache.ignite.spi.discovery.DiscoveryDataBag.JoiningNodeDiscoveryData;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_CACHE_REMOVED_ENTRIES_TTL;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK;
import static org.apache.ignite.IgniteSystemProperties.getBoolean;
import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
import static org.apache.ignite.cache.CacheMode.LOCAL;
import static org.apache.ignite.cache.CacheMode.PARTITIONED;
import static org.apache.ignite.cache.CacheMode.REPLICATED;
import static org.apache.ignite.cache.CacheRebalanceMode.ASYNC;
import static org.apache.ignite.cache.CacheRebalanceMode.SYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_ASYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.PRIMARY_SYNC;
import static org.apache.ignite.configuration.CacheConfiguration.DFLT_CACHE_MODE;
import static org.apache.ignite.configuration.DeploymentMode.CONTINUOUS;
import static org.apache.ignite.configuration.DeploymentMode.ISOLATED;
import static org.apache.ignite.configuration.DeploymentMode.PRIVATE;
import static org.apache.ignite.configuration.DeploymentMode.SHARED;
import static org.apache.ignite.events.EventType.EVT_NODE_JOINED;
import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType.CACHE_PROC;
import static org.apache.ignite.internal.IgniteComponentType.JTA;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_CONSISTENCY_CHECK_SKIPPED;
import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_TX_CONFIG;
import static org.apache.ignite.internal.processors.cache.GridCacheUtils.isNearEnabled;
/**
* Cache processor.
*/
@SuppressWarnings({"unchecked", "TypeMayBeWeakened", "deprecation"})
public class GridCacheProcessor extends GridProcessorAdapter {
/** */
private final boolean START_CLIENT_CACHES =
IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_START_CACHES_ON_JOIN, false);
/** Shared cache context. */
private GridCacheSharedContext<?, ?> sharedCtx;
/** */
private final Map<String, GridCacheAdapter<?, ?>> caches;
/** Caches stopped from onKernalStop callback. */
private final Map<String, GridCacheAdapter> stoppedCaches = new ConcurrentHashMap<>();
/** Map of proxies. */
private final Map<String, IgniteCacheProxy<?, ?>> jCacheProxies;
/** Caches stop sequence. */
private final Deque<String> stopSeq;
/** Transaction interface implementation. */
private IgniteTransactionsImpl transactions;
/** Pending cache starts. */
private ConcurrentMap<UUID, IgniteInternalFuture> pendingFuts = new ConcurrentHashMap<>();
/** Template configuration add futures. */
private ConcurrentMap<String, IgniteInternalFuture> pendingTemplateFuts = new ConcurrentHashMap<>();
/** Dynamic caches. */
private ConcurrentMap<String, DynamicCacheDescriptor> registeredCaches = new ConcurrentHashMap<>();
/** Cache templates. */
private ConcurrentMap<String, DynamicCacheDescriptor> registeredTemplates = new ConcurrentHashMap<>();
/** */
private IdentityHashMap<CacheStore, ThreadLocal> sesHolders = new IdentityHashMap<>();
/** Must use JDK marsh since it is used by discovery to fire custom events. */
private final Marshaller marsh;
/** Count down latch for caches. */
private final CountDownLatch cacheStartedLatch = new CountDownLatch(1);
/** */
private Map<String, DynamicCacheDescriptor> cachesOnDisconnect;
/** */
private Map<UUID, DynamicCacheChangeBatch> clientReconnectReqs;
/** Internal cache names. */
private final Set<String> internalCaches;
/**
* @param ctx Kernal context.
*/
public GridCacheProcessor(GridKernalContext ctx) {
super(ctx);
caches = new ConcurrentHashMap<>();
jCacheProxies = new ConcurrentHashMap<>();
stopSeq = new LinkedList<>();
internalCaches = new HashSet<>();
marsh = MarshallerUtils.jdkMarshaller(ctx.igniteInstanceName());
}
/**
* @param cfg Initializes cache configuration with proper defaults.
* @param cacheObjCtx Cache object context.
* @throws IgniteCheckedException If configuration is not valid.
*/
private void initialize(CacheConfiguration cfg, CacheObjectContext cacheObjCtx)
throws IgniteCheckedException {
if (cfg.getCacheMode() == null)
cfg.setCacheMode(DFLT_CACHE_MODE);
if (cfg.getNodeFilter() == null)
cfg.setNodeFilter(CacheConfiguration.ALL_NODES);
if (cfg.getAffinity() == null) {
if (cfg.getCacheMode() == PARTITIONED) {
RendezvousAffinityFunction aff = new RendezvousAffinityFunction();
cfg.setAffinity(aff);
}
else if (cfg.getCacheMode() == REPLICATED) {
RendezvousAffinityFunction aff = new RendezvousAffinityFunction(false, 512);
cfg.setAffinity(aff);
cfg.setBackups(Integer.MAX_VALUE);
}
else
cfg.setAffinity(new LocalAffinityFunction());
}
else {
if (cfg.getCacheMode() == LOCAL && !(cfg.getAffinity() instanceof LocalAffinityFunction)) {
cfg.setAffinity(new LocalAffinityFunction());
U.warn(log, "AffinityFunction configuration parameter will be ignored for local cache" +
" [cacheName=" + U.maskName(cfg.getName()) + ']');
}
}
if (cfg.getCacheMode() == REPLICATED)
cfg.setBackups(Integer.MAX_VALUE);
if( cfg.getQueryParallelism() > 1 && cfg.getCacheMode() != PARTITIONED)
throw new IgniteCheckedException("Segmented indices are supported for PARTITIONED mode only.");
if (cfg.getAffinityMapper() == null)
cfg.setAffinityMapper(cacheObjCtx.defaultAffMapper());
ctx.igfsHelper().preProcessCacheConfiguration(cfg);
if (cfg.getRebalanceMode() == null)
cfg.setRebalanceMode(ASYNC);
if (cfg.getAtomicityMode() == null)
cfg.setAtomicityMode(CacheConfiguration.DFLT_CACHE_ATOMICITY_MODE);
if (cfg.getWriteSynchronizationMode() == null)
cfg.setWriteSynchronizationMode(PRIMARY_SYNC);
assert cfg.getWriteSynchronizationMode() != null;
if (cfg.getCacheStoreFactory() == null) {
Factory<CacheLoader> ldrFactory = cfg.getCacheLoaderFactory();
Factory<CacheWriter> writerFactory = cfg.isWriteThrough() ? cfg.getCacheWriterFactory() : null;
if (ldrFactory != null || writerFactory != null)
cfg.setCacheStoreFactory(new GridCacheLoaderWriterStoreFactory(ldrFactory, writerFactory));
}
else {
if (cfg.getCacheLoaderFactory() != null)
throw new IgniteCheckedException("Cannot set both cache loaded factory and cache store factory " +
"for cache: " + U.maskName(cfg.getName()));
if (cfg.getCacheWriterFactory() != null)
throw new IgniteCheckedException("Cannot set both cache writer factory and cache store factory " +
"for cache: " + U.maskName(cfg.getName()));
}
}
/**
* @param cfg Configuration to check for possible performance issues.
* @param hasStore {@code True} if store is configured.
*/
private void suggestOptimizations(CacheConfiguration cfg, boolean hasStore) {
GridPerformanceSuggestions perf = ctx.performance();
String msg = "Disable eviction policy (remove from configuration)";
if (cfg.getEvictionPolicy() != null)
perf.add(msg, false);
else
perf.add(msg, true);
if (cfg.getCacheMode() == PARTITIONED) {
perf.add("Disable near cache (set 'nearConfiguration' to null)", cfg.getNearConfiguration() == null);
if (cfg.getAffinity() != null)
perf.add("Decrease number of backups (set 'backups' to 0)", cfg.getBackups() == 0);
}
// Suppress warning if at least one ATOMIC cache found.
perf.add("Enable ATOMIC mode if not using transactions (set 'atomicityMode' to ATOMIC)",
cfg.getAtomicityMode() == ATOMIC);
// Suppress warning if at least one non-FULL_SYNC mode found.
perf.add("Disable fully synchronous writes (set 'writeSynchronizationMode' to PRIMARY_SYNC or FULL_ASYNC)",
cfg.getWriteSynchronizationMode() != FULL_SYNC);
if (hasStore && cfg.isWriteThrough())
perf.add("Enable write-behind to persistent store (set 'writeBehindEnabled' to true)",
cfg.isWriteBehindEnabled());
}
/**
* Create exchange worker task for custom discovery message.
*
* @param msg Custom discovery message.
* @return Task or {@code null} if message doesn't require any special processing.
*/
public CachePartitionExchangeWorkerTask exchangeTaskForCustomDiscoveryMessage(DiscoveryCustomMessage msg) {
if (msg instanceof SchemaAbstractDiscoveryMessage) {
SchemaAbstractDiscoveryMessage msg0 = (SchemaAbstractDiscoveryMessage)msg;
if (msg0.exchange())
return new SchemaExchangeWorkerTask(msg0);
}
return null;
}
/**
* Process custom exchange task.
*
* @param task Task.
*/
public void processCustomExchangeTask(CachePartitionExchangeWorkerTask task) {
if (task instanceof SchemaExchangeWorkerTask) {
SchemaAbstractDiscoveryMessage msg = ((SchemaExchangeWorkerTask)task).message();
if (msg instanceof SchemaProposeDiscoveryMessage) {
SchemaProposeDiscoveryMessage msg0 = (SchemaProposeDiscoveryMessage)msg;
ctx.query().onSchemaPropose(msg0);
}
else
U.warn(log, "Unsupported schema discovery message: " + msg);
}
else if (task instanceof SchemaNodeLeaveExchangeWorkerTask) {
SchemaNodeLeaveExchangeWorkerTask task0 = (SchemaNodeLeaveExchangeWorkerTask)task;
ctx.query().onNodeLeave(task0.node());
}
else
U.warn(log, "Unsupported custom exchange task: " + task);
}
/**
* @param c Ignite Configuration.
* @param cc Cache Configuration.
* @return {@code true} if cache is starting on client node and this node is affinity node for the cache.
*/
private boolean storesLocallyOnClient(IgniteConfiguration c,
CacheConfiguration cc) {
if (c.isClientMode() && c.getMemoryConfiguration() == null) {
if (cc.getCacheMode() == LOCAL)
return true;
if (ctx.discovery().cacheAffinityNode(ctx.discovery().localNode(), cc.getName()))
return true;
return false;
}
else
return false;
}
/**
* @param c Ignite configuration.
* @param cc Configuration to validate.
* @param cacheType Cache type.
* @param cfgStore Cache store.
* @throws IgniteCheckedException If failed.
*/
private void validate(IgniteConfiguration c,
CacheConfiguration cc,
CacheType cacheType,
@Nullable CacheStore cfgStore) throws IgniteCheckedException {
assertParameter(cc.getName() != null && !cc.getName().isEmpty(), "name is null or empty");
if (cc.getCacheMode() == REPLICATED) {
if (cc.getNearConfiguration() != null &&
ctx.discovery().cacheAffinityNode(ctx.discovery().localNode(), cc.getName())) {
U.warn(log, "Near cache cannot be used with REPLICATED cache, " +
"will be ignored [cacheName=" + U.maskName(cc.getName()) + ']');
cc.setNearConfiguration(null);
}
}
if (storesLocallyOnClient(c, cc))
throw new IgniteCheckedException("MemoryPolicy for client caches must be explicitly configured " +
"on client node startup. Use MemoryConfiguration to configure MemoryPolicy.");
if (cc.getCacheMode() == LOCAL && !cc.getAffinity().getClass().equals(LocalAffinityFunction.class))
U.warn(log, "AffinityFunction configuration parameter will be ignored for local cache [cacheName=" +
U.maskName(cc.getName()) + ']');
if (cc.getAffinity().partitions() > CacheConfiguration.MAX_PARTITIONS_COUNT)
throw new IgniteCheckedException("Cannot have more than " + CacheConfiguration.MAX_PARTITIONS_COUNT +
" partitions [cacheName=" + cc.getName() + ", partitions=" + cc.getAffinity().partitions() + ']');
if (cc.getRebalanceMode() != CacheRebalanceMode.NONE)
assertParameter(cc.getRebalanceBatchSize() > 0, "rebalanceBatchSize > 0");
if (cc.getCacheMode() == PARTITIONED || cc.getCacheMode() == REPLICATED) {
if (cc.getAtomicityMode() == ATOMIC && cc.getWriteSynchronizationMode() == FULL_ASYNC)
U.warn(log, "Cache write synchronization mode is set to FULL_ASYNC. All single-key 'put' and " +
"'remove' operations will return 'null', all 'putx' and 'removex' operations will return" +
" 'true' [cacheName=" + U.maskName(cc.getName()) + ']');
}
DeploymentMode depMode = c.getDeploymentMode();
if (c.isPeerClassLoadingEnabled() && (depMode == PRIVATE || depMode == ISOLATED) &&
!CU.isSystemCache(cc.getName()) && !(c.getMarshaller() instanceof BinaryMarshaller))
throw new IgniteCheckedException("Cache can be started in PRIVATE or ISOLATED deployment mode only when" +
" BinaryMarshaller is used [depMode=" + ctx.config().getDeploymentMode() + ", marshaller=" +
c.getMarshaller().getClass().getName() + ']');
if (cc.getAffinity().partitions() > CacheConfiguration.MAX_PARTITIONS_COUNT)
throw new IgniteCheckedException("Affinity function must return at most " +
CacheConfiguration.MAX_PARTITIONS_COUNT + " partitions [actual=" + cc.getAffinity().partitions() +
", affFunction=" + cc.getAffinity() + ", cacheName=" + cc.getName() + ']');
if (cc.isWriteBehindEnabled()) {
if (cfgStore == null)
throw new IgniteCheckedException("Cannot enable write-behind (writer or store is not provided) " +
"for cache: " + U.maskName(cc.getName()));
assertParameter(cc.getWriteBehindBatchSize() > 0, "writeBehindBatchSize > 0");
assertParameter(cc.getWriteBehindFlushSize() >= 0, "writeBehindFlushSize >= 0");
assertParameter(cc.getWriteBehindFlushFrequency() >= 0, "writeBehindFlushFrequency >= 0");
assertParameter(cc.getWriteBehindFlushThreadCount() > 0, "writeBehindFlushThreadCount > 0");
if (cc.getWriteBehindFlushSize() == 0 && cc.getWriteBehindFlushFrequency() == 0)
throw new IgniteCheckedException("Cannot set both 'writeBehindFlushFrequency' and " +
"'writeBehindFlushSize' parameters to 0 for cache: " + U.maskName(cc.getName()));
}
if (cc.isReadThrough() && cfgStore == null)
throw new IgniteCheckedException("Cannot enable read-through (loader or store is not provided) " +
"for cache: " + U.maskName(cc.getName()));
if (cc.isWriteThrough() && cfgStore == null)
throw new IgniteCheckedException("Cannot enable write-through (writer or store is not provided) " +
"for cache: " + U.maskName(cc.getName()));
long delay = cc.getRebalanceDelay();
if (delay != 0) {
if (cc.getCacheMode() != PARTITIONED)
U.warn(log, "Rebalance delay is supported only for partitioned caches (will ignore): " + (cc.getName()),
"Will ignore rebalance delay for cache: " + U.maskName(cc.getName()));
else if (cc.getRebalanceMode() == SYNC) {
if (delay < 0) {
U.warn(log, "Ignoring SYNC rebalance mode with manual rebalance start (node will not wait for " +
"rebalancing to be finished): " + U.maskName(cc.getName()),
"Node will not wait for rebalance in SYNC mode: " + U.maskName(cc.getName()));
}
else {
U.warn(log, "Using SYNC rebalance mode with rebalance delay (node will wait until rebalancing is " +
"initiated for " + delay + "ms) for cache: " + U.maskName(cc.getName()),
"Node will wait until rebalancing is initiated for " + delay + "ms for cache: " + U.maskName(cc.getName()));
}
}
}
ctx.igfsHelper().validateCacheConfiguration(cc);
if (cc.getAtomicityMode() == ATOMIC)
assertParameter(cc.getTransactionManagerLookupClassName() == null,
"transaction manager can not be used with ATOMIC cache");
if (cc.getEvictionPolicy() != null && !cc.isOnheapCacheEnabled())
throw new IgniteCheckedException("Onheap cache must be enabled if eviction policy is configured [cacheName="
+ U.maskName(cc.getName()) + "]");
QueryUtils.validateCacheConfiguration(cc);
}
/**
* @param ctx Context.
* @return DHT managers.
*/
private List<GridCacheManager> dhtManagers(GridCacheContext ctx) {
return F.asList(ctx.store(), ctx.events(), ctx.evicts(), ctx.queries(), ctx.continuousQueries(),
ctx.dr(), ctx.offheap());
}
/**
* @param ctx Context.
* @return Managers present in both, DHT and Near caches.
*/
@SuppressWarnings("IfMayBeConditional")
private Collection<GridCacheManager> dhtExcludes(GridCacheContext ctx) {
if (ctx.config().getCacheMode() == LOCAL || !isNearEnabled(ctx))
return Collections.emptyList();
else
return F.asList(ctx.queries(), ctx.continuousQueries(), ctx.store(), ctx.offheap());
}
/**
* @param cfg Configuration.
* @param objs Extra components.
* @throws IgniteCheckedException If failed to inject.
*/
private void prepare(CacheConfiguration cfg, Collection<Object> objs) throws IgniteCheckedException {
prepare(cfg, cfg.getEvictionPolicy(), false);
prepare(cfg, cfg.getAffinity(), false);
prepare(cfg, cfg.getAffinityMapper(), false);
prepare(cfg, cfg.getEvictionFilter(), false);
prepare(cfg, cfg.getInterceptor(), false);
prepare(cfg, cfg.getTopologyValidator(), false);
NearCacheConfiguration nearCfg = cfg.getNearConfiguration();
if (nearCfg != null)
prepare(cfg, nearCfg.getNearEvictionPolicy(), true);
for (Object obj : objs)
prepare(cfg, obj, false);
}
/**
* @param cfg Cache configuration.
* @param rsrc Resource.
* @param near Near flag.
* @throws IgniteCheckedException If failed.
*/
private void prepare(CacheConfiguration cfg, @Nullable Object rsrc, boolean near) throws IgniteCheckedException {
if (rsrc != null) {
ctx.resource().injectGeneric(rsrc);
ctx.resource().injectCacheName(rsrc, cfg.getName());
registerMbean(rsrc, cfg.getName(), near);
}
}
/**
* @param cctx Cache context.
*/
private void cleanup(GridCacheContext cctx) {
CacheConfiguration cfg = cctx.config();
cleanup(cfg, cfg.getEvictionPolicy(), false);
cleanup(cfg, cfg.getAffinity(), false);
cleanup(cfg, cfg.getAffinityMapper(), false);
cleanup(cfg, cfg.getEvictionFilter(), false);
cleanup(cfg, cfg.getInterceptor(), false);
cleanup(cfg, cfg.getTopologyValidator(), false);
cleanup(cfg, cctx.store().configuredStore(), false);
if (!CU.isUtilityCache(cfg.getName()) && !CU.isSystemCache(cfg.getName())) {
unregisterMbean(cctx.cache().localMxBean(), cfg.getName(), false);
unregisterMbean(cctx.cache().clusterMxBean(), cfg.getName(), false);
}
NearCacheConfiguration nearCfg = cfg.getNearConfiguration();
if (nearCfg != null)
cleanup(cfg, nearCfg.getNearEvictionPolicy(), true);
cctx.cleanup();
}
/**
* @param cfg Cache configuration.
* @param rsrc Resource.
* @param near Near flag.
*/
private void cleanup(CacheConfiguration cfg, @Nullable Object rsrc, boolean near) {
if (rsrc != null) {
unregisterMbean(rsrc, cfg.getName(), near);
try {
ctx.resource().cleanupGeneric(rsrc);
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to cleanup resource: " + rsrc, e);
}
}
}
/** {@inheritDoc} */
@SuppressWarnings({"unchecked"})
@Override public void start(boolean activeOnStart) throws IgniteCheckedException {
DeploymentMode depMode = ctx.config().getDeploymentMode();
if (!F.isEmpty(ctx.config().getCacheConfiguration())) {
if (depMode != CONTINUOUS && depMode != SHARED)
U.warn(log, "Deployment mode for cache is not CONTINUOUS or SHARED " +
"(it is recommended that you change deployment mode and restart): " + depMode,
"Deployment mode for cache is not CONTINUOUS or SHARED.");
}
initializeInternalCacheNames();
sharedCtx = createSharedContext(
ctx, CU.startStoreSessionListeners(ctx, ctx.config().getCacheStoreSessionListenerFactories()));
transactions = new IgniteTransactionsImpl(sharedCtx);
// Start shared managers.
for (GridCacheSharedManager mgr : sharedCtx.managers())
mgr.start(sharedCtx);
//if inActivate on start then skip registrate caches
if (!activeOnStart)
return;
CacheConfiguration[] cfgs = ctx.config().getCacheConfiguration();
registerCacheFromConfig(cfgs);
registerCacheFromPersistentStore(cfgs);
if (log.isDebugEnabled())
log.debug("Started cache processor.");
}
/**
* @param cfgs Cache configurations.
* @throws IgniteCheckedException If failed.
*/
private void registerCacheFromConfig(CacheConfiguration[] cfgs) throws IgniteCheckedException {
for (int i = 0; i < cfgs.length; i++) {
if (ctx.config().isDaemon())
continue;
CacheConfiguration<?, ?> cfg = new CacheConfiguration(cfgs[i]);
cfgs[i] = cfg; // Replace original configuration value.
registerCache(cfg);
}
}
/**
* @param cfgs Cache configurations.
* @throws IgniteCheckedException If failed.
*/
private void registerCacheFromPersistentStore(CacheConfiguration[] cfgs) throws IgniteCheckedException {
if (sharedCtx.pageStore() != null &&
sharedCtx.database().persistenceEnabled() &&
!ctx.config().isDaemon()) {
Set<String> savedCacheNames = sharedCtx.pageStore().savedCacheNames();
for (CacheConfiguration cfg : cfgs)
savedCacheNames.remove(cfg.getName());
for (String name : internalCaches)
savedCacheNames.remove(name);
if (!F.isEmpty(savedCacheNames)) {
log.info("Registrate persistent caches: " + savedCacheNames);
for (String name : savedCacheNames) {
CacheConfiguration cfg = sharedCtx.pageStore().readConfiguration(name);
if (cfg != null)
registerCache(cfg);
}
}
}
}
/**
* @param cfg Cache configuration.
* @throws IgniteCheckedException If failed.
*/
private void registerCache(CacheConfiguration<?, ?> cfg) throws IgniteCheckedException {
CU.validateCacheName(cfg.getName());
cloneCheckSerializable(cfg);
CacheObjectContext cacheObjCtx = ctx.cacheObjects().contextForCache(cfg);
// Initialize defaults.
initialize(cfg, cacheObjCtx);
String cacheName = cfg.getName();
if (cacheDescriptor(cfg.getName()) != null) {
throw new IgniteCheckedException("Duplicate cache name found (check configuration and " +
"assign unique name to each cache): " + cacheName);
}
CacheType cacheType;
if (CU.isUtilityCache(cfg.getName()))
cacheType = CacheType.UTILITY;
else if (internalCaches.contains(cfg.getName()))
cacheType = CacheType.INTERNAL;
else
cacheType = CacheType.USER;
if (cacheType != CacheType.USER && cfg.getMemoryPolicyName() == null)
cfg.setMemoryPolicyName(sharedCtx.database().systemMemoryPolicyName());
boolean template = cfg.getName() != null && cfg.getName().endsWith("*");
DynamicCacheDescriptor desc = new DynamicCacheDescriptor(ctx,
cfg,
cacheType,
template,
IgniteUuid.randomUuid(),
new QuerySchema(cfg.getQueryEntities()));
desc.locallyConfigured(true);
desc.staticallyConfigured(true);
desc.receivedFrom(ctx.localNodeId());
if (!template) {
cacheDescriptor(cfg.getName(), desc);
ctx.discovery().setCacheFilter(
cfg.getName(),
cfg.getNodeFilter(),
cfg.getNearConfiguration() != null && cfg.getCacheMode() == PARTITIONED,
cfg.getCacheMode());
ctx.discovery().addClientNode(cfg.getName(),
ctx.localNodeId(),
cfg.getNearConfiguration() != null);
if (!cacheType.userCache())
stopSeq.addLast(cfg.getName());
else
stopSeq.addFirst(cfg.getName());
}
else {
if (log.isDebugEnabled())
log.debug("Use cache configuration as template: " + cfg);
registeredTemplates.put(cacheName, desc);
}
if (cfg.getName() == null) { // Use cache configuration with null name as template.
DynamicCacheDescriptor desc0 = new DynamicCacheDescriptor(ctx,
cfg,
cacheType,
true,
IgniteUuid.randomUuid(),
new QuerySchema(cfg.getQueryEntities()));
desc0.locallyConfigured(true);
desc0.staticallyConfigured(true);
registeredTemplates.put(cacheName, desc0);
}
}
/**
* Initialize internal cache names
*/
private void initializeInternalCacheNames() {
FileSystemConfiguration[] igfsCfgs = ctx.grid().configuration().getFileSystemConfiguration();
if (igfsCfgs != null) {
for (FileSystemConfiguration igfsCfg : igfsCfgs) {
internalCaches.add(igfsCfg.getMetaCacheConfiguration().getName());
internalCaches.add(igfsCfg.getDataCacheConfiguration().getName());
}
}
if (IgniteComponentType.HADOOP.inClassPath())
internalCaches.add(CU.SYS_CACHE_HADOOP_MR);
internalCaches.add(CU.ATOMICS_CACHE_NAME);
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public void onKernalStart(boolean activeOnStart) throws IgniteCheckedException {
ClusterNode locNode = ctx.discovery().localNode();
try {
checkConsistency();
boolean currStatus = ctx.state().active();
// If we start as inactive node, and join to active cluster, we must register all caches
// which were received on join.
if (!ctx.isDaemon() && currStatus && !activeOnStart) {
List<CacheConfiguration> tmpCacheCfg = new ArrayList<>();
for (CacheConfiguration conf : ctx.config().getCacheConfiguration()) {
assert conf.getName() != null;
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
CacheConfiguration c = desc.cacheConfiguration();
IgnitePredicate filter = c.getNodeFilter();
if (c.getName().equals(conf.getName()) &&
((desc.receivedOnDiscovery() && CU.affinityNode(locNode, filter)) ||
CU.isSystemCache(c.getName()))) {
tmpCacheCfg.add(c);
break;
}
}
}
if (!tmpCacheCfg.isEmpty()) {
CacheConfiguration[] newCacheCfg = new CacheConfiguration[tmpCacheCfg.size()];
tmpCacheCfg.toArray(newCacheCfg);
ctx.config().setCacheConfiguration(newCacheCfg);
}
activeOnStart = currStatus;
}
if (activeOnStart && !ctx.clientNode() && !ctx.isDaemon())
sharedCtx.database().lock();
// Must start database before start first cache.
sharedCtx.database().onKernalStart(false);
ctx.query().onCacheKernalStart();
// Start dynamic caches received from collect discovery data.
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
if (ctx.config().isDaemon())
continue;
desc.clearRemoteConfigurations();
CacheConfiguration ccfg = desc.cacheConfiguration();
IgnitePredicate filter = ccfg.getNodeFilter();
boolean loc = desc.locallyConfigured();
if (loc || (desc.receivedOnDiscovery() &&
(startAllCachesOnClientStart() || CU.affinityNode(locNode, filter)))) {
boolean started = desc.onStart();
assert started : "Failed to change started flag for locally configured cache: " + desc;
CacheObjectContext cacheObjCtx = ctx.cacheObjects().contextForCache(ccfg);
CachePluginManager pluginMgr = desc.pluginManager();
GridCacheContext ctx = createCache(
ccfg, pluginMgr, desc.cacheType(), cacheObjCtx, desc.updatesAllowed());
ctx.dynamicDeploymentId(desc.deploymentId());
sharedCtx.addCacheContext(ctx);
GridCacheAdapter cache = ctx.cache();
String name = ccfg.getName();
caches.put(name, cache);
startCache(cache, desc.schema());
jCacheProxies.put(name, new IgniteCacheProxy(ctx, cache, null, false));
}
}
}
finally {
cacheStartedLatch.countDown();
}
// Must call onKernalStart on shared managers after creation of fetched caches.
for (GridCacheSharedManager<?, ?> mgr : sharedCtx.managers())
if (sharedCtx.database() != mgr)
mgr.onKernalStart(false);
// Escape if start active on start false
if (!activeOnStart)
return;
for (GridCacheAdapter<?, ?> cache : caches.values())
onKernalStart(cache);
if (!ctx.config().isDaemon())
ctx.cacheObjects().onUtilityCacheStarted();
ctx.service().onUtilityCacheStarted();
// Wait for caches in SYNC preload mode.
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
CacheConfiguration cfg = desc.cacheConfiguration();
IgnitePredicate filter = cfg.getNodeFilter();
if (desc.locallyConfigured() || (desc.receivedOnDiscovery() && CU.affinityNode(locNode, filter))) {
GridCacheAdapter cache = caches.get(cfg.getName());
if (cache != null) {
if (cfg.getRebalanceMode() == SYNC) {
CacheMode cacheMode = cfg.getCacheMode();
if (cacheMode == REPLICATED || (cacheMode == PARTITIONED && cfg.getRebalanceDelay() >= 0))
cache.preloader().syncFuture().get();
}
}
}
}
assert ctx.config().isDaemon() || caches.containsKey(CU.UTILITY_CACHE_NAME) : "Utility cache should be started";
if (!ctx.clientNode() && !ctx.isDaemon())
addRemovedItemsCleanupTask(Long.getLong(IGNITE_CACHE_REMOVED_ENTRIES_TTL, 10_000));
}
/**
* @param timeout Cleanup timeout.
*/
private void addRemovedItemsCleanupTask(long timeout) {
ctx.timeout().addTimeoutObject(new RemovedItemsCleanupTask(timeout));
}
/**
*
*/
private void checkConsistency() throws IgniteCheckedException {
if (!ctx.config().isDaemon() && !getBoolean(IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK)) {
for (ClusterNode n : ctx.discovery().remoteNodes()) {
if (n.attribute(ATTR_CONSISTENCY_CHECK_SKIPPED))
continue;
checkTransactionConfiguration(n);
DeploymentMode locDepMode = ctx.config().getDeploymentMode();
DeploymentMode rmtDepMode = n.attribute(IgniteNodeAttributes.ATTR_DEPLOYMENT_MODE);
CU.checkAttributeMismatch(
log, null, n.id(), "deploymentMode", "Deployment mode",
locDepMode, rmtDepMode, true);
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
CacheConfiguration rmtCfg = desc.remoteConfiguration(n.id());
if (rmtCfg != null) {
CacheConfiguration locCfg = desc.cacheConfiguration();
checkCache(locCfg, rmtCfg, n);
// Check plugin cache configurations.
CachePluginManager pluginMgr = desc.pluginManager();
pluginMgr.validateRemotes(rmtCfg, n);
}
}
}
}
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public void stop(boolean cancel) throws IgniteCheckedException {
stopCaches(cancel);
List<? extends GridCacheSharedManager<?, ?>> mgrs = sharedCtx.managers();
for (ListIterator<? extends GridCacheSharedManager<?, ?>> it = mgrs.listIterator(mgrs.size()); it.hasPrevious(); ) {
GridCacheSharedManager<?, ?> mgr = it.previous();
mgr.stop(cancel);
}
CU.stopStoreSessionListeners(ctx, sharedCtx.storeSessionListeners());
sharedCtx.cleanup();
if (log.isDebugEnabled())
log.debug("Stopped cache processor.");
}
/**
* @param cancel Cancel.
*/
public void stopCaches(boolean cancel){
for (String cacheName : stopSeq) {
GridCacheAdapter<?, ?> cache = stoppedCaches.remove(cacheName);
if (cache != null)
stopCache(cache, cancel, false);
}
for (GridCacheAdapter<?, ?> cache : stoppedCaches.values()) {
if (cache == stoppedCaches.remove(cache.name()))
stopCache(cache, cancel, false);
}
registeredCaches.clear();
}
/**
* Blocks all available gateways
*/
public void blockGateways() {
for (IgniteCacheProxy<?, ?> proxy : jCacheProxies.values())
proxy.gate().onStopped();
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override public void onKernalStop(boolean cancel) {
cacheStartedLatch.countDown();
GridCachePartitionExchangeManager<Object, Object> exch = context().exchange();
// Stop exchange manager first so that we call onKernalStop on all caches.
// No new caches should be added after this point.
exch.onKernalStop(cancel);
onKernalStopCaches(cancel);
cancelFutures();
List<? extends GridCacheSharedManager<?, ?>> sharedMgrs = sharedCtx.managers();
for (ListIterator<? extends GridCacheSharedManager<?, ?>> it = sharedMgrs.listIterator(sharedMgrs.size());
it.hasPrevious(); ) {
GridCacheSharedManager<?, ?> mgr = it.previous();
if (mgr != exch)
mgr.onKernalStop(cancel);
}
}
/**
* @param cancel Cancel.
*/
public void onKernalStopCaches(boolean cancel){
for (GridCacheAdapter<?, ?> cache : caches.values()) {
GridCacheAffinityManager aff = cache.context().affinity();
if (aff != null)
aff.cancelFutures();
}
for (String cacheName : stopSeq) {
GridCacheAdapter<?, ?> cache = caches.remove(cacheName);
if (cache != null) {
stoppedCaches.put(cacheName, cache);
onKernalStop(cache, cancel);
}
}
for (Map.Entry<String, GridCacheAdapter<?, ?>> entry : caches.entrySet()) {
GridCacheAdapter<?, ?> cache = entry.getValue();
if (cache == caches.remove(entry.getKey())) {
stoppedCaches.put(entry.getKey(), cache);
onKernalStop(entry.getValue(), cancel);
}
}
}
/** {@inheritDoc} */
@Override public void onDisconnected(IgniteFuture<?> reconnectFut) throws IgniteCheckedException {
cachesOnDisconnect = new HashMap<>(registeredCaches);
IgniteClientDisconnectedCheckedException err = new IgniteClientDisconnectedCheckedException(
ctx.cluster().clientReconnectFuture(),
"Failed to execute dynamic cache change request, client node disconnected.");
for (IgniteInternalFuture fut : pendingFuts.values())
((GridFutureAdapter)fut).onDone(err);
for (IgniteInternalFuture fut : pendingTemplateFuts.values())
((GridFutureAdapter)fut).onDone(err);
for (GridCacheAdapter cache : caches.values()) {
GridCacheContext cctx = cache.context();
cctx.gate().onDisconnected(reconnectFut);
List<GridCacheManager> mgrs = cache.context().managers();
for (ListIterator<GridCacheManager> it = mgrs.listIterator(mgrs.size()); it.hasPrevious(); ) {
GridCacheManager mgr = it.previous();
mgr.onDisconnected(reconnectFut);
}
}
sharedCtx.onDisconnected(reconnectFut);
registeredCaches.clear();
registeredTemplates.clear();
}
/** {@inheritDoc} */
@Override public IgniteInternalFuture<?> onReconnected(boolean clusterRestarted) throws IgniteCheckedException {
List<GridCacheAdapter> reconnected = new ArrayList<>(caches.size());
GridCompoundFuture<?, ?> stopFut = null;
for (final GridCacheAdapter cache : caches.values()) {
String name = cache.name();
boolean stopped;
boolean sysCache = CU.isUtilityCache(name) || CU.isAtomicsCache(name);
if (!sysCache) {
DynamicCacheDescriptor oldDesc = cachesOnDisconnect.get(name);
assert oldDesc != null : "No descriptor for cache: " + name;
DynamicCacheDescriptor newDesc = cacheDescriptor(name);
stopped = newDesc == null || !oldDesc.deploymentId().equals(newDesc.deploymentId());
}
else
stopped = false;
if (stopped) {
cache.context().gate().reconnected(true);
sharedCtx.removeCacheContext(cache.ctx);
caches.remove(cache.name());
jCacheProxies.remove(cache.name());
IgniteInternalFuture<?> fut = ctx.closure().runLocalSafe(new Runnable() {
@Override public void run() {
onKernalStop(cache, true);
stopCache(cache, true, false);
}
});
if (stopFut == null)
stopFut = new GridCompoundFuture<>();
stopFut.add((IgniteInternalFuture)fut);
}
else {
cache.onReconnected();
reconnected.add(cache);
if (!sysCache) {
// Re-create cache structures inside indexing in order to apply recent schema changes.
GridCacheContext cctx = cache.context();
DynamicCacheDescriptor desc = cacheDescriptor(name);
assert desc != null;
ctx.query().onCacheStop0(cctx.name());
ctx.query().onCacheStart0(cctx, desc.schema());
}
}
}
if (clientReconnectReqs != null) {
for (Map.Entry<UUID, DynamicCacheChangeBatch> e : clientReconnectReqs.entrySet())
processClientReconnectData(e.getKey(), e.getValue());
clientReconnectReqs = null;
}
sharedCtx.onReconnected();
for (GridCacheAdapter cache : reconnected)
cache.context().gate().reconnected(false);
cachesOnDisconnect = null;
if (stopFut != null)
stopFut.markInitialized();
return stopFut;
}
/**
* @param cache Cache to start.
* @param schema Cache schema.
* @throws IgniteCheckedException If failed to start cache.
*/
@SuppressWarnings({"TypeMayBeWeakened", "unchecked"})
private void startCache(GridCacheAdapter<?, ?> cache, QuerySchema schema) throws IgniteCheckedException {
GridCacheContext<?, ?> cacheCtx = cache.context();
ctx.continuous().onCacheStart(cacheCtx);
if (sharedCtx.pageStore() != null)
sharedCtx.pageStore().initializeForCache(cacheCtx.config());
CacheConfiguration cfg = cacheCtx.config();
// Intentionally compare Boolean references using '!=' below to check if the flag has been explicitly set.
if (cfg.isStoreKeepBinary() && cfg.isStoreKeepBinary() != CacheConfiguration.DFLT_STORE_KEEP_BINARY
&& !(ctx.config().getMarshaller() instanceof BinaryMarshaller))
U.warn(log, "CacheConfiguration.isStoreKeepBinary() configuration property will be ignored because " +
"BinaryMarshaller is not used");
// Start managers.
for (GridCacheManager mgr : F.view(cacheCtx.managers(), F.notContains(dhtExcludes(cacheCtx))))
mgr.start(cacheCtx);
cacheCtx.initConflictResolver();
if (cfg.getCacheMode() != LOCAL && GridCacheUtils.isNearEnabled(cfg)) {
GridCacheContext<?, ?> dhtCtx = cacheCtx.near().dht().context();
// Start DHT managers.
for (GridCacheManager mgr : dhtManagers(dhtCtx))
mgr.start(dhtCtx);
dhtCtx.initConflictResolver();
// Start DHT cache.
dhtCtx.cache().start();
if (log.isDebugEnabled())
log.debug("Started DHT cache: " + dhtCtx.cache().name());
}
cacheCtx.cache().start();
ctx.query().onCacheStart(cacheCtx, schema);
cacheCtx.onStarted();
if (log.isInfoEnabled())
log.info("Started cache [name=" + U.maskName(cfg.getName()) + ", memoryPolicyName=" + cfg.getMemoryPolicyName() + ", mode=" + cfg.getCacheMode() + ']');
}
/**
* @param cache Cache to stop.
* @param cancel Cancel flag.
*/
@SuppressWarnings({"TypeMayBeWeakened", "unchecked"})
private void stopCache(GridCacheAdapter<?, ?> cache, boolean cancel, boolean destroy) {
GridCacheContext ctx = cache.context();
if (!cache.isNear() && ctx.shared().wal() != null) {
try {
ctx.shared().wal().fsync(null);
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to flush write-ahead log on cache stop " +
"[cache=" + ctx.name() + "]", e);
}
}
sharedCtx.removeCacheContext(ctx);
cache.stop();
ctx.kernalContext().query().onCacheStop(ctx);
if (isNearEnabled(ctx)) {
GridDhtCacheAdapter dht = ctx.near().dht();
// Check whether dht cache has been started.
if (dht != null) {
dht.stop();
GridCacheContext<?, ?> dhtCtx = dht.context();
List<GridCacheManager> dhtMgrs = dhtManagers(dhtCtx);
for (ListIterator<GridCacheManager> it = dhtMgrs.listIterator(dhtMgrs.size()); it.hasPrevious(); ) {
GridCacheManager mgr = it.previous();
mgr.stop(cancel, destroy);
}
}
}
List<GridCacheManager> mgrs = ctx.managers();
Collection<GridCacheManager> excludes = dhtExcludes(ctx);
// Reverse order.
for (ListIterator<GridCacheManager> it = mgrs.listIterator(mgrs.size()); it.hasPrevious(); ) {
GridCacheManager mgr = it.previous();
if (!excludes.contains(mgr))
mgr.stop(cancel, destroy);
}
ctx.kernalContext().continuous().onCacheStop(ctx);
ctx.kernalContext().cache().context().database().onCacheStop(ctx);
U.stopLifecycleAware(log, lifecycleAwares(cache.configuration(), ctx.store().configuredStore()));
if (log.isInfoEnabled())
log.info("Stopped cache: " + cache.name());
if (sharedCtx.pageStore() != null) {
try {
sharedCtx.pageStore().shutdownForCache(ctx, destroy);
}
catch (IgniteCheckedException e) {
U.error(log, "Failed to gracefully clean page store resources for destroyed cache " +
"[cache=" + ctx.name() + "]", e);
}
}
cleanup(ctx);
}
/**
* @throws IgniteCheckedException If failed to wait.
*/
public void awaitStarted() throws IgniteCheckedException {
U.await(cacheStartedLatch);
}
/**
* @param cache Cache.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
private void onKernalStart(GridCacheAdapter<?, ?> cache) throws IgniteCheckedException {
GridCacheContext<?, ?> ctx = cache.context();
// Start DHT cache as well.
if (isNearEnabled(ctx)) {
GridDhtCacheAdapter dht = ctx.near().dht();
GridCacheContext<?, ?> dhtCtx = dht.context();
for (GridCacheManager mgr : dhtManagers(dhtCtx))
mgr.onKernalStart();
dht.onKernalStart();
if (log.isDebugEnabled())
log.debug("Executed onKernalStart() callback for DHT cache: " + dht.name());
}
for (GridCacheManager mgr : F.view(ctx.managers(), F0.notContains(dhtExcludes(ctx))))
mgr.onKernalStart();
cache.onKernalStart();
if (ctx.events().isRecordable(EventType.EVT_CACHE_STARTED))
ctx.events().addEvent(EventType.EVT_CACHE_STARTED);
if (log.isDebugEnabled())
log.debug("Executed onKernalStart() callback for cache [name=" + cache.name() + ", mode=" +
cache.configuration().getCacheMode() + ']');
}
/**
* @param cache Cache to stop.
* @param cancel Cancel flag.
*/
@SuppressWarnings("unchecked")
private void onKernalStop(GridCacheAdapter<?, ?> cache, boolean cancel) {
GridCacheContext ctx = cache.context();
if (isNearEnabled(ctx)) {
GridDhtCacheAdapter dht = ctx.near().dht();
if (dht != null) {
GridCacheContext<?, ?> dhtCtx = dht.context();
for (GridCacheManager mgr : dhtManagers(dhtCtx))
mgr.onKernalStop(cancel);
dht.onKernalStop();
}
}
List<GridCacheManager> mgrs = ctx.managers();
Collection<GridCacheManager> excludes = dhtExcludes(ctx);
// Reverse order.
for (ListIterator<GridCacheManager> it = mgrs.listIterator(mgrs.size()); it.hasPrevious(); ) {
GridCacheManager mgr = it.previous();
if (!excludes.contains(mgr))
mgr.onKernalStop(cancel);
}
cache.onKernalStop();
if (ctx.events().isRecordable(EventType.EVT_CACHE_STOPPED))
ctx.events().addEvent(EventType.EVT_CACHE_STOPPED);
}
/**
* @param cfg Cache configuration to use to create cache.
* @param pluginMgr Cache plugin manager.
* @param cacheType Cache type.
* @param cacheObjCtx Cache object context.
* @param updatesAllowed Updates allowed flag.
* @return Cache context.
* @throws IgniteCheckedException If failed to create cache.
*/
private GridCacheContext createCache(CacheConfiguration<?, ?> cfg,
@Nullable CachePluginManager pluginMgr,
CacheType cacheType,
CacheObjectContext cacheObjCtx,
boolean updatesAllowed)
throws IgniteCheckedException {
assert cfg != null;
if (cfg.getCacheStoreFactory() instanceof GridCacheLoaderWriterStoreFactory) {
GridCacheLoaderWriterStoreFactory factory = (GridCacheLoaderWriterStoreFactory)cfg.getCacheStoreFactory();
prepare(cfg, factory.loaderFactory(), false);
prepare(cfg, factory.writerFactory(), false);
}
else
prepare(cfg, cfg.getCacheStoreFactory(), false);
CacheStore cfgStore = cfg.getCacheStoreFactory() != null ? cfg.getCacheStoreFactory().create() : null;
QueryUtils.prepareCacheConfiguration(cfg);
validate(ctx.config(), cfg, cacheType, cfgStore);
if (pluginMgr == null)
pluginMgr = new CachePluginManager(ctx, cfg);
pluginMgr.validate();
sharedCtx.jta().registerCache(cfg);
// Skip suggestions for internal caches.
if (cacheType.userCache())
suggestOptimizations(cfg, cfgStore != null);
Collection<Object> toPrepare = new ArrayList<>();
if (cfgStore instanceof GridCacheLoaderWriterStore) {
toPrepare.add(((GridCacheLoaderWriterStore)cfgStore).loader());
toPrepare.add(((GridCacheLoaderWriterStore)cfgStore).writer());
}
else
toPrepare.add(cfgStore);
prepare(cfg, toPrepare);
U.startLifecycleAware(lifecycleAwares(cfg, cfgStore));
boolean nearEnabled = GridCacheUtils.isNearEnabled(cfg);
GridCacheAffinityManager affMgr = new GridCacheAffinityManager();
GridCacheEventManager evtMgr = new GridCacheEventManager();
CacheEvictionManager evictMgr = (nearEnabled || cfg.isOnheapCacheEnabled()) ? new GridCacheEvictionManager() : new CacheOffheapEvictionManager();
GridCacheQueryManager qryMgr = queryManager(cfg);
CacheContinuousQueryManager contQryMgr = new CacheContinuousQueryManager();
CacheDataStructuresManager dataStructuresMgr = new CacheDataStructuresManager();
GridCacheTtlManager ttlMgr = new GridCacheTtlManager();
CacheConflictResolutionManager rslvrMgr = pluginMgr.createComponent(CacheConflictResolutionManager.class);
GridCacheDrManager drMgr = pluginMgr.createComponent(GridCacheDrManager.class);
CacheStoreManager storeMgr = pluginMgr.createComponent(CacheStoreManager.class);
IgniteCacheOffheapManager offheapMgr = pluginMgr.createComponent(IgniteCacheOffheapManager.class);
storeMgr.initialize(cfgStore, sesHolders);
boolean affNode = cfg.getCacheMode() == LOCAL || CU.affinityNode(ctx.discovery().localNode(), cfg.getNodeFilter());
String memPlcName = cfg.getMemoryPolicyName();
MemoryPolicy memPlc = sharedCtx.database().memoryPolicy(memPlcName);
FreeList freeList = sharedCtx.database().freeList(memPlcName);
ReuseList reuseList = sharedCtx.database().reuseList(memPlcName);
GridCacheContext<?, ?> cacheCtx = new GridCacheContext(
ctx,
sharedCtx,
cfg,
cacheType,
affNode,
updatesAllowed,
memPlc,
freeList,
reuseList,
/*
* Managers in starting order!
* ===========================
*/
evtMgr,
storeMgr,
evictMgr,
qryMgr,
contQryMgr,
dataStructuresMgr,
ttlMgr,
drMgr,
offheapMgr,
rslvrMgr,
pluginMgr,
affMgr
);
cacheCtx.cacheObjectContext(cacheObjCtx);
GridCacheAdapter cache = null;
switch (cfg.getCacheMode()) {
case LOCAL: {
switch (cfg.getAtomicityMode()) {
case TRANSACTIONAL: {
cache = new GridLocalCache(cacheCtx);
break;
}
case ATOMIC: {
cache = new GridLocalAtomicCache(cacheCtx);
break;
}
default: {
assert false : "Invalid cache atomicity mode: " + cfg.getAtomicityMode();
}
}
break;
}
case PARTITIONED:
case REPLICATED: {
if (nearEnabled) {
switch (cfg.getAtomicityMode()) {
case TRANSACTIONAL: {
cache = new GridNearTransactionalCache(cacheCtx);
break;
}
case ATOMIC: {
cache = new GridNearAtomicCache(cacheCtx);
break;
}
default: {
assert false : "Invalid cache atomicity mode: " + cfg.getAtomicityMode();
}
}
}
else {
switch (cfg.getAtomicityMode()) {
case TRANSACTIONAL: {
cache = cacheCtx.affinityNode() ?
new GridDhtColocatedCache(cacheCtx) :
new GridDhtColocatedCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
break;
}
case ATOMIC: {
cache = cacheCtx.affinityNode() ?
new GridDhtAtomicCache(cacheCtx) :
new GridDhtAtomicCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
break;
}
default: {
assert false : "Invalid cache atomicity mode: " + cfg.getAtomicityMode();
}
}
}
break;
}
default: {
assert false : "Invalid cache mode: " + cfg.getCacheMode();
}
}
cacheCtx.cache(cache);
GridCacheContext<?, ?> ret = cacheCtx;
/*
* Create DHT cache.
* ================
*/
if (cfg.getCacheMode() != LOCAL && nearEnabled) {
/*
* Specifically don't create the following managers
* here and reuse the one from Near cache:
* 1. GridCacheVersionManager
* 2. GridCacheIoManager
* 3. GridCacheDeploymentManager
* 4. GridCacheQueryManager (note, that we start it for DHT cache though).
* 5. CacheContinuousQueryManager (note, that we start it for DHT cache though).
* 6. GridCacheDgcManager
* 7. GridCacheTtlManager.
* ===============================================
*/
evictMgr = cfg.isOnheapCacheEnabled() ? new GridCacheEvictionManager() : new CacheOffheapEvictionManager();
evtMgr = new GridCacheEventManager();
pluginMgr = new CachePluginManager(ctx, cfg);
drMgr = pluginMgr.createComponent(GridCacheDrManager.class);
cacheCtx = new GridCacheContext(
ctx,
sharedCtx,
cfg,
cacheType,
affNode,
true,
memPlc,
freeList,
reuseList,
/*
* Managers in starting order!
* ===========================
*/
evtMgr,
storeMgr,
evictMgr,
qryMgr,
contQryMgr,
dataStructuresMgr,
ttlMgr,
drMgr,
offheapMgr,
rslvrMgr,
pluginMgr,
affMgr
);
cacheCtx.cacheObjectContext(cacheObjCtx);
GridDhtCacheAdapter dht = null;
switch (cfg.getAtomicityMode()) {
case TRANSACTIONAL: {
assert cache instanceof GridNearTransactionalCache;
GridNearTransactionalCache near = (GridNearTransactionalCache)cache;
GridDhtCache dhtCache = cacheCtx.affinityNode() ?
new GridDhtCache(cacheCtx) :
new GridDhtCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
dhtCache.near(near);
near.dht(dhtCache);
dht = dhtCache;
break;
}
case ATOMIC: {
assert cache instanceof GridNearAtomicCache;
GridNearAtomicCache near = (GridNearAtomicCache)cache;
GridDhtAtomicCache dhtCache = cacheCtx.affinityNode() ?
new GridDhtAtomicCache(cacheCtx) :
new GridDhtAtomicCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
dhtCache.near(near);
near.dht(dhtCache);
dht = dhtCache;
break;
}
default: {
assert false : "Invalid cache atomicity mode: " + cfg.getAtomicityMode();
}
}
cacheCtx.cache(dht);
}
if (!CU.isUtilityCache(cache.name()) && !CU.isSystemCache(cache.name())) {
registerMbean(cache.localMxBean(), cache.name(), false);
registerMbean(cache.clusterMxBean(), cache.name(), false);
}
return ret;
}
/**
* Gets a collection of currently started caches.
*
* @return Collection of started cache names.
*/
public Collection<String> cacheNames() {
return F.viewReadOnly(cacheDescriptors(),
new IgniteClosure<DynamicCacheDescriptor, String>() {
@Override public String apply(DynamicCacheDescriptor desc) {
return desc.cacheConfiguration().getName();
}
},
new IgnitePredicate<DynamicCacheDescriptor>() {
@Override public boolean apply(DynamicCacheDescriptor desc) {
return desc.started();
}
});
}
/**
* Gets public cache that can be used for query execution.
* If cache isn't created on current node it will be started.
*
* @param start Start cache.
* @param inclLoc Include local caches.
* @return Cache or {@code null} if there is no suitable cache.
*/
public IgniteCacheProxy<?, ?> getOrStartPublicCache(boolean start, boolean inclLoc) throws IgniteCheckedException {
// Try to find started cache first.
for (Map.Entry<String, GridCacheAdapter<?, ?>> e : caches.entrySet()) {
CacheConfiguration ccfg = e.getValue().configuration();
String cacheName = ccfg.getName();
if ((inclLoc || ccfg.getCacheMode() != LOCAL) && QueryUtils.isEnabled(ccfg))
return publicJCache(cacheName);
}
if (start) {
for (Map.Entry<String, DynamicCacheDescriptor> e : registeredCaches.entrySet()) {
DynamicCacheDescriptor desc = e.getValue();
CacheConfiguration ccfg = desc.cacheConfiguration();
if (ccfg.getCacheMode() != LOCAL && QueryUtils.isEnabled(ccfg)) {
dynamicStartCache(null, ccfg.getName(), null, false, true, true).get();
return publicJCache(ccfg.getName());
}
}
}
return null;
}
/**
* Gets a collection of currently started public cache names.
*
* @return Collection of currently started public cache names
*/
public Collection<String> publicCacheNames() {
return F.viewReadOnly(cacheDescriptors(),
new IgniteClosure<DynamicCacheDescriptor, String>() {
@Override public String apply(DynamicCacheDescriptor desc) {
return desc.cacheConfiguration().getName();
}
},
new IgnitePredicate<DynamicCacheDescriptor>() {
@Override public boolean apply(DynamicCacheDescriptor desc) {
return desc.cacheType().userCache();
}
}
);
}
/**
* Gets cache mode.
*
* @param cacheName Cache name to check.
* @return Cache mode.
*/
public CacheMode cacheMode(String cacheName) {
assert cacheName != null;
DynamicCacheDescriptor desc = cacheDescriptor(cacheName);
return desc != null ? desc.cacheConfiguration().getCacheMode() : null;
}
/**
* @param req Cache start request.
* @param topVer Topology version.
* @throws IgniteCheckedException If failed.
*/
public void prepareCacheStart(DynamicCacheChangeRequest req, AffinityTopologyVersion topVer)
throws IgniteCheckedException {
assert req.start() : req;
assert req.cacheType() != null : req;
DynamicCacheDescriptor desc = cacheDescriptor(req.cacheName());
if (desc != null)
desc.onStart();
prepareCacheStart(
req.startCacheConfiguration(),
req.nearCacheConfiguration(),
req.cacheType(),
req.clientStartOnly(),
req.initiatingNodeId(),
req.deploymentId(),
topVer,
desc != null ? desc.schema() : null
);
}
/**
* Starts statically configured caches received from remote nodes during exchange.
*
* @param topVer Topology version.
* @return Started caches descriptors.
* @throws IgniteCheckedException If failed.
*/
public Collection<DynamicCacheDescriptor> startReceivedCaches(AffinityTopologyVersion topVer)
throws IgniteCheckedException {
List<DynamicCacheDescriptor> started = null;
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
if (!desc.started() && desc.staticallyConfigured() && !desc.locallyConfigured()) {
if (desc.receivedFrom() != null) {
AffinityTopologyVersion startVer = desc.receivedFromStartVersion();
if (startVer == null || startVer.compareTo(topVer) > 0)
continue;
}
if (desc.onStart()) {
if (started == null)
started = new ArrayList<>();
started.add(desc);
prepareCacheStart(
desc.cacheConfiguration(),
null,
desc.cacheType(),
false,
null,
desc.deploymentId(),
topVer,
desc.schema()
);
}
}
}
return started;
}
/**
* @param cfg Start configuration.
* @param nearCfg Near configuration.
* @param cacheType Cache type.
* @param clientStartOnly Client only start request.
* @param initiatingNodeId Initiating node ID.
* @param deploymentId Deployment ID.
* @param topVer Topology version.
* @param schema Query schema.
* @throws IgniteCheckedException If failed.
*/
private void prepareCacheStart(
CacheConfiguration cfg,
NearCacheConfiguration nearCfg,
CacheType cacheType,
boolean clientStartOnly,
UUID initiatingNodeId,
IgniteUuid deploymentId,
AffinityTopologyVersion topVer,
@Nullable QuerySchema schema
) throws IgniteCheckedException {
CacheConfiguration ccfg = new CacheConfiguration(cfg);
IgnitePredicate nodeFilter = ccfg.getNodeFilter();
ClusterNode locNode = ctx.discovery().localNode();
boolean affNodeStart = !clientStartOnly && CU.affinityNode(locNode, nodeFilter);
boolean clientNodeStart = locNode.id().equals(initiatingNodeId);
if (sharedCtx.cacheContext(CU.cacheId(cfg.getName())) != null)
return;
if (affNodeStart || clientNodeStart || CU.isSystemCache(cfg.getName())) {
if (clientNodeStart && !affNodeStart) {
if (nearCfg != null)
ccfg.setNearConfiguration(nearCfg);
else
ccfg.setNearConfiguration(null);
}
CacheObjectContext cacheObjCtx = ctx.cacheObjects().contextForCache(ccfg);
GridCacheContext cacheCtx = createCache(ccfg, null, cacheType, cacheObjCtx, true);
cacheCtx.startTopologyVersion(topVer);
cacheCtx.dynamicDeploymentId(deploymentId);
GridCacheAdapter cache = cacheCtx.cache();
sharedCtx.addCacheContext(cacheCtx);
caches.put(cacheCtx.name(), cache);
startCache(cache, schema != null ? schema : new QuerySchema());
onKernalStart(cache);
}
}
/**
* @param req Stop request.
*/
public void blockGateway(DynamicCacheChangeRequest req) {
assert req.stop() || req.close();
if (req.stop() || (req.close() && req.initiatingNodeId().equals(ctx.localNodeId()))) {
// Break the proxy before exchange future is done.
IgniteCacheProxy<?, ?> proxy = jCacheProxies.get(req.cacheName());
if (proxy != null) {
if (req.stop())
proxy.gate().stopped();
else
proxy.closeProxy();
}
}
}
/**
* @param req Request.
*/
private void stopGateway(DynamicCacheChangeRequest req) {
assert req.stop() : req;
// Break the proxy before exchange future is done.
IgniteCacheProxy<?, ?> proxy = jCacheProxies.remove(req.cacheName());
if (proxy != null)
proxy.gate().onStopped();
}
/**
* @param req Stop request.
*/
private void prepareCacheStop(DynamicCacheChangeRequest req) {
assert req.stop() || req.close() : req;
GridCacheAdapter<?, ?> cache = caches.remove(req.cacheName());
if (cache != null) {
GridCacheContext<?, ?> ctx = cache.context();
sharedCtx.removeCacheContext(ctx);
assert req.deploymentId().equals(ctx.dynamicDeploymentId()) : "Different deployment IDs [req=" + req +
", ctxDepId=" + ctx.dynamicDeploymentId() + ']';
onKernalStop(cache, req.destroy());
stopCache(cache, true, req.destroy());
}
}
/**
* Callback invoked when first exchange future for dynamic cache is completed.
*
* @param topVer Completed topology version.
* @param reqs Change requests.
* @param err Error.
*/
@SuppressWarnings("unchecked")
public void onExchangeDone(
AffinityTopologyVersion topVer,
Collection<DynamicCacheChangeRequest> reqs,
Throwable err
) {
for (GridCacheAdapter<?, ?> cache : caches.values()) {
GridCacheContext<?, ?> cacheCtx = cache.context();
if (F.eq(cacheCtx.startTopologyVersion(), topVer)) {
if (cacheCtx.preloader() != null)
cacheCtx.preloader().onInitialExchangeComplete(err);
String masked = cacheCtx.name();
jCacheProxies.put(masked, new IgniteCacheProxy(cache.context(), cache, null, false));
}
}
if (!F.isEmpty(reqs) && err == null) {
for (DynamicCacheChangeRequest req : reqs) {
String masked = req.cacheName();
if (req.stop()) {
stopGateway(req);
prepareCacheStop(req);
}
else if (req.close() && req.initiatingNodeId().equals(ctx.localNodeId())) {
IgniteCacheProxy<?, ?> proxy = jCacheProxies.remove(masked);
if (proxy != null) {
if (proxy.context().affinityNode()) {
GridCacheAdapter<?, ?> cache = caches.get(masked);
if (cache != null)
jCacheProxies.put(masked, new IgniteCacheProxy(cache.context(), cache, null, false));
}
else {
proxy.context().gate().onStopped();
prepareCacheStop(req);
}
}
}
}
}
}
/**
* @param req Request to complete future for.
*/
public void completeStartFuture(DynamicCacheChangeRequest req) {
DynamicCacheStartFuture fut = (DynamicCacheStartFuture)pendingFuts.get(req.requestId());
assert req.deploymentId() != null || req.globalStateChange() || req.resetLostPartitions();
assert fut == null || fut.deploymentId != null || req.globalStateChange() || req.resetLostPartitions();
if (fut != null && F.eq(fut.deploymentId(), req.deploymentId()) &&
F.eq(req.initiatingNodeId(), ctx.localNodeId()))
fut.onDone();
}
/**
* Creates shared context.
*
* @param kernalCtx Kernal context.
* @param storeSesLsnrs Store session listeners.
* @return Shared context.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
private GridCacheSharedContext createSharedContext(GridKernalContext kernalCtx,
Collection<CacheStoreSessionListener> storeSesLsnrs) throws IgniteCheckedException {
IgniteTxManager tm = new IgniteTxManager();
GridCacheMvccManager mvccMgr = new GridCacheMvccManager();
GridCacheVersionManager verMgr = new GridCacheVersionManager();
GridCacheDeploymentManager depMgr = new GridCacheDeploymentManager();
GridCachePartitionExchangeManager exchMgr = new GridCachePartitionExchangeManager();
IgniteCacheDatabaseSharedManager dbMgr = ctx.plugins().createComponent(IgniteCacheDatabaseSharedManager.class);
if (dbMgr == null)
dbMgr = new IgniteCacheDatabaseSharedManager();
IgnitePageStoreManager pageStoreMgr = ctx.plugins().createComponent(IgnitePageStoreManager.class);
IgniteWriteAheadLogManager walMgr = ctx.plugins().createComponent(IgniteWriteAheadLogManager.class);
GridCacheIoManager ioMgr = new GridCacheIoManager();
CacheAffinitySharedManager topMgr = new CacheAffinitySharedManager();
GridCacheSharedTtlCleanupManager ttl = new GridCacheSharedTtlCleanupManager();
CacheJtaManagerAdapter jta = JTA.createOptional();
return new GridCacheSharedContext(
kernalCtx,
tm,
verMgr,
mvccMgr,
pageStoreMgr,
walMgr,
dbMgr,
depMgr,
exchMgr,
topMgr,
ioMgr,
ttl,
jta,
storeSesLsnrs
);
}
/** {@inheritDoc} */
@Nullable @Override public DiscoveryDataExchangeType discoveryDataType() {
return CACHE_PROC;
}
/** {@inheritDoc} */
@Override public void collectJoiningNodeData(DiscoveryDataBag dataBag) {
dataBag.addJoiningNodeData(CACHE_PROC.ordinal(), getDiscoveryData(dataBag.joiningNodeId()));
}
/** {@inheritDoc} */
@Override public void collectGridNodeData(DiscoveryDataBag dataBag) {
dataBag.addNodeSpecificData(CACHE_PROC.ordinal(), getDiscoveryData(dataBag.joiningNodeId()));
}
/**
* @param joiningNodeId Joining node id.
*/
private Serializable getDiscoveryData(UUID joiningNodeId) {
boolean reconnect = ctx.localNodeId().equals(joiningNodeId) && cachesOnDisconnect != null;
// Collect dynamically started caches to a single object.
Collection<DynamicCacheChangeRequest> reqs;
Map<String, Map<UUID, Boolean>> clientNodesMap;
if (reconnect) {
reqs = new ArrayList<>(caches.size() + 1);
clientNodesMap = U.newHashMap(caches.size());
collectDataOnReconnectingNode(reqs, clientNodesMap, joiningNodeId);
}
else {
reqs = new ArrayList<>(registeredCaches.size() + registeredTemplates.size() + 1);
clientNodesMap = ctx.discovery().clientNodesMap();
collectDataOnGridNode(reqs);
}
DynamicCacheChangeBatch batch = new DynamicCacheChangeBatch(reqs);
batch.clientNodes(clientNodesMap);
batch.clientReconnect(reconnect);
if (ctx.localNodeId().equals(joiningNodeId))
batch.startCaches(startAllCachesOnClientStart());
// Reset random batch ID so that serialized batches with the same descriptors will be exactly the same.
batch.id(null);
return batch;
}
/**
* @param reqs requests.
*/
private void collectDataOnGridNode(Collection<DynamicCacheChangeRequest> reqs) {
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
// RequestId must be null because on different node will be different byte [] and
// we get duplicate discovery data, for more details see
// TcpDiscoveryNodeAddedMessage#addDiscoveryData.
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(null, desc.cacheConfiguration().getName(),
null);
req.startCacheConfiguration(desc.cacheConfiguration());
req.cacheType(desc.cacheType());
req.deploymentId(desc.deploymentId());
req.receivedFrom(desc.receivedFrom());
req.schema(desc.schema());
reqs.add(req);
}
for (DynamicCacheDescriptor desc : registeredTemplates.values()) {
// RequestId must be null because on different node will be different byte [] and
// we get duplicate discovery data, for more details see
// TcpDiscoveryNodeAddedMessage#addDiscoveryData.
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(null, desc.cacheConfiguration().getName(),
null);
req.startCacheConfiguration(desc.cacheConfiguration());
req.schema(desc.schema());
req.template(true);
reqs.add(req);
}
}
/**
* @param reqs requests.
* @param clientNodesMap Client nodes map.
* @param nodeId Node id.
*/
private void collectDataOnReconnectingNode(
Collection<DynamicCacheChangeRequest> reqs,
Map<String, Map<UUID, Boolean>> clientNodesMap,
UUID nodeId
) {
for (GridCacheAdapter<?, ?> cache : caches.values()) {
DynamicCacheDescriptor desc = cachesOnDisconnect.get(cache.name());
if (desc == null)
continue;
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(null, cache.name(), null);
req.startCacheConfiguration(desc.cacheConfiguration());
req.cacheType(desc.cacheType());
req.deploymentId(desc.deploymentId());
req.receivedFrom(desc.receivedFrom());
req.schema(desc.schema());
reqs.add(req);
Boolean nearEnabled = cache.isNear();
Map<UUID, Boolean> map = U.newHashMap(1);
map.put(nodeId, nearEnabled);
clientNodesMap.put(cache.name(), map);
}
}
/**
* @return {@code True} if need locally start all existing caches on client node start.
*/
private boolean startAllCachesOnClientStart() {
return START_CLIENT_CACHES && ctx.clientNode();
}
/** {@inheritDoc} */
@Override public void onJoiningNodeDataReceived(JoiningNodeDiscoveryData data) {
if (data.hasJoiningNodeData()) {
Serializable joiningNodeData = data.joiningNodeData();
if (joiningNodeData instanceof DynamicCacheChangeBatch)
onDiscoDataReceived(
data.joiningNodeId(),
data.joiningNodeId(),
(DynamicCacheChangeBatch) joiningNodeData, true);
}
}
/** {@inheritDoc} */
@Override public void onGridDataReceived(GridDiscoveryData data) {
Map<UUID, Serializable> nodeSpecData = data.nodeSpecificData();
if (nodeSpecData != null) {
for (Map.Entry<UUID, Serializable> e : nodeSpecData.entrySet()) {
if (e.getValue() != null && e.getValue() instanceof DynamicCacheChangeBatch) {
DynamicCacheChangeBatch batch = (DynamicCacheChangeBatch) e.getValue();
onDiscoDataReceived(data.joiningNodeId(), e.getKey(), batch, false);
}
}
}
}
/**
* @param joiningNodeId Joining node id.
* @param rmtNodeId Rmt node id.
* @param batch Batch.
* @param join Whether this is data from joining node.
*/
private void onDiscoDataReceived(UUID joiningNodeId, UUID rmtNodeId, DynamicCacheChangeBatch batch, boolean join) {
if (batch.clientReconnect()) {
if (ctx.clientDisconnected()) {
if (clientReconnectReqs == null)
clientReconnectReqs = new LinkedHashMap<>();
clientReconnectReqs.put(joiningNodeId, batch);
return;
}
processClientReconnectData(joiningNodeId, batch);
}
else {
for (DynamicCacheChangeRequest req : batch.requests()) {
initReceivedCacheConfiguration(req);
if (req.template()) {
CacheConfiguration ccfg = req.startCacheConfiguration();
assert ccfg != null : req;
DynamicCacheDescriptor existing = registeredTemplates.get(req.cacheName());
if (existing == null) {
DynamicCacheDescriptor desc = new DynamicCacheDescriptor(
ctx,
ccfg,
req.cacheType(),
true,
req.deploymentId(),
req.schema());
registeredTemplates.put(req.cacheName(), desc);
}
continue;
}
DynamicCacheDescriptor existing = cacheDescriptor(req.cacheName());
if (req.start() && !req.clientStartOnly()) {
CacheConfiguration ccfg = req.startCacheConfiguration();
if (existing != null) {
if (joiningNodeId.equals(ctx.localNodeId())) {
existing.receivedFrom(req.receivedFrom());
existing.deploymentId(req.deploymentId());
}
if (existing.locallyConfigured()) {
existing.addRemoteConfiguration(rmtNodeId, req.startCacheConfiguration());
if (!join)
// Overwrite existing with remote.
existing.schema(req.schema());
ctx.discovery().setCacheFilter(
req.cacheName(),
ccfg.getNodeFilter(),
ccfg.getNearConfiguration() != null,
ccfg.getCacheMode());
}
}
else {
assert req.cacheType() != null : req;
DynamicCacheDescriptor desc = new DynamicCacheDescriptor(
ctx,
ccfg,
req.cacheType(),
false,
req.deploymentId(),
req.schema());
// Received statically configured cache.
if (req.initiatingNodeId() == null)
desc.staticallyConfigured(true);
if (joiningNodeId.equals(ctx.localNodeId()))
desc.receivedOnDiscovery(true);
desc.receivedFrom(req.receivedFrom());
DynamicCacheDescriptor old = cacheDescriptor(req.cacheName(), desc);
assert old == null : old;
ctx.discovery().setCacheFilter(
req.cacheName(),
ccfg.getNodeFilter(),
ccfg.getNearConfiguration() != null,
ccfg.getCacheMode());
}
}
}
if (!F.isEmpty(batch.clientNodes())) {
for (Map.Entry<String, Map<UUID, Boolean>> entry : batch.clientNodes().entrySet()) {
String cacheName = entry.getKey();
for (Map.Entry<UUID, Boolean> tup : entry.getValue().entrySet())
ctx.discovery().addClientNode(cacheName, tup.getKey(), tup.getValue());
}
}
if (batch.startCaches()) {
for (Map.Entry<String, DynamicCacheDescriptor> entry : registeredCaches.entrySet())
ctx.discovery().addClientNode(entry.getKey(), joiningNodeId, false);
}
}
}
/**
* @param clientNodeId Client node ID.
* @param batch Cache change batch.
*/
private void processClientReconnectData(UUID clientNodeId, DynamicCacheChangeBatch batch) {
assert batch.clientReconnect() : batch;
for (DynamicCacheChangeRequest req : batch.requests()) {
assert !req.template() : req;
initReceivedCacheConfiguration(req);
String name = req.cacheName();
boolean sysCache = CU.isUtilityCache(name) || CU.isAtomicsCache(name);
if (!sysCache) {
DynamicCacheDescriptor desc = cacheDescriptor(req.cacheName());
if (desc != null && desc.deploymentId().equals(req.deploymentId())) {
Map<UUID, Boolean> nodes = batch.clientNodes().get(name);
assert nodes != null : req;
assert nodes.containsKey(clientNodeId) : nodes;
ctx.discovery().addClientNode(req.cacheName(), clientNodeId, nodes.get(clientNodeId));
}
}
else
ctx.discovery().addClientNode(req.cacheName(), clientNodeId, false);
}
}
/**
* Dynamically starts cache using template configuration.
*
* @param cacheName Cache name.
* @return Future that will be completed when cache is deployed.
*/
public IgniteInternalFuture<?> createFromTemplate(String cacheName) {
try {
CacheConfiguration cfg = createConfigFromTemplate(cacheName);
return dynamicStartCache(cfg, cacheName, null, true, true, true);
}
catch (IgniteCheckedException e) {
throw U.convertException(e);
}
}
/**
* Dynamically starts cache using template configuration.
*
* @param cacheName Cache name.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when cache is deployed.
*/
public IgniteInternalFuture<?> getOrCreateFromTemplate(String cacheName, boolean checkThreadTx) {
assert cacheName != null;
try {
if (publicJCache(cacheName, false, checkThreadTx) != null) // Cache with given name already started.
return new GridFinishedFuture<>();
CacheConfiguration cfg = createConfigFromTemplate(cacheName);
return dynamicStartCache(cfg, cacheName, null, false, true, checkThreadTx);
}
catch (IgniteCheckedException e) {
return new GridFinishedFuture<>(e);
}
}
/**
* @param cacheName Cache name.
* @return Cache configuration.
* @throws IgniteCheckedException If failed.
*/
private CacheConfiguration createConfigFromTemplate(String cacheName) throws IgniteCheckedException {
CacheConfiguration cfgTemplate = null;
CacheConfiguration dfltCacheCfg = null;
List<CacheConfiguration> wildcardNameCfgs = null;
for (DynamicCacheDescriptor desc : registeredTemplates.values()) {
assert desc.template();
CacheConfiguration cfg = desc.cacheConfiguration();
assert cfg != null;
if (F.eq(cacheName, cfg.getName())) {
cfgTemplate = cfg;
break;
}
if (cfg.getName() != null) {
if (cfg.getName().endsWith("*")) {
if (cfg.getName().length() > 1) {
if (wildcardNameCfgs == null)
wildcardNameCfgs = new ArrayList<>();
wildcardNameCfgs.add(cfg);
}
else
dfltCacheCfg = cfg; // Template with name '*'.
}
}
else if (dfltCacheCfg == null)
dfltCacheCfg = cfg;
}
if (cfgTemplate == null && cacheName != null && wildcardNameCfgs != null) {
Collections.sort(wildcardNameCfgs, new Comparator<CacheConfiguration>() {
@Override public int compare(CacheConfiguration cfg1, CacheConfiguration cfg2) {
Integer len1 = cfg1.getName() != null ? cfg1.getName().length() : 0;
Integer len2 = cfg2.getName() != null ? cfg2.getName().length() : 0;
return len2.compareTo(len1);
}
});
for (CacheConfiguration cfg : wildcardNameCfgs) {
if (cacheName.startsWith(cfg.getName().substring(0, cfg.getName().length() - 1))) {
cfgTemplate = cfg;
break;
}
}
}
if (cfgTemplate == null)
cfgTemplate = dfltCacheCfg;
cfgTemplate = cfgTemplate == null ? new CacheConfiguration() : cloneCheckSerializable(cfgTemplate);
CacheConfiguration cfg = new CacheConfiguration(cfgTemplate);
cfg.setName(cacheName);
return cfg;
}
/**
* Dynamically starts cache.
*
* @param ccfg Cache configuration.
* @param cacheName Cache name.
* @param nearCfg Near cache configuration.
* @param failIfExists Fail if exists flag.
* @param failIfNotStarted If {@code true} fails if cache is not started.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when cache is deployed.
*/
@SuppressWarnings("IfMayBeConditional")
public IgniteInternalFuture<?> dynamicStartCache(
@Nullable CacheConfiguration ccfg,
String cacheName,
@Nullable NearCacheConfiguration nearCfg,
boolean failIfExists,
boolean failIfNotStarted,
boolean checkThreadTx
) {
return dynamicStartCache(ccfg,
cacheName,
nearCfg,
CacheType.USER,
failIfExists,
failIfNotStarted,
checkThreadTx);
}
/**
* Dynamically starts cache.
*
* @param ccfg Cache configuration.
* @param cacheName Cache name.
* @param nearCfg Near cache configuration.
* @param cacheType Cache type.
* @param failIfExists Fail if exists flag.
* @param failIfNotStarted If {@code true} fails if cache is not started.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when cache is deployed.
*/
@SuppressWarnings("IfMayBeConditional")
public IgniteInternalFuture<?> dynamicStartCache(
@Nullable CacheConfiguration ccfg,
String cacheName,
@Nullable NearCacheConfiguration nearCfg,
CacheType cacheType,
boolean failIfExists,
boolean failIfNotStarted,
boolean checkThreadTx
) {
assert cacheName != null;
if (checkThreadTx)
checkEmptyTransactions();
try {
DynamicCacheChangeRequest req = prepareCacheChangeRequest(
ccfg,
cacheName,
nearCfg,
cacheType,
failIfExists,
failIfNotStarted);
if (req != null)
return F.first(initiateCacheChanges(F.asList(req), failIfExists));
else
return new GridFinishedFuture<>();
}
catch (Exception e) {
return new GridFinishedFuture<>(e);
}
}
/**
* Dynamically starts multiple caches.
*
* @param ccfgList Collection of cache configuration.
* @param failIfExists Fail if exists flag.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when all caches are deployed.
*/
public IgniteInternalFuture<?> dynamicStartCaches(Collection<CacheConfiguration> ccfgList, boolean failIfExists,
boolean checkThreadTx) {
return dynamicStartCaches(ccfgList, CacheType.USER, failIfExists, checkThreadTx);
}
/**
* Dynamically starts multiple caches.
*
* @param ccfgList Collection of cache configuration.
* @param cacheType Cache type.
* @param failIfExists Fail if exists flag.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when all caches are deployed.
*/
private IgniteInternalFuture<?> dynamicStartCaches(
Collection<CacheConfiguration> ccfgList,
CacheType cacheType,
boolean failIfExists,
boolean checkThreadTx
) {
if (checkThreadTx)
checkEmptyTransactions();
List<DynamicCacheChangeRequest> reqList = new ArrayList<>(ccfgList.size());
try {
for (CacheConfiguration ccfg : ccfgList) {
DynamicCacheChangeRequest req = prepareCacheChangeRequest(
ccfg,
ccfg.getName(),
null,
cacheType,
failIfExists,
true
);
if (req != null)
reqList.add(req);
}
}
catch (Exception e) {
return new GridFinishedFuture<>(e);
}
if (!reqList.isEmpty()) {
GridCompoundFuture<?, ?> compoundFut = new GridCompoundFuture<>();
for (DynamicCacheStartFuture fut : initiateCacheChanges(reqList, failIfExists))
compoundFut.add((IgniteInternalFuture)fut);
compoundFut.markInitialized();
return compoundFut;
}
else
return new GridFinishedFuture<>();
}
/**
* @param cacheName Cache name to destroy.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when cache is destroyed.
*/
public IgniteInternalFuture<?> dynamicDestroyCache(String cacheName, boolean checkThreadTx) {
assert cacheName != null;
if (checkThreadTx)
checkEmptyTransactions();
DynamicCacheChangeRequest t = new DynamicCacheChangeRequest(UUID.randomUUID(), cacheName, ctx.localNodeId());
t.stop(true);
t.destroy(true);
return F.first(initiateCacheChanges(F.asList(t), false));
}
/**
* @param cacheNames Collection of cache names to destroy.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Future that will be completed when cache is destroyed.
*/
public IgniteInternalFuture<?> dynamicDestroyCaches(Collection<String> cacheNames, boolean checkThreadTx) {
if (checkThreadTx)
checkEmptyTransactions();
List<DynamicCacheChangeRequest> reqs = new ArrayList<>(cacheNames.size());
for (String cacheName : cacheNames) {
DynamicCacheChangeRequest t = new DynamicCacheChangeRequest(UUID.randomUUID(), cacheName, ctx.localNodeId());
t.stop(true);
reqs.add(t);
}
GridCompoundFuture<?, ?> compoundFut = new GridCompoundFuture<>();
for (DynamicCacheStartFuture fut : initiateCacheChanges(reqs, false))
compoundFut.add((IgniteInternalFuture)fut);
compoundFut.markInitialized();
return compoundFut;
}
/**
* @param cacheName Cache name to close.
* @return Future that will be completed when cache is closed.
*/
public IgniteInternalFuture<?> dynamicCloseCache(String cacheName) {
assert cacheName != null;
IgniteCacheProxy<?, ?> proxy = jCacheProxies.get(cacheName);
if (proxy == null || proxy.proxyClosed())
return new GridFinishedFuture<>(); // No-op.
checkEmptyTransactions();
DynamicCacheChangeRequest t = new DynamicCacheChangeRequest(UUID.randomUUID(), cacheName, ctx.localNodeId());
t.close(true);
return F.first(initiateCacheChanges(F.asList(t), false));
}
/**
* Resets cache state after the cache has been moved to recovery state.
*
* @param cacheNames Cache names.
* @return Future that will be completed when state is changed for all caches.
*/
public IgniteInternalFuture<?> resetCacheState(Collection<String> cacheNames) {
checkEmptyTransactions();
if (F.isEmpty(cacheNames))
cacheNames = registeredCaches.keySet();
Collection<DynamicCacheChangeRequest> reqs = new ArrayList<>(cacheNames.size());
for (String cacheName : cacheNames) {
DynamicCacheDescriptor desc = cacheDescriptor(cacheName);
if (desc == null) {
log.warning("Reset lost partition will not be executed, " +
"because cache with name:" + cacheName + " doesn't not exist");
continue;
}
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(
UUID.randomUUID(), cacheName, ctx.localNodeId());
req.markResetLostPartitions();
reqs.add(req);
}
GridCompoundFuture fut = new GridCompoundFuture();
for (DynamicCacheStartFuture f : initiateCacheChanges(reqs, false))
fut.add(f);
fut.markInitialized();
return fut;
}
/**
*
*/
public Collection<DynamicCacheChangeRequest> startAllCachesRequests() throws IgniteCheckedException {
List<DynamicCacheChangeRequest> reqs = new ArrayList<>();
if (!ctx.config().isDaemon() &&
sharedCtx.pageStore() != null &&
sharedCtx.database().persistenceEnabled()) {
Set<String> savedCacheNames = sharedCtx.pageStore().savedCacheNames();
for (String name : savedCacheNames) {
CacheConfiguration cfg = sharedCtx.pageStore().readConfiguration(name);
if (cfg != null)
reqs.add(createRequest(cfg, false));
}
for (CacheConfiguration cfg : ctx.config().getCacheConfiguration()) {
if (!savedCacheNames.contains(cfg.getName()))
reqs.add(createRequest(cfg, true));
}
}
else {
for (CacheConfiguration cfg : ctx.config().getCacheConfiguration())
reqs.add(createRequest(cfg, true));
}
return reqs;
}
/**
*
*/
public Collection<DynamicCacheChangeRequest> stopAllCachesRequests(){
List<DynamicCacheChangeRequest> reqs = new ArrayList<>();
for (String cacheName : cacheNames()) {
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(
UUID.randomUUID(), cacheName, ctx.localNodeId());
DynamicCacheDescriptor desc = cacheDescriptor(cacheName);
req.deploymentId(desc.deploymentId());
req.stop(true);
req.destroy(false);
reqs.add(req);
}
return reqs;
}
/**
* @param cfg Cache configuration.
*/
private DynamicCacheChangeRequest createRequest(
CacheConfiguration cfg,
boolean needInit
) throws IgniteCheckedException {
assert cfg != null;
assert cfg.getName() != null;
cloneCheckSerializable(cfg);
if (needInit){
CacheObjectContext cacheObjCtx = ctx.cacheObjects().contextForCache(cfg);
initialize(cfg, cacheObjCtx);
}
String cacheName = cfg.getName();
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(
UUID.randomUUID(), cacheName, ctx.localNodeId());
req.startCacheConfiguration(cfg);
req.template(cfg.getName() != null && cfg.getName().endsWith("*"));
req.nearCacheConfiguration(cfg.getNearConfiguration());
req.deploymentId(IgniteUuid.randomUuid());
req.schema(new QuerySchema(cfg.getQueryEntities()));
if (CU.isUtilityCache(cacheName))
req.cacheType(CacheType.UTILITY);
else if (internalCaches.contains(cacheName))
req.cacheType(CacheType.INTERNAL);
else
req.cacheType(CacheType.USER);
return req;
}
/**
* @param reqs Requests.
* @param failIfExists Fail if exists flag.
* @return Collection of futures.
*/
@SuppressWarnings("TypeMayBeWeakened")
private Collection<DynamicCacheStartFuture> initiateCacheChanges(
Collection<DynamicCacheChangeRequest> reqs,
boolean failIfExists
) {
Collection<DynamicCacheStartFuture> res = new ArrayList<>(reqs.size());
Collection<DynamicCacheChangeRequest> sndReqs = new ArrayList<>(reqs.size());
for (DynamicCacheChangeRequest req : reqs) {
DynamicCacheStartFuture fut = new DynamicCacheStartFuture(req.cacheName(), req.deploymentId(), req);
try {
if (req.stop() || req.close()) {
DynamicCacheDescriptor desc = cacheDescriptor(req.cacheName());
if (desc == null)
// No-op.
fut.onDone();
else {
assert desc.cacheConfiguration() != null : desc;
if (req.close() && desc.cacheConfiguration().getCacheMode() == LOCAL) {
req.close(false);
req.stop(true);
}
IgniteUuid dynamicDeploymentId = desc.deploymentId();
assert dynamicDeploymentId != null : desc;
// Save deployment ID to avoid concurrent stops.
req.deploymentId(dynamicDeploymentId);
fut.deploymentId = dynamicDeploymentId;
}
}
if (fut.isDone())
continue;
DynamicCacheStartFuture old = (DynamicCacheStartFuture)pendingFuts.putIfAbsent(
req.requestId(), fut);
assert old == null; //TODO : check failIfExists.
if (fut.isDone())
continue;
sndReqs.add(req);
}
catch (Exception e) {
fut.onDone(e);
}
finally {
res.add(fut);
}
}
Exception err = null;
if (!sndReqs.isEmpty()) {
try {
ctx.discovery().sendCustomEvent(new DynamicCacheChangeBatch(sndReqs));
if (ctx.isStopping()) {
err = new IgniteCheckedException("Failed to execute dynamic cache change request, " +
"node is stopping.");
}
else if (ctx.clientDisconnected()) {
err = new IgniteClientDisconnectedCheckedException(ctx.cluster().clientReconnectFuture(),
"Failed to execute dynamic cache change request, client node disconnected.");
}
}
catch (IgniteCheckedException e) {
err = e;
}
}
if (err != null) {
for (DynamicCacheStartFuture fut : res)
fut.onDone(err);
}
return res;
}
/**
* @param type Event type.
* @param node Event node.
* @param topVer Topology version.
*/
public void onDiscoveryEvent(int type, ClusterNode node, AffinityTopologyVersion topVer) {
if (type == EVT_NODE_JOINED) {
for (DynamicCacheDescriptor cacheDesc : cacheDescriptors()) {
if (node.id().equals(cacheDesc.receivedFrom()))
cacheDesc.receivedFromStartVersion(topVer);
}
}
sharedCtx.affinity().onDiscoveryEvent(type, node, topVer);
}
/**
* Callback invoked from discovery thread when discovery custom message is received.
*
* @param msg Customer message.
* @param topVer Current topology version.
* @return {@code True} if minor topology version should be increased.
*/
public boolean onCustomEvent(DiscoveryCustomMessage msg, AffinityTopologyVersion topVer) {
if (msg instanceof SchemaAbstractDiscoveryMessage) {
ctx.query().onDiscovery((SchemaAbstractDiscoveryMessage)msg);
return false;
}
if (msg instanceof CacheAffinityChangeMessage)
return sharedCtx.affinity().onCustomEvent(((CacheAffinityChangeMessage)msg));
if (msg instanceof StartFullSnapshotAckDiscoveryMessage &&
((StartFullSnapshotAckDiscoveryMessage)msg).error() == null)
return true;
if (msg instanceof DynamicCacheChangeBatch)
return onCacheChangeRequested((DynamicCacheChangeBatch)msg, topVer);
return false;
}
/**
* @param batch Change request batch.
* @param topVer Current topology version.
* @return {@code True} if minor topology version should be increased.
*/
private boolean onCacheChangeRequested(
DynamicCacheChangeBatch batch,
AffinityTopologyVersion topVer
) {
AffinityTopologyVersion newTopVer = null;
boolean incMinorTopVer = false;
for (DynamicCacheChangeRequest req : batch.requests()) {
initReceivedCacheConfiguration(req);
if (req.template()) {
CacheConfiguration ccfg = req.startCacheConfiguration();
assert ccfg != null : req;
DynamicCacheDescriptor desc = registeredTemplates.get(req.cacheName());
if (desc == null) {
DynamicCacheDescriptor templateDesc = new DynamicCacheDescriptor(ctx, ccfg, req.cacheType(), true,
req.deploymentId(), req.schema());
DynamicCacheDescriptor old = registeredTemplates.put(ccfg.getName(), templateDesc);
assert old == null :
"Dynamic cache map was concurrently modified [new=" + templateDesc + ", old=" + old + ']';
}
TemplateConfigurationFuture fut =
(TemplateConfigurationFuture)pendingTemplateFuts.get(ccfg.getName());
if (fut != null && fut.deploymentId().equals(req.deploymentId()))
fut.onDone();
continue;
}
DynamicCacheDescriptor desc = cacheDescriptor(req.cacheName());
DynamicCacheStartFuture fut = null;
if (ctx.localNodeId().equals(req.initiatingNodeId())) {
fut = (DynamicCacheStartFuture)pendingFuts.get(req.requestId());
if (fut != null && !F.eq(req.deploymentId(), fut.deploymentId()))
fut = null;
}
boolean needExchange = false;
if (req.start()) {
if (desc == null) {
if (req.clientStartOnly()) {
if (fut != null)
fut.onDone(new IgniteCheckedException("Failed to start client cache " +
"(a cache with the given name is not started): " + U.maskName(req.cacheName())));
}
else {
CacheConfiguration ccfg = req.startCacheConfiguration();
assert req.cacheType() != null : req;
assert F.eq(ccfg.getName(), req.cacheName()) : req;
DynamicCacheDescriptor startDesc = new DynamicCacheDescriptor(ctx, ccfg, req.cacheType(), false,
req.deploymentId(), req.schema());
if (newTopVer == null) {
newTopVer = new AffinityTopologyVersion(topVer.topologyVersion(),
topVer.minorTopologyVersion() + 1);
}
startDesc.startTopologyVersion(newTopVer);
DynamicCacheDescriptor old = cacheDescriptor(ccfg.getName(), startDesc);
assert old == null :
"Dynamic cache map was concurrently modified [new=" + startDesc + ", old=" + old + ']';
ctx.discovery().setCacheFilter(
ccfg.getName(),
ccfg.getNodeFilter(),
ccfg.getNearConfiguration() != null,
ccfg.getCacheMode());
ctx.discovery().addClientNode(req.cacheName(),
req.initiatingNodeId(),
req.nearCacheConfiguration() != null);
needExchange = true;
}
}
else {
assert req.initiatingNodeId() != null : req;
// Cache already exists, exchange is needed only if client cache should be created.
ClusterNode node = ctx.discovery().node(req.initiatingNodeId());
boolean clientReq = node != null &&
!ctx.discovery().cacheAffinityNode(node, req.cacheName());
if (req.clientStartOnly()) {
needExchange = clientReq && ctx.discovery().addClientNode(req.cacheName(),
req.initiatingNodeId(),
req.nearCacheConfiguration() != null);
}
else {
if (req.failIfExists()) {
if (fut != null)
fut.onDone(new CacheExistsException("Failed to start cache " +
"(a cache with the same name is already started): " + U.maskName(req.cacheName())));
}
else {
needExchange = clientReq && ctx.discovery().addClientNode(req.cacheName(),
req.initiatingNodeId(),
req.nearCacheConfiguration() != null);
if (needExchange)
req.clientStartOnly(true);
}
}
if (needExchange) {
if (newTopVer == null) {
newTopVer = new AffinityTopologyVersion(topVer.topologyVersion(),
topVer.minorTopologyVersion() + 1);
}
desc.clientCacheStartVersion(newTopVer);
}
}
if (!needExchange && desc != null) {
if (desc.clientCacheStartVersion() != null)
req.cacheFutureTopologyVersion(desc.clientCacheStartVersion());
else
req.cacheFutureTopologyVersion(desc.startTopologyVersion());
}
}
else if (req.globalStateChange() || req.resetLostPartitions())
needExchange = true;
else {
assert req.stop() ^ req.close() : req;
if (desc != null) {
if (req.stop()) {
DynamicCacheDescriptor old = registeredCaches.remove(req.cacheName());
assert old != null : "Dynamic cache map was concurrently modified [req=" + req + ']';
ctx.discovery().removeCacheFilter(req.cacheName());
needExchange = true;
}
else {
assert req.close() : req;
needExchange = ctx.discovery().onClientCacheClose(req.cacheName(), req.initiatingNodeId());
}
}
}
req.exchangeNeeded(needExchange);
incMinorTopVer |= needExchange;
}
return incMinorTopVer;
}
/**
* @param req Cache change request.
*/
private void initReceivedCacheConfiguration(DynamicCacheChangeRequest req) {
if (req.startCacheConfiguration() != null) {
CacheConfiguration ccfg = req.startCacheConfiguration();
if (ccfg.isStoreKeepBinary() == null)
ccfg.setStoreKeepBinary(CacheConfiguration.DFLT_STORE_KEEP_BINARY);
}
}
/**
* Checks that preload-order-dependant caches has SYNC or ASYNC preloading mode.
*
* @param cfgs Caches.
* @return Maximum detected preload order.
* @throws IgniteCheckedException If validation failed.
*/
private int validatePreloadOrder(CacheConfiguration[] cfgs) throws IgniteCheckedException {
int maxOrder = 0;
for (CacheConfiguration cfg : cfgs) {
int rebalanceOrder = cfg.getRebalanceOrder();
if (rebalanceOrder > 0) {
if (cfg.getCacheMode() == LOCAL)
throw new IgniteCheckedException("Rebalance order set for local cache (fix configuration and restart the " +
"node): " + U.maskName(cfg.getName()));
if (cfg.getRebalanceMode() == CacheRebalanceMode.NONE)
throw new IgniteCheckedException("Only caches with SYNC or ASYNC rebalance mode can be set as rebalance " +
"dependency for other caches [cacheName=" + U.maskName(cfg.getName()) +
", rebalanceMode=" + cfg.getRebalanceMode() + ", rebalanceOrder=" + cfg.getRebalanceOrder() + ']');
maxOrder = Math.max(maxOrder, rebalanceOrder);
}
else if (rebalanceOrder < 0)
throw new IgniteCheckedException("Rebalance order cannot be negative for cache (fix configuration and restart " +
"the node) [cacheName=" + U.maskName(cfg.getName()) + ", rebalanceOrder=" + rebalanceOrder + ']');
}
return maxOrder;
}
/** {@inheritDoc} */
@Nullable @Override public IgniteNodeValidationResult validateNode(ClusterNode node) {
return validateHashIdResolvers(node);
}
/**
* @param node Joining node.
* @return Validation result or {@code null} in case of success.
*/
@Nullable private IgniteNodeValidationResult validateHashIdResolvers(ClusterNode node) {
if (!node.isClient()) {
for (DynamicCacheDescriptor desc : cacheDescriptors()) {
CacheConfiguration cfg = desc.cacheConfiguration();
if (cfg.getAffinity() instanceof RendezvousAffinityFunction) {
RendezvousAffinityFunction aff = (RendezvousAffinityFunction)cfg.getAffinity();
Object nodeHashObj = aff.resolveNodeHash(node);
for (ClusterNode topNode : ctx.discovery().allNodes()) {
Object topNodeHashObj = aff.resolveNodeHash(topNode);
if (nodeHashObj.hashCode() == topNodeHashObj.hashCode()) {
String errMsg = "Failed to add node to topology because it has the same hash code for " +
"partitioned affinity as one of existing nodes [cacheName=" +
U.maskName(cfg.getName()) + ", existingNodeId=" + topNode.id() + ']';
String sndMsg = "Failed to add node to topology because it has the same hash code for " +
"partitioned affinity as one of existing nodes [cacheName=" +
U.maskName(cfg.getName()) + ", existingNodeId=" + topNode.id() + ']';
return new IgniteNodeValidationResult(topNode.id(), errMsg, sndMsg);
}
}
}
}
}
return null;
}
/**
* Checks that remote caches has configuration compatible with the local.
*
* @param locCfg Local configuration.
* @param rmtCfg Remote configuration.
* @param rmtNode Remote node.
* @throws IgniteCheckedException If check failed.
*/
private void checkCache(CacheConfiguration locCfg, CacheConfiguration rmtCfg, ClusterNode rmtNode) throws IgniteCheckedException {
ClusterNode locNode = ctx.discovery().localNode();
UUID rmt = rmtNode.id();
GridCacheAttributes rmtAttr = new GridCacheAttributes(rmtCfg);
GridCacheAttributes locAttr = new GridCacheAttributes(locCfg);
boolean isLocAff = CU.affinityNode(locNode, locCfg.getNodeFilter());
boolean isRmtAff = CU.affinityNode(rmtNode, rmtCfg.getNodeFilter());
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "cacheMode", "Cache mode",
locAttr.cacheMode(), rmtAttr.cacheMode(), true);
if (rmtAttr.cacheMode() != LOCAL) {
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "interceptor", "Cache Interceptor",
locAttr.interceptorClassName(), rmtAttr.interceptorClassName(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "atomicityMode",
"Cache atomicity mode", locAttr.atomicityMode(), rmtAttr.atomicityMode(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "cachePreloadMode",
"Cache preload mode", locAttr.cacheRebalanceMode(), rmtAttr.cacheRebalanceMode(), true);
boolean checkStore = isLocAff && isRmtAff;
if (checkStore)
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "storeFactory", "Store factory",
locAttr.storeFactoryClassName(), rmtAttr.storeFactoryClassName(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "cacheAffinity", "Cache affinity",
locAttr.cacheAffinityClassName(), rmtAttr.cacheAffinityClassName(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "cacheAffinityMapper",
"Cache affinity mapper", locAttr.cacheAffinityMapperClassName(),
rmtAttr.cacheAffinityMapperClassName(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "affinityPartitionsCount",
"Affinity partitions count", locAttr.affinityPartitionsCount(),
rmtAttr.affinityPartitionsCount(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "evictionFilter", "Eviction filter",
locAttr.evictionFilterClassName(), rmtAttr.evictionFilterClassName(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "evictionPolicy", "Eviction policy",
locAttr.evictionPolicyClassName(), rmtAttr.evictionPolicyClassName(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "transactionManagerLookup",
"Transaction manager lookup", locAttr.transactionManagerLookupClassName(),
rmtAttr.transactionManagerLookupClassName(), false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "defaultLockTimeout",
"Default lock timeout", locAttr.defaultLockTimeout(), rmtAttr.defaultLockTimeout(), false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "preloadBatchSize",
"Preload batch size", locAttr.rebalanceBatchSize(), rmtAttr.rebalanceBatchSize(), false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeSynchronizationMode",
"Write synchronization mode", locAttr.writeSynchronization(), rmtAttr.writeSynchronization(),
true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeBehindBatchSize",
"Write behind batch size", locAttr.writeBehindBatchSize(), rmtAttr.writeBehindBatchSize(),
false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeBehindEnabled",
"Write behind enabled", locAttr.writeBehindEnabled(), rmtAttr.writeBehindEnabled(), false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeBehindFlushFrequency",
"Write behind flush frequency", locAttr.writeBehindFlushFrequency(),
rmtAttr.writeBehindFlushFrequency(), false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeBehindFlushSize",
"Write behind flush size", locAttr.writeBehindFlushSize(), rmtAttr.writeBehindFlushSize(),
false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "writeBehindFlushThreadCount",
"Write behind flush thread count", locAttr.writeBehindFlushThreadCount(),
rmtAttr.writeBehindFlushThreadCount(), false);
if (locAttr.cacheMode() == PARTITIONED) {
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "nearEvictionPolicy",
"Near eviction policy", locAttr.nearEvictionPolicyClassName(),
rmtAttr.nearEvictionPolicyClassName(), false);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "affinityIncludeNeighbors",
"Affinity include neighbors", locAttr.affinityIncludeNeighbors(),
rmtAttr.affinityIncludeNeighbors(), true);
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "affinityKeyBackups",
"Affinity key backups", locAttr.affinityKeyBackups(),
rmtAttr.affinityKeyBackups(), true);
}
}
}
/**
* @param rmt Remote node to check.
* @throws IgniteCheckedException If check failed.
*/
private void checkTransactionConfiguration(ClusterNode rmt) throws IgniteCheckedException {
TransactionConfiguration txCfg = rmt.attribute(ATTR_TX_CONFIG);
if (txCfg != null) {
TransactionConfiguration locTxCfg = ctx.config().getTransactionConfiguration();
if (locTxCfg.isTxSerializableEnabled() != txCfg.isTxSerializableEnabled())
throw new IgniteCheckedException("Serializable transactions enabled mismatch " +
"(fix txSerializableEnabled property or set -D" + IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK + "=true " +
"system property) [rmtNodeId=" + rmt.id() +
", locTxSerializableEnabled=" + locTxCfg.isTxSerializableEnabled() +
", rmtTxSerializableEnabled=" + txCfg.isTxSerializableEnabled() + ']');
}
}
/**
* @param cfg Cache configuration.
* @return Query manager.
*/
private GridCacheQueryManager queryManager(CacheConfiguration cfg) {
return cfg.getCacheMode() == LOCAL ? new GridCacheLocalQueryManager() : new GridCacheDistributedQueryManager();
}
/**
* @return Last data version.
*/
public long lastDataVersion() {
long max = 0;
for (GridCacheAdapter<?, ?> cache : caches.values()) {
GridCacheContext<?, ?> ctx = cache.context();
if (ctx.versions().last().order() > max)
max = ctx.versions().last().order();
if (ctx.isNear()) {
ctx = ctx.near().dht().context();
if (ctx.versions().last().order() > max)
max = ctx.versions().last().order();
}
}
return max;
}
/**
* @param name Cache name.
* @param <K> type of keys.
* @param <V> type of values.
* @return Cache instance for given name.
*/
@SuppressWarnings("unchecked")
public <K, V> IgniteInternalCache<K, V> cache(String name) {
assert name != null;
if (log.isDebugEnabled())
log.debug("Getting cache for name: " + name);
IgniteCacheProxy<K, V> jcache = (IgniteCacheProxy<K, V>)jCacheProxies.get(name);
return jcache == null ? null : jcache.internalProxy();
}
/**
* @param name Cache name.
* @return Cache instance for given name.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
public <K, V> IgniteInternalCache<K, V> getOrStartCache(String name) throws IgniteCheckedException {
assert name != null;
if (log.isDebugEnabled())
log.debug("Getting cache for name: " + name);
IgniteCacheProxy<?, ?> cache = jCacheProxies.get(name);
if (cache == null) {
dynamicStartCache(null, name, null, false, true, true).get();
cache = jCacheProxies.get(name);
}
return cache == null ? null : (IgniteInternalCache<K, V>)cache.internalProxy();
}
/**
* @return All configured cache instances.
*/
public Collection<IgniteInternalCache<?, ?>> caches() {
return F.viewReadOnly(jCacheProxies.values(), new IgniteClosure<IgniteCacheProxy<?, ?>,
IgniteInternalCache<?, ?>>() {
@Override public IgniteInternalCache<?, ?> apply(IgniteCacheProxy<?, ?> entries) {
return entries.internalProxy();
}
});
}
/**
* @return All configured cache instances.
*/
public Collection<IgniteCacheProxy<?, ?>> jcaches() {
return jCacheProxies.values();
}
/**
* Gets utility cache.
*
* @return Utility cache.
*/
public <K, V> IgniteInternalCache<K, V> utilityCache() {
return internalCacheEx(CU.UTILITY_CACHE_NAME);
}
/**
* Gets utility cache for atomic data structures.
*
* @return Utility cache for atomic data structures.
*/
public <K, V> IgniteInternalCache<K, V> atomicsCache() {
return internalCacheEx(CU.ATOMICS_CACHE_NAME);
}
/**
* @param name Cache name.
* @return Cache.
*/
private <K, V> IgniteInternalCache<K, V> internalCacheEx(String name) {
if (ctx.discovery().localNode().isClient()) {
IgniteCacheProxy<K, V> proxy = (IgniteCacheProxy<K, V>)jCacheProxies.get(name);
assert proxy != null;
return proxy.internalProxy();
}
return internalCache(name);
}
/**
* @param name Cache name.
* @param <K> type of keys.
* @param <V> type of values.
* @return Cache instance for given name.
*/
@SuppressWarnings("unchecked")
public <K, V> IgniteInternalCache<K, V> publicCache(String name) {
assert name != null;
if (log.isDebugEnabled())
log.debug("Getting public cache for name: " + name);
DynamicCacheDescriptor desc = cacheDescriptor(name);
if (desc == null)
throw new IllegalArgumentException("Cache is not started: " + name);
if (!desc.cacheType().userCache())
throw new IllegalStateException("Failed to get cache because it is a system cache: " + name);
IgniteCacheProxy<K, V> jcache = (IgniteCacheProxy<K, V>)jCacheProxies.get(name);
if (jcache == null)
throw new IllegalArgumentException("Cache is not started: " + name);
return jcache.internalProxy();
}
/**
* @param cacheName Cache name.
* @param <K> type of keys.
* @param <V> type of values.
* @return Cache instance for given name.
* @throws IgniteCheckedException If failed.
*/
public <K, V> IgniteCacheProxy<K, V> publicJCache(String cacheName) throws IgniteCheckedException {
return publicJCache(cacheName, true, true);
}
/**
* @param cacheName Cache name.
* @param failIfNotStarted If {@code true} throws {@link IllegalArgumentException} if cache is not started,
* otherwise returns {@code null} in this case.
* @param checkThreadTx If {@code true} checks that current thread does not have active transactions.
* @return Cache instance for given name.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings({"unchecked", "ConstantConditions"})
@Nullable public <K, V> IgniteCacheProxy<K, V> publicJCache(String cacheName,
boolean failIfNotStarted,
boolean checkThreadTx) throws IgniteCheckedException {
assert cacheName != null;
if (log.isDebugEnabled())
log.debug("Getting public cache for name: " + cacheName);
IgniteCacheProxy<?, ?> cache = jCacheProxies.get(cacheName);
DynamicCacheDescriptor desc = cacheDescriptor(cacheName);
if (desc != null && !desc.cacheType().userCache())
throw new IllegalStateException("Failed to get cache because it is a system cache: " + cacheName);
if (cache == null) {
dynamicStartCache(null, cacheName, null, false, failIfNotStarted, checkThreadTx).get();
cache = jCacheProxies.get(cacheName);
}
return (IgniteCacheProxy<K, V>)cache;
}
/**
* Get configuration for the given cache.
*
* @param name Cache name.
* @return Cache configuration.
*/
public CacheConfiguration cacheConfiguration(String name) {
assert name != null;
DynamicCacheDescriptor desc = cacheDescriptor(name);
if (desc == null)
throw new IllegalStateException("Cache doesn't exist: " + name);
else
return desc.cacheConfiguration();
}
/**
* Get registered cache descriptor.
*
* @param name Name.
* @return Descriptor.
*/
public DynamicCacheDescriptor cacheDescriptor(String name) {
return name != null ? registeredCaches.get(name) : null;
}
/**
* Put registered cache descriptor.
*
* @param name Name.
* @param desc Descriptor.
* @return Old descriptor (if any).
*/
private DynamicCacheDescriptor cacheDescriptor(String name, DynamicCacheDescriptor desc) {
assert name != null;
return registeredCaches.put(name, desc);
}
/**
* @return Cache descriptors.
*/
public Collection<DynamicCacheDescriptor> cacheDescriptors() {
return registeredCaches.values();
}
/**
* @param cacheId Cache ID.
* @return Cache descriptor.
*/
@Nullable public DynamicCacheDescriptor cacheDescriptor(int cacheId) {
for (DynamicCacheDescriptor cacheDesc : cacheDescriptors()) {
CacheConfiguration ccfg = cacheDesc.cacheConfiguration();
assert ccfg != null : cacheDesc;
if (CU.cacheId(ccfg.getName()) == cacheId)
return cacheDesc;
}
return null;
}
/**
* @param cacheCfg Cache configuration template.
* @throws IgniteCheckedException If failed.
*/
public void addCacheConfiguration(CacheConfiguration cacheCfg) throws IgniteCheckedException {
assert cacheCfg.getName() != null;
String masked = cacheCfg.getName();
DynamicCacheDescriptor desc = registeredTemplates.get(masked);
if (desc != null)
return;
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(UUID.randomUUID(), cacheCfg.getName(), ctx.localNodeId());
CacheConfiguration cfg = new CacheConfiguration(cacheCfg);
req.template(true);
req.startCacheConfiguration(cfg);
req.schema(new QuerySchema(cfg.getQueryEntities()));
req.deploymentId(IgniteUuid.randomUuid());
TemplateConfigurationFuture fut = new TemplateConfigurationFuture(req.cacheName(), req.deploymentId());
TemplateConfigurationFuture old =
(TemplateConfigurationFuture)pendingTemplateFuts.putIfAbsent(cacheCfg.getName(), fut);
if (old != null)
fut = old;
Exception err = null;
try {
ctx.discovery().sendCustomEvent(new DynamicCacheChangeBatch(Collections.singleton(req)));
if (ctx.isStopping()) {
err = new IgniteCheckedException("Failed to execute dynamic cache change request, " +
"node is stopping.");
}
else if (ctx.clientDisconnected()) {
err = new IgniteClientDisconnectedCheckedException(ctx.cluster().clientReconnectFuture(),
"Failed to execute dynamic cache change request, client node disconnected.");
}
}
catch (IgniteCheckedException e) {
err = e;
}
if (err != null)
fut.onDone(err);
fut.get();
}
/**
* @param name Cache name.
* @return Cache instance for given name.
*/
@SuppressWarnings("unchecked")
public <K, V> IgniteCacheProxy<K, V> jcache(String name) {
assert name != null;
IgniteCacheProxy<K, V> cache = (IgniteCacheProxy<K, V>)jCacheProxies.get(name);
if (cache == null)
throw new IllegalArgumentException("Cache is not configured: " + name);
return cache;
}
/**
* @return All configured public cache instances.
*/
public Collection<IgniteCacheProxy<?, ?>> publicCaches() {
Collection<IgniteCacheProxy<?, ?>> res = new ArrayList<>(jCacheProxies.size());
for (Map.Entry<String, IgniteCacheProxy<?, ?>> entry : jCacheProxies.entrySet()) {
if (entry.getValue().context().userCache())
res.add(entry.getValue());
}
return res;
}
/**
* @param name Cache name.
* @param <K> type of keys.
* @param <V> type of values.
* @return Cache instance for given name.
*/
@SuppressWarnings("unchecked")
public <K, V> GridCacheAdapter<K, V> internalCache(String name) {
assert name != null;
if (log.isDebugEnabled())
log.debug("Getting internal cache adapter: " + name);
return (GridCacheAdapter<K, V>)caches.get(name);
}
/**
* Cancel all user operations.
*/
private void cancelFutures() {
sharedCtx.mvcc().onStop();
Exception err = new IgniteCheckedException("Operation has been cancelled (node is stopping).");
for (IgniteInternalFuture fut : pendingFuts.values())
((GridFutureAdapter)fut).onDone(err);
for (IgniteInternalFuture fut : pendingTemplateFuts.values())
((GridFutureAdapter)fut).onDone(err);
}
/**
* @return All internal cache instances.
*/
public Collection<GridCacheAdapter<?, ?>> internalCaches() {
return caches.values();
}
/**
* @param name Cache name.
* @return {@code True} if specified cache is system, {@code false} otherwise.
*/
public boolean systemCache(String name) {
assert name != null;
DynamicCacheDescriptor desc = cacheDescriptor(name);
return desc != null && !desc.cacheType().userCache();
}
/** {@inheritDoc} */
@Override public void printMemoryStats() {
X.println(">>> ");
for (GridCacheAdapter c : caches.values()) {
X.println(">>> Cache memory stats [igniteInstanceName=" + ctx.igniteInstanceName() +
", cache=" + c.name() + ']');
c.context().printMemoryStats();
}
}
/**
* Callback invoked by deployment manager for whenever a class loader gets undeployed.
*
* @param ldr Class loader.
*/
public void onUndeployed(ClassLoader ldr) {
if (!ctx.isStopping()) {
for (GridCacheAdapter<?, ?> cache : caches.values()) {
// Do not notify system caches and caches for which deployment is disabled.
if (cache.context().userCache() && cache.context().deploymentEnabled())
cache.onUndeploy(ldr);
}
}
}
/**
* @return Shared context.
*/
public <K, V> GridCacheSharedContext<K, V> context() {
return (GridCacheSharedContext<K, V>)sharedCtx;
}
/**
* @return Transactions interface implementation.
*/
public IgniteTransactionsEx transactions() {
return transactions;
}
/**
* Starts client caches that do not exist yet.
*
* @throws IgniteCheckedException In case of error.
*/
public void createMissingQueryCaches() throws IgniteCheckedException {
for (Map.Entry<String, DynamicCacheDescriptor> e : registeredCaches.entrySet()) {
DynamicCacheDescriptor desc = e.getValue();
if (isMissingQueryCache(desc))
dynamicStartCache(null, desc.cacheConfiguration().getName(), null, false, true, true).get();
}
}
/**
* Whether cache defined by provided descriptor is not yet started and has queries enabled.
*
* @param desc Descriptor.
* @return {@code True} if this is missing query cache.
*/
private boolean isMissingQueryCache(DynamicCacheDescriptor desc) {
CacheConfiguration ccfg = desc.cacheConfiguration();
return !caches.containsKey(ccfg.getName()) && QueryUtils.isEnabled(ccfg);
}
/**
* Registers MBean for cache components.
*
* @param obj Cache component.
* @param cacheName Cache name.
* @param near Near flag.
* @throws IgniteCheckedException If registration failed.
*/
@SuppressWarnings("unchecked")
private void registerMbean(Object obj, @Nullable String cacheName, boolean near)
throws IgniteCheckedException {
assert obj != null;
MBeanServer srvr = ctx.config().getMBeanServer();
assert srvr != null;
cacheName = U.maskName(cacheName);
cacheName = near ? cacheName + "-near" : cacheName;
final Object mbeanImpl = (obj instanceof IgniteMBeanAware) ? ((IgniteMBeanAware)obj).getMBean() : obj;
for (Class<?> itf : mbeanImpl.getClass().getInterfaces()) {
if (itf.getName().endsWith("MBean") || itf.getName().endsWith("MXBean")) {
try {
U.registerCacheMBean(srvr, ctx.igniteInstanceName(), cacheName, obj.getClass().getName(), mbeanImpl,
(Class<Object>)itf);
}
catch (JMException e) {
throw new IgniteCheckedException("Failed to register MBean for component: " + obj, e);
}
break;
}
}
}
/**
* Unregisters MBean for cache components.
*
* @param o Cache component.
* @param cacheName Cache name.
* @param near Near flag.
*/
private void unregisterMbean(Object o, @Nullable String cacheName, boolean near) {
assert o != null;
MBeanServer srvr = ctx.config().getMBeanServer();
assert srvr != null;
cacheName = U.maskName(cacheName);
cacheName = near ? cacheName + "-near" : cacheName;
boolean needToUnregister = o instanceof IgniteMBeanAware;
if (!needToUnregister) {
for (Class<?> itf : o.getClass().getInterfaces()) {
if (itf.getName().endsWith("MBean") || itf.getName().endsWith("MXBean")) {
needToUnregister = true;
break;
}
}
}
if (needToUnregister) {
try {
srvr.unregisterMBean(U.makeCacheMBeanName(ctx.igniteInstanceName(), cacheName, o.getClass().getName()));
}
catch (JMException e) {
U.error(log, "Failed to unregister MBean for component: " + o, e);
}
}
}
/**
* @param ccfg Cache configuration.
* @param objs Extra components.
* @return Components provided in cache configuration which can implement {@link LifecycleAware} interface.
*/
private Iterable<Object> lifecycleAwares(CacheConfiguration ccfg, Object... objs) {
Collection<Object> ret = new ArrayList<>(7 + objs.length);
ret.add(ccfg.getAffinity());
ret.add(ccfg.getAffinityMapper());
ret.add(ccfg.getEvictionFilter());
ret.add(ccfg.getEvictionPolicy());
ret.add(ccfg.getInterceptor());
ret.add(ccfg.getTopologyValidator());
NearCacheConfiguration nearCfg = ccfg.getNearConfiguration();
if (nearCfg != null)
ret.add(nearCfg.getNearEvictionPolicy());
Collections.addAll(ret, objs);
return ret;
}
/**
* @throws IgniteException If transaction exist.
*/
private void checkEmptyTransactions() throws IgniteException {
if (transactions().tx() != null || sharedCtx.lockedTopologyVersion(null) != null)
throw new IgniteException("Cannot start/stop cache within lock or transaction.");
}
/**
* @param val Object to check.
* @return Configuration copy.
* @throws IgniteCheckedException If validation failed.
*/
private CacheConfiguration cloneCheckSerializable(final CacheConfiguration val) throws IgniteCheckedException {
if (val == null)
return null;
return withBinaryContext(new IgniteOutClosureX<CacheConfiguration>() {
@Override public CacheConfiguration applyx() throws IgniteCheckedException {
if (val.getCacheStoreFactory() != null) {
try {
ClassLoader ldr = ctx.config().getClassLoader();
if (ldr == null)
ldr = val.getCacheStoreFactory().getClass().getClassLoader();
U.unmarshal(marsh, U.marshal(marsh, val.getCacheStoreFactory()),
U.resolveClassLoader(ldr, ctx.config()));
}
catch (IgniteCheckedException e) {
throw new IgniteCheckedException("Failed to validate cache configuration. " +
"Cache store factory is not serializable. Cache name: " + U.maskName(val.getName()), e);
}
}
try {
return U.unmarshal(marsh, U.marshal(marsh, val), U.resolveClassLoader(ctx.config()));
}
catch (IgniteCheckedException e) {
throw new IgniteCheckedException("Failed to validate cache configuration " +
"(make sure all objects in cache configuration are serializable): " + U.maskName(val.getName()), e);
}
}
});
}
/**
* @param c Closure.
* @return Closure result.
* @throws IgniteCheckedException If failed.
*/
private <T> T withBinaryContext(IgniteOutClosureX<T> c) throws IgniteCheckedException {
IgniteCacheObjectProcessor objProc = ctx.cacheObjects();
BinaryContext oldCtx = null;
if (objProc instanceof CacheObjectBinaryProcessorImpl) {
GridBinaryMarshaller binMarsh = ((CacheObjectBinaryProcessorImpl)objProc).marshaller();
oldCtx = binMarsh == null ? null : binMarsh.pushContext();
}
try {
return c.applyx();
}
finally {
if (objProc instanceof CacheObjectBinaryProcessorImpl)
GridBinaryMarshaller.popContext(oldCtx);
}
}
/**
* Prepares DynamicCacheChangeRequest for cache creation.
*
* @param ccfg Cache configuration
* @param cacheName Cache name
* @param nearCfg Near cache configuration
* @param cacheType Cache type
* @param failIfExists Fail if exists flag.
* @param failIfNotStarted If {@code true} fails if cache is not started.
* @return Request or {@code null} if cache already exists.
* @throws IgniteCheckedException if some of pre-checks failed
* @throws CacheExistsException if cache exists and failIfExists flag is {@code true}
*/
private DynamicCacheChangeRequest prepareCacheChangeRequest(
@Nullable CacheConfiguration ccfg,
String cacheName,
@Nullable NearCacheConfiguration nearCfg,
CacheType cacheType,
boolean failIfExists,
boolean failIfNotStarted
) throws IgniteCheckedException {
DynamicCacheDescriptor desc = cacheDescriptor(cacheName);
DynamicCacheChangeRequest req = new DynamicCacheChangeRequest(UUID.randomUUID(), cacheName, ctx.localNodeId());
req.failIfExists(failIfExists);
if (ccfg != null) {
cloneCheckSerializable(ccfg);
if (desc != null) {
if (failIfExists) {
throw new CacheExistsException("Failed to start cache " +
"(a cache with the same name is already started): " + cacheName);
}
else {
CacheConfiguration descCfg = desc.cacheConfiguration();
// Check if we were asked to start a near cache.
if (nearCfg != null) {
if (CU.affinityNode(ctx.discovery().localNode(), descCfg.getNodeFilter())) {
// If we are on a data node and near cache was enabled, return success, else - fail.
if (descCfg.getNearConfiguration() != null)
return null;
else
throw new IgniteCheckedException("Failed to start near " +
"cache (local node is an affinity node for cache): " + cacheName);
}
else
// If local node has near cache, return success.
req.clientStartOnly(true);
}
else
req.clientStartOnly(true);
req.deploymentId(desc.deploymentId());
req.startCacheConfiguration(descCfg);
req.schema(desc.schema());
}
}
else {
req.deploymentId(IgniteUuid.randomUuid());
CacheConfiguration cfg = new CacheConfiguration(ccfg);
CacheObjectContext cacheObjCtx = ctx.cacheObjects().contextForCache(cfg);
initialize(cfg, cacheObjCtx);
req.startCacheConfiguration(cfg);
req.schema(new QuerySchema(cfg.getQueryEntities()));
}
}
else {
req.clientStartOnly(true);
if (desc != null)
ccfg = desc.cacheConfiguration();
if (ccfg == null) {
if (failIfNotStarted) {
throw new CacheExistsException("Failed to start client cache " +
"(a cache with the given name is not started): " + cacheName);
}
else
return null;
}
req.deploymentId(desc.deploymentId());
req.startCacheConfiguration(ccfg);
req.schema(desc.schema());
}
if (nearCfg != null)
req.nearCacheConfiguration(nearCfg);
req.cacheType(cacheType);
return req;
}
/**
* @param obj Object to clone.
* @return Object copy.
* @throws IgniteCheckedException If failed.
*/
public <T> T clone(final T obj) throws IgniteCheckedException {
return withBinaryContext(new IgniteOutClosureX<T>() {
@Override public T applyx() throws IgniteCheckedException {
return U.unmarshal(marsh, U.marshal(marsh, obj), U.resolveClassLoader(ctx.config()));
}
});
}
/**
*
*/
@SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
private class DynamicCacheStartFuture extends GridFutureAdapter<Object> {
/** Start ID. */
@GridToStringInclude
private IgniteUuid deploymentId;
/** Cache name. */
private String cacheName;
/** Change request. */
@GridToStringInclude
private DynamicCacheChangeRequest req;
/**
* @param cacheName Cache name.
* @param deploymentId Deployment ID.
* @param req Cache start request.
*/
private DynamicCacheStartFuture(String cacheName, IgniteUuid deploymentId, DynamicCacheChangeRequest req) {
this.deploymentId = deploymentId;
this.cacheName = cacheName;
this.req = req;
}
/**
* @return Start ID.
*/
public IgniteUuid deploymentId() {
return deploymentId;
}
/**
* @return Request.
*/
public DynamicCacheChangeRequest request() {
return req;
}
/** {@inheritDoc} */
@Override public boolean onDone(@Nullable Object res, @Nullable Throwable err) {
// Make sure to remove future before completion.
pendingFuts.remove(req.requestId(), this);
return super.onDone(res, err);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DynamicCacheStartFuture.class, this);
}
}
/**
*
*/
@SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
private class TemplateConfigurationFuture extends GridFutureAdapter<Object> {
/** Start ID. */
@GridToStringInclude
private IgniteUuid deploymentId;
/** Cache name. */
private String cacheName;
/**
* @param cacheName Cache name.
* @param deploymentId Deployment ID.
*/
private TemplateConfigurationFuture(String cacheName, IgniteUuid deploymentId) {
this.deploymentId = deploymentId;
this.cacheName = cacheName;
}
/**
* @return Start ID.
*/
public IgniteUuid deploymentId() {
return deploymentId;
}
/** {@inheritDoc} */
@Override public boolean onDone(@Nullable Object res, @Nullable Throwable err) {
// Make sure to remove future before completion.
pendingTemplateFuts.remove(cacheName, this);
return super.onDone(res, err);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(TemplateConfigurationFuture.class, this);
}
}
/**
*
*/
private static class LocalAffinityFunction implements AffinityFunction {
/** */
private static final long serialVersionUID = 0L;
/** {@inheritDoc} */
@Override public List<List<ClusterNode>> assignPartitions(AffinityFunctionContext affCtx) {
ClusterNode locNode = null;
for (ClusterNode n : affCtx.currentTopologySnapshot()) {
if (n.isLocal()) {
locNode = n;
break;
}
}
if (locNode == null)
throw new IgniteException("Local node is not included into affinity nodes for 'LOCAL' cache");
List<List<ClusterNode>> res = new ArrayList<>(partitions());
for (int part = 0; part < partitions(); part++)
res.add(Collections.singletonList(locNode));
return Collections.unmodifiableList(res);
}
/** {@inheritDoc} */
@Override public void reset() {
// No-op.
}
/** {@inheritDoc} */
@Override public int partitions() {
return 1;
}
/** {@inheritDoc} */
@Override public int partition(Object key) {
return 0;
}
/** {@inheritDoc} */
@Override public void removeNode(UUID nodeId) {
// No-op.
}
}
/**
*
*/
private class RemovedItemsCleanupTask implements GridTimeoutObject {
/** */
private final IgniteUuid id = IgniteUuid.randomUuid();
/** */
private final long endTime;
/** */
private final long timeout;
/**
* @param timeout Timeout.
*/
RemovedItemsCleanupTask(long timeout) {
this.timeout = timeout;
this.endTime = U.currentTimeMillis() + timeout;
}
/** {@inheritDoc} */
@Override public IgniteUuid timeoutId() {
return id;
}
/** {@inheritDoc} */
@Override public long endTime() {
return endTime;
}
/** {@inheritDoc} */
@Override public void onTimeout() {
ctx.closure().runLocalSafe(new Runnable() {
@Override public void run() {
try {
for (GridCacheContext cacheCtx : sharedCtx.cacheContexts()) {
if (!cacheCtx.isLocal() && cacheCtx.affinityNode()) {
GridDhtPartitionTopology top = null;
try {
top = cacheCtx.topology();
}
catch (IllegalStateException ignore) {
// Cache stopped.
}
if (top != null) {
for (GridDhtLocalPartition part : top.currentLocalPartitions())
part.cleanupRemoveQueue();
}
if (ctx.isStopping())
return;
}
}
}
catch (Exception e) {
U.error(log, "Failed to cleanup removed cache items: " + e, e);
}
if (ctx.isStopping())
return;
addRemovedItemsCleanupTask(timeout);
}
}, true);
}
}
}