You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/06/04 08:03:00 UTC

[01/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Repository: ignite
Updated Branches:
  refs/heads/ignite-5267 c7a7e6423 -> 7e45010b4


http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreDataStructuresTest.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreDataStructuresTest.java b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreDataStructuresTest.java
new file mode 100644
index 0000000..0183779
--- /dev/null
+++ b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreDataStructuresTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.cache.database;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteAtomicLong;
+import org.apache.ignite.IgniteAtomicSequence;
+import org.apache.ignite.IgniteQueue;
+import org.apache.ignite.IgniteSet;
+import org.apache.ignite.configuration.CollectionConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.configuration.PersistentStoreConfiguration;
+import org.apache.ignite.internal.processors.cache.database.wal.FileWriteAheadLogManager;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class IgnitePersistentStoreDataStructuresTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
+
+        MemoryConfiguration dbCfg = new MemoryConfiguration();
+
+        MemoryPolicyConfiguration memPlcCfg = new MemoryPolicyConfiguration();
+
+        memPlcCfg.setName("dfltMemPlc");
+        memPlcCfg.setInitialSize(200 * 1024 * 1024);
+        memPlcCfg.setMaxSize(200 * 1024 * 1024);
+
+        dbCfg.setMemoryPolicies(memPlcCfg);
+        dbCfg.setDefaultMemoryPolicyName("dfltMemPlc");
+
+        cfg.setMemoryConfiguration(dbCfg);
+
+        cfg.setPersistentStoreConfiguration(new PersistentStoreConfiguration());
+
+        cfg.setActiveOnStart(false);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        System.setProperty(FileWriteAheadLogManager.IGNITE_PDS_WAL_MODE, "LOG_ONLY");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        System.clearProperty(FileWriteAheadLogManager.IGNITE_PDS_WAL_MODE);
+
+        GridTestUtils.deleteDbFiles();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        GridTestUtils.deleteDbFiles();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testQueue() throws Exception {
+        Ignite ignite = startGrids(4);
+
+        ignite.active(true);
+
+        IgniteQueue<Object> queue = ignite.queue("testQueue", 100, new CollectionConfiguration());
+
+        for (int i = 0; i < 100; i++)
+            queue.offer(i);
+
+        stopAllGrids();
+
+        ignite = startGrids(4);
+
+        ignite.active(true);
+
+        queue = ignite.queue("testQueue", 0, null);
+
+        for (int i = 0; i < 100; i++)
+            assertEquals(i, queue.poll());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testAtomic() throws Exception {
+        Ignite ignite = startGrids(4);
+
+        ignite.active(true);
+
+        IgniteAtomicLong atomicLong = ignite.atomicLong("testLong", 0, true);
+
+        for (int i = 0; i < 100; i++)
+            atomicLong.incrementAndGet();
+
+        stopAllGrids();
+
+        ignite = startGrids(4);
+
+        ignite.active(true);
+
+        atomicLong = ignite.atomicLong("testLong", 0, false);
+
+        for (int i = 100; i != 0; )
+            assertEquals(i--, atomicLong.getAndDecrement());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testSequence() throws Exception {
+        Ignite ignite = startGrids(4);
+
+        ignite.active(true);
+
+        IgniteAtomicSequence sequence = ignite.atomicSequence("testSequence", 0, true);
+
+        int i = 0;
+
+        while (i < 1000) {
+            sequence.incrementAndGet();
+
+            i++;
+        }
+
+        stopAllGrids();
+
+        ignite = startGrids(4);
+
+        ignite.active(true);
+
+        sequence = ignite.atomicSequence("testSequence", 0, false);
+
+        assertTrue(sequence.incrementAndGet() > i);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testSet() throws Exception {
+        Ignite ignite = startGrids(4);
+
+        ignite.active(true);
+
+        IgniteSet<Object> set = ignite.set("testSet", new CollectionConfiguration());
+
+        for (int i = 0; i < 100; i++)
+            set.add(i);
+
+        stopAllGrids();
+
+        ignite = startGrids(4);
+
+        ignite.active(true);
+
+        set = ignite.set("testSet", null);
+
+        assertFalse(set.add(99));
+
+        for (int i = 0; i < 100; i++)
+            assertTrue(set.contains(i));
+
+        assertEquals(100, set.size());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreMultiNodePutGetRestartSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreMultiNodePutGetRestartSelfTest.java b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreMultiNodePutGetRestartSelfTest.java
index 46ac5d2..a2eb3d4 100644
--- a/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreMultiNodePutGetRestartSelfTest.java
+++ b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreMultiNodePutGetRestartSelfTest.java
@@ -132,8 +132,6 @@ public class IgnitePersistentStoreMultiNodePutGetRestartSelfTest extends GridCom
      * @throws Exception if failed.
      */
     public void testPutGetSimple() throws Exception {
-
-
         info(">>> Will use path: " + allocPath);
 
         startGrids(GRID_CNT);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest.java b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest.java
new file mode 100644
index 0000000..665b9af
--- /dev/null
+++ b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.cache.database;
+
+import org.apache.ignite.cache.affinity.AffinityFunction;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+
+/**
+ *
+ */
+public class IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest
+    extends IgnitePersistentStoreSingleNodeWithIndexingPutGetPersistenceSelfTest {
+    /** {@inheritDoc} */
+    @Override protected void configure(IgniteConfiguration cfg) {
+        super.configure(cfg);
+
+        for (CacheConfiguration ccfg : cfg.getCacheConfiguration()) {
+            AffinityFunction aff = ccfg.getAffinity();
+
+            int parts = aff != null ? aff.partitions() : RendezvousAffinityFunction.DFLT_PARTITION_COUNT;
+
+            ccfg.setGroupName("testGroup-parts" + parts);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/cache/database/pagemem/NoOpPageStoreManager.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/cache/database/pagemem/NoOpPageStoreManager.java b/modules/pds/src/test/java/org/apache/ignite/cache/database/pagemem/NoOpPageStoreManager.java
index 9d5fe69..783faf8 100644
--- a/modules/pds/src/test/java/org/apache/ignite/cache/database/pagemem/NoOpPageStoreManager.java
+++ b/modules/pds/src/test/java/org/apache/ignite/cache/database/pagemem/NoOpPageStoreManager.java
@@ -19,7 +19,7 @@ package org.apache.ignite.cache.database.pagemem;
 
 import java.nio.ByteBuffer;
 import java.util.Collections;
-import java.util.Set;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -29,7 +29,8 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.pagemem.FullPageId;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteFuture;
@@ -52,17 +53,17 @@ public class NoOpPageStoreManager implements IgnitePageStoreManager {
     }
 
     /** {@inheritDoc} */
-    @Override public void initializeForCache(CacheConfiguration ccfg) throws IgniteCheckedException {
+    @Override public void initializeForCache(CacheGroupDescriptor grpDesc, CacheConfiguration ccfg) throws IgniteCheckedException {
         // No-op.
     }
 
     /** {@inheritDoc} */
-    @Override public void shutdownForCache(GridCacheContext cacheCtx, boolean destroy) throws IgniteCheckedException {
+    @Override public void shutdownForCacheGroup(CacheGroupContext grp, boolean destroy) throws IgniteCheckedException {
         // No-op.
     }
 
     /** {@inheritDoc} */
-    @Override public void onPartitionCreated(int cacheId, int partId) throws IgniteCheckedException {
+    @Override public void onPartitionCreated(int grpId, int partId) throws IgniteCheckedException {
         // No-op.
     }
 
@@ -170,17 +171,12 @@ public class NoOpPageStoreManager implements IgnitePageStoreManager {
     }
 
     /** {@inheritDoc} */
-    @Override public Set<String> savedCacheNames() {
-        return Collections.emptySet();
+    @Override public Map<String, CacheConfiguration> readCacheConfigurations() throws IgniteCheckedException {
+        return Collections.emptyMap();
     }
 
     /** {@inheritDoc} */
-    @Override public CacheConfiguration readConfiguration(String cacheName) {
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean hasIndexStore(int cacheId) {
+    @Override public boolean hasIndexStore(int grpId) {
         return false;
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java b/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
index c3e5a70..d38d403 100644
--- a/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
+++ b/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
@@ -18,9 +18,11 @@
 package org.apache.ignite.testsuites;
 
 import junit.framework.TestSuite;
+import org.apache.ignite.cache.database.IgnitePersistentStoreCacheGroupsTest;
 import org.apache.ignite.cache.database.IgnitePersistentStoreClientNearCachePutGetWithPersistenceSelfTest;
 import org.apache.ignite.cache.database.IgnitePersistentStoreDynamicCacheTest;
 import org.apache.ignite.cache.database.IgnitePersistentStoreSingleNodePutGetPersistenceSelfTest;
+import org.apache.ignite.cache.database.IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest;
 import org.apache.ignite.cache.database.IgnitePersistentStoreSingleNodeWithIndexingPutGetPersistenceSelfTest;
 import org.apache.ignite.cache.database.db.IgniteDbMultiNodePutGetRestartSelfTest;
 import org.apache.ignite.cache.database.db.IgniteDbPageEvictionSelfTest;
@@ -80,11 +82,14 @@ public class IgnitePdsTestSuite extends TestSuite {
         suite.addTestSuite(IgniteDbMultiNodePutGetRestartSelfTest.class);
         suite.addTestSuite(IgnitePersistentStoreSingleNodePutGetPersistenceSelfTest.class);
         suite.addTestSuite(IgnitePersistentStoreSingleNodeWithIndexingPutGetPersistenceSelfTest.class);
+        suite.addTestSuite(IgnitePersistentStoreSingleNodeWithIndexingAndGroupPutGetPersistenceSelfTest.class);
         suite.addTestSuite(IgniteDbPageEvictionSelfTest.class);
         suite.addTestSuite(IgnitePersistentStoreDynamicCacheTest.class);
         suite.addTestSuite(IgniteWalDirectoriesConfigurationTest.class);
         suite.addTestSuite(IgnitePersistentStoreClientNearCachePutGetWithPersistenceSelfTest.class);
 
+        suite.addTestSuite(IgnitePersistentStoreCacheGroupsTest.class);
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java b/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
index 223c3cb..2db7ff7 100644
--- a/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
+++ b/modules/pds/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite2.java
@@ -19,10 +19,11 @@ package org.apache.ignite.testsuites;
 
 import junit.framework.TestSuite;
 import org.apache.ignite.cache.database.IgnitePersistentStoreAtomicCacheRebalancingTest;
-import org.apache.ignite.cache.database.IgnitePersistentStoreTxCacheRebalancingTest;
 import org.apache.ignite.cache.database.IgnitePersistentStoreContinuousRestartSelfTest;
+import org.apache.ignite.cache.database.IgnitePersistentStoreDataStructuresTest;
 import org.apache.ignite.cache.database.IgnitePersistentStorePageSizesTest;
 import org.apache.ignite.cache.database.IgnitePersistentStoreRecoveryAfterFileCorruptionTest;
+import org.apache.ignite.cache.database.IgnitePersistentStoreTxCacheRebalancingTest;
 import org.apache.ignite.cache.database.db.DbPageEvictionDuringPartitionClearSelfTest;
 import org.apache.ignite.cache.database.db.IgniteDbWholeClusterRestartSelfTest;
 import org.apache.ignite.cache.database.db.RebalancingOnNotStableTopologyTest;
@@ -70,6 +71,8 @@ public class IgnitePdsTestSuite2 extends TestSuite {
 
         suite.addTestSuite(IgnitePersistentStoreContinuousRestartSelfTest.class);
 
+        suite.addTestSuite(IgnitePersistentStoreDataStructuresTest.class);
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/pom.xml
----------------------------------------------------------------------
diff --git a/modules/yardstick/pom.xml b/modules/yardstick/pom.xml
index f496e02..6e8bf9b 100644
--- a/modules/yardstick/pom.xml
+++ b/modules/yardstick/pom.xml
@@ -62,6 +62,12 @@
 
         <dependency>
             <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-pds</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
             <artifactId>ignite-log4j</artifactId>
             <version>${project.version}</version>
         </dependency>

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
index 11f9472..602b227 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteBenchmarkArguments.java
@@ -20,12 +20,15 @@ package org.apache.ignite.yardstick;
 import com.beust.jcommander.Parameter;
 import org.apache.ignite.cache.CacheWriteSynchronizationMode;
 import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.PersistentStoreConfiguration;
 import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.transactions.TransactionConcurrency;
 import org.apache.ignite.transactions.TransactionIsolation;
 
 import java.util.ArrayList;
 import java.util.List;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Input arguments for Ignite benchmarks.
@@ -82,7 +85,8 @@ public class IgniteBenchmarkArguments {
 
     /** */
     @Parameter(names = {"-r", "--range"}, description = "Key range")
-    public int range = 1_000_000;
+    @GridToStringInclude
+    private int range = 1_000_000;
 
     /** */
     @Parameter(names = {"-sf", "--scaleFactor"}, description = "Scale factor")
@@ -194,6 +198,25 @@ public class IgniteBenchmarkArguments {
     @Parameter(names = {"-ps", "--pageSize"}, description = "Page size")
     private int pageSize = MemoryConfiguration.DFLT_PAGE_SIZE;
 
+    /** */
+    @Parameter(names = {"-cg", "--cacheGrp"}, description = "Cache group for caches")
+    private String cacheGrp;
+
+    /** */
+    @Parameter(names = {"-cc", "--cachesCnt"}, description = "Number of caches to create")
+    private int cachesCnt = 1;
+
+    /** */
+    @Parameter(names = {"-pds", "--persistentStore"}, description = "Persistent store flag")
+    private boolean persistentStoreEnabled;
+
+    /**
+     * @return {@code True} if need set {@link PersistentStoreConfiguration}.
+     */
+    public boolean persistentStoreEnabled() {
+        return persistentStoreEnabled;
+    }
+
     /**
      * @return List of enabled load test operations.
      */
@@ -475,6 +498,20 @@ public class IgniteBenchmarkArguments {
     }
 
     /**
+     * @return Name of cache group to be set for caches.
+     */
+    @Nullable public String cacheGroup() {
+        return cacheGrp;
+    }
+
+    /**
+     * @return Number of caches to create.
+     */
+    public int cachesCount() {
+        return cachesCnt;
+    }
+
+    /**
      * @return Description.
      */
     public String description() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
index 6e25fc4..35fa949 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/IgniteNode.java
@@ -25,11 +25,13 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteSpring;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.cache.eviction.lru.LruEvictionPolicy;
+import org.apache.ignite.configuration.BinaryConfiguration;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.ConnectorConfiguration;
 import org.apache.ignite.configuration.MemoryConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.configuration.PersistentStoreConfiguration;
 import org.apache.ignite.configuration.TransactionConfiguration;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -112,6 +114,9 @@ public class IgniteNode implements BenchmarkServer {
                     cc.setNearConfiguration(nearCfg);
                 }
 
+                if (args.cacheGroup() != null)
+                    cc.setGroupName(args.cacheGroup());
+
                 cc.setWriteSynchronizationMode(args.syncMode());
 
                 cc.setBackups(args.backups());
@@ -152,15 +157,23 @@ public class IgniteNode implements BenchmarkServer {
         c.setCommunicationSpi(commSpi);
 
         if (args.getPageSize() != MemoryConfiguration.DFLT_PAGE_SIZE) {
-            MemoryConfiguration dbCfg = c.getMemoryConfiguration();
+            MemoryConfiguration memCfg = c.getMemoryConfiguration();
 
-            if (dbCfg == null) {
-                dbCfg = new MemoryConfiguration();
+            if (memCfg == null) {
+                memCfg = new MemoryConfiguration();
 
-                c.setMemoryConfiguration(dbCfg);
+                c.setMemoryConfiguration(memCfg);
             }
 
-            dbCfg.setPageSize(args.getPageSize());
+            memCfg.setPageSize(args.getPageSize());
+        }
+
+        if (args.persistentStoreEnabled()) {
+            PersistentStoreConfiguration pcCfg = new PersistentStoreConfiguration();
+
+            c.setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false));
+
+            c.setPersistentStoreConfiguration(pcCfg);
         }
 
         ignite = IgniteSpring.start(c, appCtx);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteCacheAbstractBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteCacheAbstractBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteCacheAbstractBenchmark.java
index da5cb1d..28aa5a2 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteCacheAbstractBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteCacheAbstractBenchmark.java
@@ -19,21 +19,30 @@ package org.apache.ignite.yardstick.cache;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteDataStreamer;
 import org.apache.ignite.cache.affinity.Affinity;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.util.typedef.T2;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.marshaller.jdk.JdkMarshaller;
 import org.apache.ignite.yardstick.IgniteAbstractBenchmark;
+import org.apache.ignite.yardstick.cache.model.SampleValue;
 import org.yardstickframework.BenchmarkConfiguration;
 import org.yardstickframework.BenchmarkUtils;
 
+import static org.yardstickframework.BenchmarkUtils.println;
+
 /**
  * Abstract class for Ignite benchmarks which use cache.
  */
@@ -42,21 +51,96 @@ public abstract class IgniteCacheAbstractBenchmark<K, V> extends IgniteAbstractB
     protected IgniteCache<K, V> cache;
 
     /** */
+    protected List<IgniteCache> testCaches;
+
+    /** */
     private ThreadLocal<ThreadRange> threadRange = new ThreadLocal<>();
 
     /** */
     private AtomicInteger threadIdx = new AtomicInteger();
 
+    /** */
+    private int caches;
+
+    /** */
+    private final AtomicInteger opCacheIdx = new AtomicInteger();
+
+    /** */
+    private final ThreadLocal<IgniteCache<K, V>> opCache = new ThreadLocal<>();
+
+    /**
+     * @return Cache for benchmark operation.
+     */
+    protected final IgniteCache<K, V> cacheForOperation() {
+        return cacheForOperation(false);
+    }
+
+    /**
+     * @param perThread If {@code true} then cache is selected once and set in thread local.
+     * @return Cache for benchmark operation.
+     */
+    protected final IgniteCache<K, V> cacheForOperation(boolean perThread) {
+        if (caches > 1) {
+            if (perThread) {
+                IgniteCache<K, V> cache = opCache.get();
+
+                if (cache == null) {
+                    cache = testCaches.get(opCacheIdx.getAndIncrement() % caches);
+
+                    opCache.set(cache);
+
+                    BenchmarkUtils.println(cfg, "Initialized cache for thread [cache=" + cache.getName() + ']');
+                }
+
+                return cache;
+            }
+            else
+                return testCaches.get(ThreadLocalRandom.current().nextInt(caches));
+        }
+
+        return cache;
+    }
+
     /** {@inheritDoc} */
     @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
         super.setUp(cfg);
 
         cache = cache();
 
+        CacheConfiguration<?, ?> ccfg = cache.getConfiguration(CacheConfiguration.class);
+
+        String grpName = ccfg.getGroupName();
+
         BenchmarkUtils.println(cfg, "Benchmark setUp [name=" + getClass().getSimpleName() +
             ", cacheName="+ cache.getName() +
+            ", cacheGroup="+ grpName +
             ", cacheCfg=" + cache.getConfiguration(CacheConfiguration.class) + ']');
 
+        caches = args.cachesCount();
+
+        if (caches > 1) {
+            List<CacheConfiguration> toCreate = new ArrayList<>();
+
+            for (int i = 0; i < caches - 1; i++) {
+                JdkMarshaller marsh = new JdkMarshaller();
+
+                CacheConfiguration ccfg0 = marsh.unmarshal(marsh.marshal(ccfg), null);
+
+                ccfg0.setName(cache.getName() + "-" + i);
+
+                toCreate.add(ccfg0);
+            }
+
+            Collection<IgniteCache> caches = ignite().getOrCreateCaches(toCreate);
+
+            testCaches = new ArrayList<>(caches);
+
+            testCaches.add(cache);
+
+            BenchmarkUtils.println(cfg, "Created additional caches [caches=" + testCaches.size() +
+                ", grp=" + grpName + ']');
+        }
+
         if (args.printPartitionStatistics()) {
             Map<ClusterNode, T2<List<Integer>, List<Integer>>> parts = new HashMap<>();
 
@@ -104,6 +188,80 @@ public abstract class IgniteCacheAbstractBenchmark<K, V> extends IgniteAbstractB
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    protected final void loadCachesData() throws Exception {
+        List<IgniteCache> caches = testCaches != null ? testCaches : Collections.<IgniteCache>singletonList(cache);
+
+        if (caches.size() > 1) {
+            ExecutorService executor = Executors.newFixedThreadPool(10);
+
+            try {
+                List<Future<?>> futs = new ArrayList<>();
+
+                for (final IgniteCache cache : caches) {
+                    futs.add(executor.submit(new Runnable() {
+                        @Override public void run() {
+                            loadCacheData0(cache.getName());
+                        }
+                    }));
+                }
+
+                for (Future<?> fut : futs)
+                    fut.get();
+            }
+            finally {
+                executor.shutdown();
+            }
+        }
+        else
+            loadCacheData(caches.get(0).getName());
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @param cnt Number of entries to load.
+     */
+    protected final void loadSampleValues(String cacheName, int cnt) {
+        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cacheName)) {
+            for (int i = 0; i < cnt; i++) {
+                dataLdr.addData(i, new SampleValue(i));
+
+                if (i % 100000 == 0) {
+                    if (Thread.currentThread().isInterrupted())
+                        break;
+
+                    println("Loaded entries [cache=" + cacheName + ", cnt=" + i + ']');
+                }
+            }
+        }
+
+        println("Load entries done [cache=" + cacheName + ", cnt=" + cnt + ']');
+    }
+
+    /**
+     * @param cacheName Cache name.
+     */
+    private void loadCacheData0(String cacheName) {
+        println(cfg, "Loading data for cache: " + cacheName);
+
+        long start = System.nanoTime();
+
+        loadCacheData(cacheName);
+
+        long time = ((System.nanoTime() - start) / 1_000_000);
+
+        println(cfg, "Finished populating data [cache=" + cacheName + ", time=" + time + "ms]");
+    }
+
+    /**
+     * @param cacheName Cache name.
+     */
+    protected void loadCacheData(String cacheName) {
+        throw new IllegalStateException("Not implemented for " + getClass().getSimpleName());
+    }
+
+    /**
      * @return Range.
      */
     protected final ThreadRange threadRange() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllBenchmark.java
index 2f76b7c..f563ec8 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllBenchmark.java
@@ -19,6 +19,7 @@ package org.apache.ignite.yardstick.cache;
 
 import java.util.Map;
 import java.util.Set;
+import org.apache.ignite.IgniteCache;
 import org.apache.ignite.internal.util.typedef.internal.U;
 
 /**
@@ -35,6 +36,8 @@ public class IgniteGetAllBenchmark extends IgniteGetBenchmark {
             keys.add(key);
         }
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.getAll(keys);
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllPutAllTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllPutAllTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllPutAllTxBenchmark.java
index a8f6d7b..d984e64 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllPutAllTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAllPutAllTxBenchmark.java
@@ -55,6 +55,8 @@ public class IgniteGetAllPutAllTxBenchmark extends IgniteCacheAbstractBenchmark<
                     vals.put(key, key);
                 }
 
+                IgniteCache<Integer, Integer> cache = cacheForOperation();
+
                 cache.getAll(vals.keySet());
 
                 cache.putAll(vals);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutBenchmark.java
index 8d15e5e..f943f16 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutBenchmark.java
@@ -29,6 +29,8 @@ public class IgniteGetAndPutBenchmark extends IgniteCacheAbstractBenchmark<Integ
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         int key = nextRandom(args.range());
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.getAndPut(key, new SampleValue(key));
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutTxBenchmark.java
index 0a3794c..4a1af43 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetAndPutTxBenchmark.java
@@ -49,6 +49,8 @@ public class IgniteGetAndPutTxBenchmark extends IgniteCacheAbstractBenchmark<Int
             @Override public Void call() throws Exception {
                 int key = nextRandom(args.range());
 
+                IgniteCache<Integer, Object> cache = cacheForOperation();
+
                 cache.getAndPut(key, new SampleValue(key));
 
                 return null;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetBenchmark.java
index 6154ba4..96a99ff 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetBenchmark.java
@@ -19,19 +19,12 @@ package org.apache.ignite.yardstick.cache;
 
 import java.util.Map;
 import org.apache.ignite.IgniteCache;
-import org.apache.ignite.IgniteDataStreamer;
-import org.apache.ignite.yardstick.cache.model.SampleValue;
 import org.yardstickframework.BenchmarkConfiguration;
 
-import static org.yardstickframework.BenchmarkUtils.println;
-
 /**
  * Ignite benchmark that performs get operations.
  */
 public class IgniteGetBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
-    /** */
-    private static final String CACHE_NAME = "atomic";
-
     /** {@inheritDoc} */
     @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
         super.setUp(cfg);
@@ -40,32 +33,20 @@ public class IgniteGetBenchmark extends IgniteCacheAbstractBenchmark<Integer, Ob
             throw new IllegalArgumentException("Preloading amount (\"-pa\", \"--preloadAmount\") " +
                 "must by less then the range (\"-r\", \"--range\").");
 
-        String cacheName = cache().getName();
-
-        println(cfg, "Loading data for cache: " + cacheName);
-
-        long start = System.nanoTime();
-
-        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cacheName)) {
-            for (int i = 0; i < args.preloadAmount(); i++) {
-                dataLdr.addData(i, new SampleValue(i));
-
-                if (i % 100000 == 0) {
-                    if (Thread.currentThread().isInterrupted())
-                        break;
-
-                    println("Loaded entries: " + i);
-                }
-            }
-        }
+        loadCachesData();
+    }
 
-        println(cfg, "Finished populating query data in " + ((System.nanoTime() - start) / 1_000_000) + " ms.");
+    /** {@inheritDoc} */
+    @Override protected void loadCacheData(String cacheName) {
+        loadSampleValues(cacheName, args.preloadAmount());
     }
 
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         int key = nextRandom(args.range());
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.get(key);
 
         return true;
@@ -73,6 +54,6 @@ public class IgniteGetBenchmark extends IgniteCacheAbstractBenchmark<Integer, Ob
 
     /** {@inheritDoc} */
     @Override protected IgniteCache<Integer, Object> cache() {
-        return ignite().cache(CACHE_NAME);
+        return ignite().cache("atomic");
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetEntriesPutAllTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetEntriesPutAllTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetEntriesPutAllTxBenchmark.java
index 501e12d..fd61d5f 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetEntriesPutAllTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteGetEntriesPutAllTxBenchmark.java
@@ -47,6 +47,8 @@ public class IgniteGetEntriesPutAllTxBenchmark extends IgniteCacheAbstractBenchm
 
         doInTransaction(txs, args.txConcurrency(), args.txIsolation(), new Callable<Void>() {
             @Override public Void call() throws Exception {
+                IgniteCache<Integer, Integer> cache = cacheForOperation();
+
                 SortedMap<Integer, Integer> vals = new TreeMap<>();
 
                 for (int i = 0; i < args.batch(); i++) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeBenchmark.java
index a1e80f0..fa29cc4 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeBenchmark.java
@@ -31,6 +31,8 @@ public class IgniteInvokeBenchmark extends IgniteCacheAbstractBenchmark<Integer,
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         int key = nextRandom(args.range());
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.invoke(key, new SetValueEntryProcessor(new SampleValue(key)));
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeTxBenchmark.java
index 64dc6b8..c30ff29 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeTxBenchmark.java
@@ -49,6 +49,8 @@ public class IgniteInvokeTxBenchmark extends IgniteInvokeBenchmark {
             @Override public Void call() throws Exception {
                 int key = nextRandom(args.range());
 
+                IgniteCache<Integer, Object> cache = cacheForOperation();
+
                 cache.invoke(key, new SetValueEntryProcessor(new SampleValue(key)));
 
                 return null;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeWithInjectionBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeWithInjectionBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeWithInjectionBenchmark.java
index ef9d17b..93704cc 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeWithInjectionBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteInvokeWithInjectionBenchmark.java
@@ -34,6 +34,8 @@ public class IgniteInvokeWithInjectionBenchmark extends IgniteCacheAbstractBench
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         int key = nextRandom(args.range());
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.invoke(key, new SetValueEntryProcessor(new SampleValue(key)));
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllBenchmark.java
index 33aa205..dc21029 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllBenchmark.java
@@ -131,6 +131,8 @@ public class IgnitePutAllBenchmark extends IgniteCacheAbstractBenchmark<Integer,
             }
         }
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.putAll(vals);
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllSerializableTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllSerializableTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllSerializableTxBenchmark.java
index 200400e..089ee7a 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllSerializableTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutAllSerializableTxBenchmark.java
@@ -54,6 +54,8 @@ public class IgnitePutAllSerializableTxBenchmark extends IgniteCacheAbstractBenc
             vals.put(key, key);
         }
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         while (true) {
             try (Transaction tx = txs.txStart(OPTIMISTIC, SERIALIZABLE)) {
                 cache.putAll(vals);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutBenchmark.java
index 69db87f..ebee87c 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutBenchmark.java
@@ -29,6 +29,8 @@ public class IgnitePutBenchmark extends IgniteCacheAbstractBenchmark<Integer, Ob
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         int key = nextRandom(args.range());
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.put(key, new SampleValue(key));
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBatchBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBatchBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBatchBenchmark.java
index a9f59d4..9526896 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBatchBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBatchBenchmark.java
@@ -30,6 +30,8 @@ import org.apache.ignite.yardstick.cache.model.SampleValue;
 public class IgnitePutGetBatchBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         Set<Integer> keys = new TreeSet<>();
 
         while (keys.size() < args.batch())

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBenchmark.java
index 42f308c..cc29e47 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetBenchmark.java
@@ -29,6 +29,8 @@ public class IgnitePutGetBenchmark extends IgniteCacheAbstractBenchmark<Integer,
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         int key = nextRandom(args.range());
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         Object val = cache.get(key);
 
         if (val != null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryBenchmark.java
index 1289fa1..eb49b57 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryBenchmark.java
@@ -28,6 +28,8 @@ import org.apache.ignite.yardstick.cache.model.SampleValue;
 public class IgnitePutGetEntryBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         CacheEntry<Integer, Object> val = cache.getEntry(key);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryTxBenchmark.java
index 6e58b41..5b35009 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetEntryTxBenchmark.java
@@ -45,6 +45,8 @@ public class IgnitePutGetEntryTxBenchmark extends IgniteCacheAbstractBenchmark<I
 
         clo = new Callable<Void>() {
             @Override public Void call() throws Exception {
+                IgniteCache<Integer, Object> cache = cacheForOperation();
+
                 int key = nextRandom(0, args.range() / 2);
 
                 CacheEntry<Integer, Object> val = cache.getEntry(key);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBatchBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBatchBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBatchBenchmark.java
index 7ac8180..954f93f 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBatchBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBatchBenchmark.java
@@ -47,6 +47,8 @@ public class IgnitePutGetTxBatchBenchmark extends IgniteCacheAbstractBenchmark<I
 
         clo = new Callable<Void>() {
             @Override public Void call() throws Exception {
+                IgniteCache<Integer, Object> cache = cacheForOperation();
+
                 Set<Integer> keys = new TreeSet<>();
 
                 while (keys.size() < args.batch())

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBenchmark.java
index 3235721..6b11ef8 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutGetTxBenchmark.java
@@ -44,6 +44,8 @@ public class IgnitePutGetTxBenchmark extends IgniteCacheAbstractBenchmark<Intege
 
         clo = new Callable<Void>() {
             @Override public Void call() throws Exception {
+                IgniteCache<Integer, Object> cache = cacheForOperation();
+
                 int key = nextRandom(0, args.range() / 2);
 
                 Object val = cache.get(key);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIfAbsentIndexedValue1Benchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIfAbsentIndexedValue1Benchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIfAbsentIndexedValue1Benchmark.java
index aea909a..8430b12 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIfAbsentIndexedValue1Benchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIfAbsentIndexedValue1Benchmark.java
@@ -31,6 +31,8 @@ public class IgnitePutIfAbsentIndexedValue1Benchmark extends IgniteCacheAbstract
 
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = insCnt.getAndIncrement();
 
         cache.putIfAbsent(key, new Person1(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue1Benchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue1Benchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue1Benchmark.java
index 6f06015..af206fe 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue1Benchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue1Benchmark.java
@@ -27,6 +27,8 @@ import org.apache.ignite.yardstick.cache.model.Person1;
 public class IgnitePutIndexedValue1Benchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         cache.put(key, new Person1(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue2Benchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue2Benchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue2Benchmark.java
index 0112163..7c62714 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue2Benchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue2Benchmark.java
@@ -27,6 +27,8 @@ import org.apache.ignite.yardstick.cache.model.Person2;
 public class IgnitePutIndexedValue2Benchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         cache.put(key, new Person2(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue8Benchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue8Benchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue8Benchmark.java
index dae32b4..a34e386 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue8Benchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutIndexedValue8Benchmark.java
@@ -27,6 +27,8 @@ import org.apache.ignite.yardstick.cache.model.Person8;
 public class IgnitePutIndexedValue8Benchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         cache.put(key, new Person8(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRandomValueSizeBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRandomValueSizeBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRandomValueSizeBenchmark.java
index 180b50e..db631aba 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRandomValueSizeBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRandomValueSizeBenchmark.java
@@ -26,6 +26,8 @@ import org.apache.ignite.IgniteCache;
 public class IgnitePutRandomValueSizeBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
         int size = 64 + nextRandom(64);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRemoveBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRemoveBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRemoveBenchmark.java
index 7ea3d91..564bff5 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRemoveBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutRemoveBenchmark.java
@@ -27,6 +27,8 @@ import org.apache.ignite.yardstick.cache.model.SampleValue;
 public class IgnitePutRemoveBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         cache.put(key, new SampleValue(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxBenchmark.java
index 15b7cd6..0a8a470 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxBenchmark.java
@@ -47,6 +47,8 @@ public class IgnitePutTxBenchmark extends IgniteCacheAbstractBenchmark<Integer,
 
         clo = new Callable<Void>() {
             @Override public Void call() throws Exception {
+                IgniteCache<Integer, Object> cache = cacheForOperation();
+
                 int key = nextRandom(args.range());
 
                 cache.put(key, new SampleValue(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxImplicitBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxImplicitBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxImplicitBenchmark.java
index 89d87a8..d1070fb 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxImplicitBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxImplicitBenchmark.java
@@ -37,6 +37,8 @@ public class IgnitePutTxImplicitBenchmark extends IgniteCacheAbstractBenchmark<I
 
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         // Implicit transaction is used.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxPrimaryOnlyBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxPrimaryOnlyBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxPrimaryOnlyBenchmark.java
index 21275eb..5e79acd 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxPrimaryOnlyBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxPrimaryOnlyBenchmark.java
@@ -39,6 +39,8 @@ public class IgnitePutTxPrimaryOnlyBenchmark extends IgniteCacheAbstractBenchmar
 
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key;
 
         Affinity<Object> aff = ignite().affinity("tx");

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxSkipLocalBackupBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxSkipLocalBackupBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxSkipLocalBackupBenchmark.java
index 63934e6..29d565a 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxSkipLocalBackupBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutTxSkipLocalBackupBenchmark.java
@@ -39,6 +39,8 @@ public class IgnitePutTxSkipLocalBackupBenchmark extends IgniteCacheAbstractBenc
 
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key;
 
         Affinity<Object> aff = ignite().affinity("tx");

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutValue8Benchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutValue8Benchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutValue8Benchmark.java
index 6a3d492..e4ac2e2 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutValue8Benchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgnitePutValue8Benchmark.java
@@ -28,6 +28,8 @@ import java.util.Map;
 public class IgnitePutValue8Benchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         int key = nextRandom(args.range());
 
         cache.put(key, new Person8(key));

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteReplaceIndexedValue1Benchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteReplaceIndexedValue1Benchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteReplaceIndexedValue1Benchmark.java
index cc50c84..70b94ab 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteReplaceIndexedValue1Benchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteReplaceIndexedValue1Benchmark.java
@@ -66,6 +66,8 @@ public class IgniteReplaceIndexedValue1Benchmark extends IgniteCacheAbstractBenc
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         ThreadLocalRandom rnd = ThreadLocalRandom.current();
 
+        IgniteCache<Integer, Object> cache = cacheForOperation();
+
         cache.replace(rnd.nextInt(args.range()), new Person1(rnd.nextInt(args.range())));
 
         return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteScanQueryBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteScanQueryBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteScanQueryBenchmark.java
new file mode 100644
index 0000000..eda4ff6
--- /dev/null
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteScanQueryBenchmark.java
@@ -0,0 +1,88 @@
+/*
+ * 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.yardstick.cache;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.yardstickframework.BenchmarkConfiguration;
+
+/**
+ *
+ */
+public class IgniteScanQueryBenchmark extends IgniteCacheAbstractBenchmark<Integer, Object> {
+    /** {@inheritDoc} */
+    @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
+        super.setUp(cfg);
+
+        loadCachesData();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void loadCacheData(String cacheName) {
+        loadSampleValues(cacheName, args.range());
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean test(Map<Object, Object> ctx) throws Exception {
+        int key = nextRandom(args.range());
+
+        ScanQuery<Integer, Object> qry = new ScanQuery<>();
+
+        qry.setFilter(new KeyFilter(key));
+
+        IgniteCache<Integer, Object> cache = cacheForOperation().withKeepBinary();
+
+        List<IgniteCache.Entry<Integer, Object>> res = cache.query(qry).getAll();
+
+        if (res.size() != 1)
+            throw new Exception("Invalid result size: " + res.size());
+
+        if (res.get(0).getKey() != key)
+            throw new Exception("Invalid entry found [key=" + key + ", entryKey=" + res.get(0).getKey() + ']');
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteCache<Integer, Object> cache() {
+        return ignite().cache("atomic");
+    }
+
+    /**
+     *
+     */
+    static class KeyFilter implements IgniteBiPredicate<Integer, Object> {
+        /** */
+        private final Integer key;
+
+        /**
+         * @param key Key to find.
+         */
+        public KeyFilter(Integer key) {
+            this.key = key;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean apply(Integer key, Object val) {
+            return this.key.equals(key);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryBenchmark.java
index 8e31455..732cb71 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryBenchmark.java
@@ -37,20 +37,22 @@ public class IgniteSqlQueryBenchmark extends IgniteCacheAbstractBenchmark<Intege
     @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
         super.setUp(cfg);
 
-        println(cfg, "Populating query data...");
+        loadCachesData();
+    }
 
-        long start = System.nanoTime();
+    /** {@inheritDoc} */
+    @Override protected void loadCacheData(String cacheName) {
+        try (IgniteDataStreamer<Integer, Person> dataLdr = ignite().dataStreamer(cacheName)) {
+            for (int i = 0; i < args.range(); i++) {
+                if (i % 100 == 0 && Thread.currentThread().isInterrupted())
+                    break;
 
-        try (IgniteDataStreamer<Integer, Person> dataLdr = ignite().dataStreamer(cache.getName())) {
-            for (int i = 0; i < args.range() && !Thread.currentThread().isInterrupted(); i++) {
                 dataLdr.addData(i, new Person(i, "firstName" + i, "lastName" + i, i * 1000));
 
                 if (i % 100000 == 0)
                     println(cfg, "Populated persons: " + i);
             }
         }
-
-        println(cfg, "Finished populating query data in " + ((System.nanoTime() - start) / 1_000_000) + " ms.");
     }
 
     /** {@inheritDoc} */
@@ -79,6 +81,8 @@ public class IgniteSqlQueryBenchmark extends IgniteCacheAbstractBenchmark<Intege
      * @throws Exception If failed.
      */
     private Collection<Cache.Entry<Integer, Object>> executeQuery(double minSalary, double maxSalary) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation(true);
+
         SqlQuery qry = new SqlQuery(Person.class, "salary >= ? and salary <= ?");
 
         qry.setArgs(minSalary, maxSalary);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
index c5fdeb2..8f4829d 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryDistributedJoinBenchmark.java
@@ -47,14 +47,23 @@ public class IgniteSqlQueryDistributedJoinBenchmark extends IgniteCacheAbstractB
 
         println(cfg, "Populating query data...");
 
-        long start = System.nanoTime();
-
         range = args.range();
 
         if (range <= 0)
             throw new IllegalArgumentException();
 
-        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cache.getName())) {
+        println(cfg, "Populating join query data [orgCnt=" + range +
+            ", personCnt=" + range +
+            ", broadcastJoin=" + broadcast + "]");
+
+        loadCachesData();
+
+        executeQueryJoin(0, broadcast, true);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void loadCacheData(String cacheName) {
+        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cacheName)) {
             for (int orgId = 0; orgId < range; orgId++) {
                 dataLdr.addData(orgId, new Organization(orgId, "org" + orgId));
 
@@ -73,13 +82,6 @@ public class IgniteSqlQueryDistributedJoinBenchmark extends IgniteCacheAbstractB
 
             dataLdr.close();
         }
-
-        println(cfg, "Finished populating join query [orgCnt=" + range +
-            ", personCnt=" + range +
-            ", broadcastJoin=" + broadcast +
-            ", time=" + ((System.nanoTime() - start) / 1_000_000) + "ms]");
-
-        executeQueryJoin(0, broadcast, true);
     }
 
     /**
@@ -141,6 +143,8 @@ public class IgniteSqlQueryDistributedJoinBenchmark extends IgniteCacheAbstractB
         qry.setDistributedJoins(true);
         qry.setArgs(orgId);
 
+        IgniteCache<Integer, Object> cache = cacheForOperation(true);
+
         if (planOnly) {
             String plan = (String)cache.query(qry).getAll().get(0).get(0);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryJoinBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryJoinBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryJoinBenchmark.java
index 1f8006d..1160803 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryJoinBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryJoinBenchmark.java
@@ -40,9 +40,15 @@ public class IgniteSqlQueryJoinBenchmark extends IgniteCacheAbstractBenchmark<In
 
         println(cfg, "Populating query data...");
 
-        long start = System.nanoTime();
+        loadCachesData();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void loadCacheData(String cacheName) {
+        if (args.range() < 100)
+            throw new IllegalArgumentException("Invalid range: " + args.range());
 
-        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cache.getName())) {
+        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cacheName)) {
             final int orgRange = args.range() / 10;
 
             // Populate organizations.
@@ -61,8 +67,6 @@ public class IgniteSqlQueryJoinBenchmark extends IgniteCacheAbstractBenchmark<In
                     println(cfg, "Populated persons: " + i);
             }
         }
-
-        println(cfg, "Finished populating join query data in " + ((System.nanoTime() - start) / 1_000_000) + " ms.");
     }
 
     /** {@inheritDoc} */
@@ -100,6 +104,8 @@ public class IgniteSqlQueryJoinBenchmark extends IgniteCacheAbstractBenchmark<In
      * @throws Exception If failed.
      */
     private Collection<List<?>> executeQueryJoin(double minSalary, double maxSalary) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation(true);
+
         SqlFieldsQuery qry = new SqlFieldsQuery(
             "select p.id, p.orgId, p.firstName, p.lastName, p.salary, o.name " +
             "from Person p " +

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutBenchmark.java
index dfa4cbc..a67f0dc 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutBenchmark.java
@@ -25,7 +25,6 @@ import javax.cache.Cache;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.query.SqlQuery;
 import org.apache.ignite.yardstick.cache.model.Person;
-import org.yardstickframework.BenchmarkConfiguration;
 
 import static org.yardstickframework.BenchmarkUtils.println;
 
@@ -40,14 +39,11 @@ public class IgniteSqlQueryPutBenchmark extends IgniteCacheAbstractBenchmark<Int
     private AtomicInteger qryCnt = new AtomicInteger();
 
     /** {@inheritDoc} */
-    @Override public void setUp(BenchmarkConfiguration cfg) throws Exception {
-        super.setUp(cfg);
-    }
-
-    /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
         ThreadLocalRandom rnd = ThreadLocalRandom.current();
 
+        IgniteCache<Integer, Object> cache = cacheForOperation(true);
+
         if (rnd.nextBoolean()) {
             double salary = rnd.nextDouble() * args.range() * 1000;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutSeparatedBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutSeparatedBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutSeparatedBenchmark.java
index b74978e..2ba06cf 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutSeparatedBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/IgniteSqlQueryPutSeparatedBenchmark.java
@@ -55,6 +55,8 @@ public class IgniteSqlQueryPutSeparatedBenchmark extends IgniteCacheAbstractBenc
             }
         }
         else {
+            IgniteCache<Integer, Object> cache = cacheForOperation(true);
+
             int i = rnd.nextInt(args.range());
 
             cache.put(i, new Person(i, "firstName" + i, "lastName" + i, i * 1000));
@@ -70,6 +72,8 @@ public class IgniteSqlQueryPutSeparatedBenchmark extends IgniteCacheAbstractBenc
      * @throws Exception If failed.
      */
     private Collection<Cache.Entry<Integer, Object>> executeQuery(double minSalary, double maxSalary) throws Exception {
+        IgniteCache<Integer, Object> cache = cacheForOperation(true);
+
         SqlQuery qry = new SqlQuery(Person.class, "salary >= ? and salary <= ?");
 
         qry.setArgs(minSalary, maxSalary);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/jdbc/JdbcPutGetBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/jdbc/JdbcPutGetBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/jdbc/JdbcPutGetBenchmark.java
index 8ee7679..a68b2b2 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/jdbc/JdbcPutGetBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/jdbc/JdbcPutGetBenchmark.java
@@ -28,8 +28,8 @@ import static org.apache.ignite.yardstick.cache.jdbc.JdbcPutBenchmark.createUpse
 public class JdbcPutGetBenchmark extends JdbcAbstractBenchmark {
     /** {@inheritDoc} */
     @Override public boolean test(Map<Object, Object> ctx) throws Exception {
-        int newKey = nextRandom(args.range);
-        int newVal = nextRandom(args.range);
+        int newKey = nextRandom(args.range());
+        int newVal = nextRandom(args.range());
 
         try (PreparedStatement stmt = createUpsertStatement(conn.get(), newKey, newVal)) {
             if (stmt.executeUpdate() <= 0)

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
index 4010f5e..681ba34 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/IgniteCacheRandomOperationBenchmark.java
@@ -31,6 +31,9 @@ import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicLong;
 import javax.cache.CacheException;
@@ -267,7 +270,7 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
                                     keys.add(keyCls);
                                 else
                                     throw new IgniteException("Class is unknown for the load test. Make sure you " +
-                                        "specified its full name [clsName=" + queryEntity.getKeyType() + ']');
+                                        "specified its full name [cache=" + cacheName + ", clsName=" + queryEntity.getKeyType() + ']');
                             }
 
                             if (queryEntity.getValueType() != null) {
@@ -277,7 +280,7 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
                                     values.add(valCls);
                                 else
                                     throw new IgniteException("Class is unknown for the load test. Make sure you " +
-                                        "specified its full name [clsName=" + queryEntity.getKeyType() + ']');
+                                        "specified its full name [cache=" + cacheName + ", clsName=" + queryEntity.getValueType() + ']');
 
                                 configureCacheSqlDescriptor(cacheName, queryEntity, valCls);
                             }
@@ -448,25 +451,36 @@ public class IgniteCacheRandomOperationBenchmark extends IgniteAbstractBenchmark
 
         startPreloadLogging(args.preloadLogsInterval());
 
-        Thread[] threads = new Thread[availableCaches.size()];
+        ExecutorService executor = Executors.newFixedThreadPool(10);
 
-        for (int i = 0; i < availableCaches.size(); i++) {
-            final String cacheName = availableCaches.get(i).getName();
+        try {
+            List<Future<?>> futs = new ArrayList<>();
+
+            final Thread thread = Thread.currentThread();
+
+            for (int i = 0; i < availableCaches.size(); i++) {
+                final String cacheName = availableCaches.get(i).getName();
 
-            threads[i] = new Thread() {
-                @Override public void run() {
-                    try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cacheName)) {
-                        for (int i = 0; i < args.preloadAmount() && !isInterrupted(); i++)
-                            dataLdr.addData(createRandomKey(i, cacheName), createRandomValue(i, cacheName));
+                futs.add(executor.submit(new Runnable() {
+                    @Override public void run() {
+                        try (IgniteDataStreamer<Object, Object> dataLdr = ignite().dataStreamer(cacheName)) {
+                            for (int i = 0; i < args.preloadAmount(); i++) {
+                                if (i % 100 == 0 && thread.isInterrupted())
+                                    break;
+
+                                dataLdr.addData(createRandomKey(i, cacheName), createRandomValue(i, cacheName));
+                            }
+                        }
                     }
-                }
-            };
+                }));
+            }
 
-            threads[i].start();
+            for (Future<?> fut : futs)
+                fut.get();
+        }
+        finally {
+            executor.shutdown();
         }
-
-        for (Thread thread : threads)
-            thread.join();
 
         stopPreloadLogging();
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/model/ModelUtil.java
----------------------------------------------------------------------
diff --git a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/model/ModelUtil.java b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/model/ModelUtil.java
index 9268cdd..667e8c9 100644
--- a/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/model/ModelUtil.java
+++ b/modules/yardstick/src/main/java/org/apache/ignite/yardstick/cache/load/model/ModelUtil.java
@@ -38,6 +38,7 @@ public class ModelUtil {
      * Classes of keys.
      */
     private static Class[] keyClasses = {
+        Integer.class,
         Double.class,
         Identifier.class,
         Mark.class,
@@ -104,7 +105,7 @@ public class ModelUtil {
      * @return object from model
      */
     public static Object create(Class c, int id) {
-        Object res = null;
+        Object res;
 
         switch (c.getSimpleName()) {
             case "Double":
@@ -150,6 +151,9 @@ public class ModelUtil {
                 break;
             case "String":
                 res = String.valueOf(id);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported class: " + c.getSimpleName());
         }
 
         return res;


[09/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
index ec9dbbf..791bac0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopologyImpl.java
@@ -40,9 +40,9 @@ import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.managers.discovery.DiscoCache;
 import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.ClusterState;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheMapEntryFactory;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
@@ -68,7 +68,8 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 /**
  * Partition topology.
  */
-@GridToStringExclude class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
+@GridToStringExclude
+public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
     /** If true, then check consistency. */
     private static final boolean CONSISTENCY_CHECK = false;
 
@@ -78,8 +79,11 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
     /** */
     private static final Long ZERO = 0L;
 
-    /** Context. */
-    private final GridCacheContext<?, ?> cctx;
+    /** */
+    private final GridCacheSharedContext ctx;
+
+    /** */
+    private final CacheGroupContext grp;
 
     /** Logger. */
     private final IgniteLogger log;
@@ -114,9 +118,6 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
     /** Lock. */
     private final StripedCompositeReadWriteLock lock = new StripedCompositeReadWriteLock(16);
 
-    /** */
-    private final GridCacheMapEntryFactory entryFactory;
-
     /** Partition update counter. */
     private Map<Integer, T2<Long, Long>> cntrMap = new HashMap<>();
 
@@ -127,25 +128,27 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
     private volatile boolean treatAllPartAsLoc;
 
     /**
-     * @param cctx Context.
-     * @param entryFactory Entry factory.
+     * @param ctx Cache shared context.
+     * @param grp Cache group.
      */
-    GridDhtPartitionTopologyImpl(GridCacheContext<?, ?> cctx, GridCacheMapEntryFactory entryFactory) {
-        assert cctx != null;
+    public GridDhtPartitionTopologyImpl(GridCacheSharedContext ctx,
+        CacheGroupContext grp) {
+        assert ctx != null;
+        assert grp != null;
 
-        this.cctx = cctx;
-        this.entryFactory = entryFactory;
+        this.ctx = ctx;
+        this.grp = grp;
 
-        log = cctx.logger(getClass());
+        log = ctx.logger(getClass());
 
-        locParts = new AtomicReferenceArray<>(cctx.config().getAffinity().partitions());
+        locParts = new AtomicReferenceArray<>(grp.affinityFunction().partitions());
 
-        part2node = new HashMap<>(cctx.config().getAffinity().partitions(), 1.0f);
+        part2node = new HashMap<>(grp.affinityFunction().partitions(), 1.0f);
     }
 
     /** {@inheritDoc} */
-    @Override public int cacheId() {
-        return cctx.cacheId();
+    @Override public int groupId() {
+        return grp.groupId();
     }
 
     /**
@@ -169,7 +172,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
             topVer = AffinityTopologyVersion.NONE;
 
-            discoCache = cctx.discovery().discoCache();
+            discoCache = ctx.discovery().discoCache();
         }
         finally {
             lock.writeLock().unlock();
@@ -240,7 +243,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         AffinityTopologyVersion topVer = this.topVer;
 
         assert topVer.topologyVersion() > 0 : "Invalid topology version [topVer=" + topVer +
-            ", cacheName=" + cctx.name() + ']';
+            ", group=" + grp.cacheOrGroupName() + ']';
 
         return topVer;
     }
@@ -282,7 +285,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
      * @param updateSeq Update sequence.
      */
     private void initPartitions0(GridDhtPartitionsExchangeFuture exchFut, long updateSeq) {
-        ClusterNode loc = cctx.localNode();
+        ClusterNode loc = ctx.localNode();
 
         ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache();
 
@@ -290,23 +293,23 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
         assert topVer.equals(exchFut.topologyVersion()) :
             "Invalid topology [topVer=" + topVer +
-                ", cache=" + cctx.name() +
+                ", grp=" + grp.cacheOrGroupName() +
                 ", futVer=" + exchFut.topologyVersion() +
                 ", fut=" + exchFut + ']';
-        assert cctx.affinity().affinityTopologyVersion().equals(exchFut.topologyVersion()) :
-            "Invalid affinity [topVer=" + cctx.affinity().affinityTopologyVersion() +
-                ", cache=" + cctx.name() +
+        assert grp.affinity().lastVersion().equals(exchFut.topologyVersion()) :
+            "Invalid affinity [topVer=" + grp.affinity().lastVersion() +
+                ", grp=" + grp.cacheOrGroupName() +
                 ", futVer=" + exchFut.topologyVersion() +
                 ", fut=" + exchFut + ']';
 
-        List<List<ClusterNode>> aff = cctx.affinity().assignments(exchFut.topologyVersion());
+        List<List<ClusterNode>> aff = grp.affinity().assignments(exchFut.topologyVersion());
 
-        int num = cctx.affinity().partitions();
+        int num = grp.affinity().partitions();
 
-        if (cctx.rebalanceEnabled()) {
-            boolean added = exchFut.cacheAddedOnExchange(cctx.cacheId(), cctx.receivedFrom());
+        if (grp.rebalanceEnabled()) {
+            boolean added = exchFut.cacheGroupAddedOnExchange(grp.groupId(), grp.receivedFrom());
 
-            boolean first = (loc.equals(oldest) && loc.id().equals(exchId.nodeId()) && exchId.isJoined()) || added;
+            boolean first = added || (loc.equals(oldest) && loc.id().equals(exchId.nodeId()) && exchId.isJoined());
 
             if (first) {
                 assert exchId.isJoined() || added;
@@ -317,7 +320,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
                         boolean owned = locPart.own();
 
-                        assert owned : "Failed to own partition for oldest node [cacheName" + cctx.name() +
+                        assert owned : "Failed to own partition for oldest node [grp=" + grp.cacheOrGroupName() +
                             ", part=" + locPart + ']';
 
                         if (log.isDebugEnabled())
@@ -376,7 +379,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
      * @param updateSeq Update sequence.
      */
     private void createPartitions(List<List<ClusterNode>> aff, long updateSeq) {
-        int num = cctx.affinity().partitions();
+        int num = grp.affinity().partitions();
 
         for (int p = 0; p < num; p++) {
             if (node2part != null && node2part.valid()) {
@@ -398,24 +401,23 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
     /** {@inheritDoc} */
     @Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean affReady)
         throws IgniteCheckedException {
-
         DiscoveryEvent discoEvt = exchFut.discoveryEvent();
 
         ClusterState newState = exchFut.newClusterState();
 
         treatAllPartAsLoc = (newState != null && newState == ClusterState.ACTIVE)
-            || (cctx.kernalContext().state().active()
+            || (ctx.kernalContext().state().active()
             && discoEvt.type() == EventType.EVT_NODE_JOINED
             && discoEvt.eventNode().isLocal()
-            && !cctx.kernalContext().clientNode()
+            && !ctx.kernalContext().clientNode()
         );
 
-        ClusterNode loc = cctx.localNode();
+        ClusterNode loc = ctx.localNode();
 
-        cctx.shared().database().checkpointReadLock();
+        ctx.database().checkpointReadLock();
 
         try {
-            synchronized (cctx.shared().exchange().interruptLock()) {
+            synchronized (ctx.exchange().interruptLock()) {
                 if (Thread.currentThread().isInterrupted())
                     throw new IgniteInterruptedCheckedException("Thread is interrupted: " + Thread.currentThread());
 
@@ -443,7 +445,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     cntrMap.clear();
 
                     // If this is the oldest node.
-                    if (oldest != null && (loc.equals(oldest) || exchFut.cacheAddedOnExchange(cctx.cacheId(), cctx.receivedFrom()))) {
+                    if (oldest != null && (loc.equals(oldest) || exchFut.cacheGroupAddedOnExchange(grp.groupId(), grp.receivedFrom()))) {
                         if (node2part == null) {
                             node2part = new GridDhtPartitionFullMap(oldest.id(), oldest.order(), updateSeq);
 
@@ -470,7 +472,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     if (affReady)
                         initPartitions0(exchFut, updateSeq);
                     else {
-                        List<List<ClusterNode>> aff = cctx.affinity().idealAssignment();
+                        List<List<ClusterNode>> aff = grp.affinity().idealAssignment();
 
                         createPartitions(aff, updateSeq);
                     }
@@ -487,23 +489,32 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             }
         }
         finally {
-            cctx.shared().database().checkpointReadUnlock();
+            ctx.database().checkpointReadUnlock();
         }
     }
 
+    /**
+     * @param p Partition number.
+     * @param topVer Topology version.
+     * @return {@code True} if given partition belongs to local node.
+     */
+    private boolean partitionLocalNode(int p, AffinityTopologyVersion topVer) {
+        return grp.affinity().nodes(p, topVer).contains(ctx.localNode());
+    }
+
     /** {@inheritDoc} */
     @Override public boolean afterExchange(GridDhtPartitionsExchangeFuture exchFut) throws IgniteCheckedException {
         treatAllPartAsLoc = false;
 
         boolean changed = false;
 
-        int num = cctx.affinity().partitions();
+        int num = grp.affinity().partitions();
 
         AffinityTopologyVersion topVer = exchFut.topologyVersion();
 
-        assert cctx.affinity().affinityTopologyVersion().equals(topVer) : "Affinity is not initialized " +
+        assert grp.affinity().lastVersion().equals(topVer) : "Affinity is not initialized " +
             "[topVer=" + topVer +
-            ", affVer=" + cctx.affinity().affinityTopologyVersion() +
+            ", affVer=" + grp.affinity().lastVersion() +
             ", fut=" + exchFut + ']';
 
         lock.writeLock().lock();
@@ -524,7 +535,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             for (int p = 0; p < num; p++) {
                 GridDhtLocalPartition locPart = localPartition0(p, topVer, false, false, false);
 
-                if (cctx.affinity().partitionLocalNode(p, topVer)) {
+                if (partitionLocalNode(p, topVer)) {
                     // This partition will be created during next topology event,
                     // which obviously has not happened at this point.
                     if (locPart == null) {
@@ -537,26 +548,28 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     GridDhtPartitionState state = locPart.state();
 
                     if (state == MOVING) {
-                        if (cctx.rebalanceEnabled()) {
+                        if (grp.rebalanceEnabled()) {
                             Collection<ClusterNode> owners = owners(p);
 
                             // If there are no other owners, then become an owner.
                             if (F.isEmpty(owners)) {
                                 boolean owned = locPart.own();
 
-                                assert owned : "Failed to own partition [cacheName" + cctx.name() + ", locPart=" +
+                                assert owned : "Failed to own partition [grp=" + grp.cacheOrGroupName() + ", locPart=" +
                                     locPart + ']';
 
                                 updateSeq = updateLocal(p, locPart.state(), updateSeq);
 
                                 changed = true;
 
-                                if (cctx.events().isRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) {
+                                if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) {
                                     DiscoveryEvent discoEvt = exchFut.discoveryEvent();
 
-                                    cctx.events().addPreloadEvent(p,
-                                        EVT_CACHE_REBALANCE_PART_DATA_LOST, discoEvt.eventNode(),
-                                        discoEvt.type(), discoEvt.timestamp());
+                                    grp.addRebalanceEvent(p,
+                                        EVT_CACHE_REBALANCE_PART_DATA_LOST,
+                                        discoEvt.eventNode(),
+                                        discoEvt.type(),
+                                        discoEvt.timestamp());
                                 }
 
                                 if (log.isDebugEnabled())
@@ -574,7 +587,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     if (locPart != null) {
                         GridDhtPartitionState state = locPart.state();
 
-                        if (state == MOVING && cctx.kernalContext().state().active()) {
+                        if (state == MOVING && ctx.kernalContext().state().active()) {
                             locPart.rent(false);
 
                             updateSeq = updateLocal(p, locPart.state(), updateSeq);
@@ -588,7 +601,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 }
             }
 
-            updateRebalanceVersion(cctx.affinity().assignments(topVer));
+            updateRebalanceVersion(grp.affinity().assignments(topVer));
 
             consistencyCheck();
         }
@@ -622,11 +635,11 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         GridDhtLocalPartition loc = locParts.get(p);
 
         if (loc == null || loc.state() == EVICTED) {
-            locParts.set(p, loc = new GridDhtLocalPartition(cctx, p, entryFactory));
+            locParts.set(p, loc = new GridDhtLocalPartition(ctx, grp, p));
 
-            if (cctx.shared().pageStore() != null) {
+            if (ctx.pageStore() != null) {
                 try {
-                    cctx.shared().pageStore().onPartitionCreated(cctx.cacheId(), p);
+                    ctx.pageStore().onPartitionCreated(grp.groupId(), p);
                 }
                 catch (IgniteCheckedException e) {
                     // TODO ignite-db
@@ -672,7 +685,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
             state = loc != null ? loc.state() : null;
 
-            boolean belongs = cctx.affinity().partitionLocalNode(p, topVer);
+            boolean belongs = partitionLocalNode(p, topVer);
 
             if (loc != null && state == EVICTED) {
                 locParts.set(p, loc = null);
@@ -692,7 +705,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                         "local node (often may be caused by inconsistent 'key.hashCode()' implementation) " +
                         "[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']');
 
-                locParts.set(p, loc = new GridDhtLocalPartition(cctx, p, entryFactory));
+                locParts.set(p, loc = new GridDhtLocalPartition(ctx, grp, p));
 
                 if (updateSeq)
                     this.updateSeq.incrementAndGet();
@@ -707,9 +720,10 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             lock.writeLock().unlock();
         }
 
-        if (created && cctx.shared().pageStore() != null) {
+        if (created && ctx.pageStore() != null) {
             try {
-                cctx.shared().pageStore().onPartitionCreated(cctx.cacheId(), p);
+                // TODO IGNITE-5075.
+                ctx.pageStore().onPartitionCreated(grp.groupId(), p);
             }
             catch (IgniteCheckedException e) {
                 // TODO ignite-db
@@ -734,8 +748,8 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
     }
 
     /** {@inheritDoc} */
-    @Override public GridDhtLocalPartition localPartition(Object key, boolean create) {
-        return localPartition(cctx.affinity().partition(key), AffinityTopologyVersion.NONE, create);
+    @Override public GridDhtLocalPartition localPartition(int part) {
+        return locParts.get(part);
     }
 
     /** {@inheritDoc} */
@@ -791,7 +805,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 map.put(i, part.state());
             }
 
-            return new GridDhtPartitionMap(cctx.nodeId(),
+            return new GridDhtPartitionMap(ctx.localNodeId(),
                 updateSeq.get(),
                 topVer,
                 Collections.unmodifiableMap(map),
@@ -831,7 +845,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
     /** {@inheritDoc} */
     @Override public List<ClusterNode> nodes(int p, AffinityTopologyVersion topVer) {
-        AffinityAssignment affAssignment = cctx.affinity().assignment(topVer);
+        AffinityAssignment affAssignment = grp.affinity().cachedAffinity(topVer);
 
         List<ClusterNode> affNodes = affAssignment.get(p);
 
@@ -854,8 +868,8 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         try {
             assert node2part != null && node2part.valid() : "Invalid node-to-partitions map [topVer1=" + topVer +
                 ", topVer2=" + this.topVer +
-                ", node=" + cctx.igniteInstanceName() +
-                ", cache=" + cctx.name() +
+                ", node=" + ctx.igniteInstanceName() +
+                ", grp=" + grp.cacheOrGroupName() +
                 ", node2part=" + node2part + ']';
 
             List<ClusterNode> nodes = null;
@@ -867,7 +881,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     HashSet<UUID> affIds = affAssignment.getIds(p);
 
                     if (!affIds.contains(nodeId) && hasState(p, nodeId, OWNING, MOVING)) {
-                        ClusterNode n = cctx.discovery().node(nodeId);
+                        ClusterNode n = ctx.discovery().node(nodeId);
 
                         if (n != null && (topVer.topologyVersion() < 0 || n.order() <= topVer.topologyVersion())) {
                             if (nodes == null) {
@@ -900,7 +914,9 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         AffinityTopologyVersion topVer,
         GridDhtPartitionState state,
         GridDhtPartitionState... states) {
-        Collection<UUID> allIds = topVer.topologyVersion() > 0 ? F.nodeIds(discoCache.cacheAffinityNodes(cctx.cacheId())) : null;
+        Collection<UUID> allIds = topVer.topologyVersion() > 0 ?
+            F.nodeIds(discoCache.cacheGroupAffinityNodes(grp.groupId())) :
+            null;
 
         lock.readLock().lock();
 
@@ -908,7 +924,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             assert node2part != null && node2part.valid() : "Invalid node-to-partitions map [topVer=" + topVer +
                 ", allIds=" + allIds +
                 ", node2part=" + node2part +
-                ", cache=" + cctx.name() + ']';
+                ", grp=" + grp.cacheOrGroupName() + ']';
 
             Collection<UUID> nodeIds = part2node.get(p);
 
@@ -925,7 +941,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     continue;
 
                 if (hasState(p, id, state, states)) {
-                    ClusterNode n = cctx.discovery().node(id);
+                    ClusterNode n = ctx.discovery().node(id);
 
                     if (n != null && (topVer.topologyVersion() < 0 || n.order() <= topVer.topologyVersion()))
                         nodes.add(n);
@@ -941,7 +957,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
     /** {@inheritDoc} */
     @Override public List<ClusterNode> owners(int p, AffinityTopologyVersion topVer) {
-        if (!cctx.rebalanceEnabled())
+        if (!grp.rebalanceEnabled())
             return ownersAndMoving(p, topVer);
 
         return nodes(p, topVer, OWNING);
@@ -954,7 +970,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
     /** {@inheritDoc} */
     @Override public List<ClusterNode> moving(int p) {
-        if (!cctx.rebalanceEnabled())
+        if (!grp.rebalanceEnabled())
             return ownersAndMoving(p, AffinityTopologyVersion.NONE);
 
         return nodes(p, AffinityTopologyVersion.NONE, MOVING);
@@ -979,12 +995,14 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         lock.readLock().lock();
 
         try {
-            assert node2part != null && node2part.valid() : "Invalid node2part [node2part: " + node2part +
-                ", cache=" + cctx.name() +
-                ", started=" + cctx.started() +
+            if (node2part == null || stopping)
+                return null;
+
+            assert node2part.valid() : "Invalid node2part [node2part=" + node2part +
+                ", grp=" + grp.cacheOrGroupName() +
                 ", stopping=" + stopping +
-                ", locNodeId=" + cctx.localNode().id() +
-                ", locName=" + cctx.igniteInstanceName() + ']';
+                ", locNodeId=" + ctx.localNode().id() +
+                ", locName=" + ctx.igniteInstanceName() + ']';
 
             GridDhtPartitionFullMap m = node2part;
 
@@ -1067,7 +1085,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                     // then we keep the newer value.
                     if (newPart != null &&
                         (newPart.updateSequence() < part.updateSequence() ||
-                        (cctx.startTopologyVersion().compareTo(newPart.topologyVersion()) > 0))
+                        (grp.localStartVersion().compareTo(newPart.topologyVersion()) > 0))
                         ) {
                         if (log.isDebugEnabled())
                             log.debug("Overriding partition map in full update map [exchId=" + exchId + ", curPart=" +
@@ -1081,7 +1099,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 for (Iterator<UUID> it = partMap.keySet().iterator(); it.hasNext(); ) {
                     UUID nodeId = it.next();
 
-                    if (!cctx.discovery().alive(nodeId)) {
+                    if (!ctx.discovery().alive(nodeId)) {
                         if (log.isDebugEnabled())
                             log.debug("Removing left node from full map update [nodeId=" + nodeId + ", partMap=" +
                                 partMap + ']');
@@ -1115,11 +1133,11 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
             boolean changed = false;
 
-            AffinityTopologyVersion affVer = cctx.affinity().affinityTopologyVersion();
+            AffinityTopologyVersion affVer = grp.affinity().lastVersion();
 
-            GridDhtPartitionMap nodeMap = partMap.get(cctx.localNodeId());
+            GridDhtPartitionMap nodeMap = partMap.get(ctx.localNodeId());
 
-            if (nodeMap != null && cctx.shared().database().persistenceEnabled()) {
+            if (nodeMap != null && ctx.database().persistenceEnabled()) {
                 for (Map.Entry<Integer, GridDhtPartitionState> e : nodeMap.entrySet()) {
                     int p = e.getKey();
                     GridDhtPartitionState state = e.getValue();
@@ -1188,7 +1206,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             long updateSeq = this.updateSeq.incrementAndGet();
 
             if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) {
-                List<List<ClusterNode>> aff = cctx.affinity().assignments(topVer);
+                List<List<ClusterNode>> aff = grp.affinity().assignments(topVer);
 
                 changed |= checkEvictions(updateSeq, aff);
 
@@ -1201,7 +1219,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 log.debug("Partition map after full update: " + fullMapString());
 
             if (changed)
-                cctx.shared().exchange().scheduleResendPartitions();
+                ctx.exchange().scheduleResendPartitions();
 
             return changed ? localPartitionMap() : null;
         }
@@ -1254,7 +1272,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         if (log.isDebugEnabled())
             log.debug("Updating single partition map [exchId=" + exchId + ", parts=" + mapString(parts) + ']');
 
-        if (!cctx.discovery().alive(parts.nodeId())) {
+        if (!ctx.discovery().alive(parts.nodeId())) {
             if (log.isDebugEnabled())
                 log.debug("Received partition update for non-existing node (will ignore) [exchId=" + exchId +
                     ", parts=" + parts + ']');
@@ -1334,10 +1352,10 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 }
             }
 
-            AffinityTopologyVersion affVer = cctx.affinity().affinityTopologyVersion();
+            AffinityTopologyVersion affVer = grp.affinity().lastVersion();
 
             if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) {
-                List<List<ClusterNode>> aff = cctx.affinity().assignments(topVer);
+                List<List<ClusterNode>> aff = grp.affinity().assignments(topVer);
 
                 changed |= checkEvictions(updateSeq, aff);
 
@@ -1350,7 +1368,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 log.debug("Partition map after single update: " + fullMapString());
 
             if (changed)
-                cctx.shared().exchange().scheduleResendPartitions();
+                ctx.exchange().scheduleResendPartitions();
 
             return changed ? localPartitionMap() : null;
         }
@@ -1364,7 +1382,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         lock.writeLock().lock();
 
         try {
-            int parts = cctx.affinity().partitions();
+            int parts = grp.affinity().partitions();
 
             Collection<Integer> lost = null;
 
@@ -1398,7 +1416,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             boolean changed = false;
 
             if (lost != null) {
-                PartitionLossPolicy plc = cctx.config().getPartitionLossPolicy();
+                PartitionLossPolicy plc = grp.config().getPartitionLossPolicy();
 
                 assert plc != null;
 
@@ -1430,13 +1448,17 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                         }
                     }
 
-                    if (cctx.events().isRecordable(EventType.EVT_CACHE_REBALANCE_PART_DATA_LOST))
-                        cctx.events().addPreloadEvent(part, EVT_CACHE_REBALANCE_PART_DATA_LOST,
-                            discoEvt.eventNode(), discoEvt.type(), discoEvt.timestamp());
+                    if (grp.eventRecordable(EventType.EVT_CACHE_REBALANCE_PART_DATA_LOST)) {
+                        grp.addRebalanceEvent(part,
+                            EVT_CACHE_REBALANCE_PART_DATA_LOST,
+                            discoEvt.eventNode(),
+                            discoEvt.type(),
+                            discoEvt.timestamp());
+                    }
                 }
 
                 if (plc != PartitionLossPolicy.IGNORE)
-                    cctx.needsRecovery(true);
+                    grp.needsRecovery(true);
             }
 
             return changed;
@@ -1451,7 +1473,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         lock.writeLock().lock();
 
         try {
-            int parts = cctx.affinity().partitions();
+            int parts = grp.affinity().partitions();
             long updSeq = updateSeq.incrementAndGet();
 
             for (int part = 0; part < parts; part++) {
@@ -1490,9 +1512,9 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 }
             }
 
-            checkEvictions(updSeq, cctx.affinity().assignments(topVer));
+            checkEvictions(updSeq, grp.affinity().assignments(topVer));
 
-            cctx.needsRecovery(false);
+            grp.needsRecovery(false);
         }
         finally {
             lock.writeLock().unlock();
@@ -1506,7 +1528,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         try {
             Collection<Integer> res = null;
 
-            int parts = cctx.affinity().partitions();
+            int parts = grp.affinity().partitions();
 
             for (int part = 0; part < parts; part++) {
                 Set<UUID> nodeIds = part2node.get(part);
@@ -1544,7 +1566,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             GridDhtLocalPartition locPart = locParts.get(p);
 
             if (locPart != null) {
-                if (locPart.state() == OWNING && !owners.contains(cctx.localNodeId())) {
+                if (locPart.state() == OWNING && !owners.contains(ctx.localNodeId())) {
                     if (haveHistory)
                         locPart.moving();
                     else {
@@ -1552,7 +1574,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
                         locPart.reload(true);
 
-                        result.add(cctx.localNodeId());
+                        result.add(ctx.localNodeId());
                     }
 
                 }
@@ -1588,12 +1610,12 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
      * @return {@code True} if state changed.
      */
     private boolean checkEvictions(long updateSeq) {
-        AffinityTopologyVersion affVer = cctx.affinity().affinityTopologyVersion();
+        AffinityTopologyVersion affVer = grp.affinity().lastVersion();
 
         boolean changed = false;
 
         if (!affVer.equals(AffinityTopologyVersion.NONE) && affVer.compareTo(topVer) >= 0) {
-            List<List<ClusterNode>> aff = cctx.affinity().assignments(topVer);
+            List<List<ClusterNode>> aff = grp.affinity().assignments(topVer);
 
             changed = checkEvictions(updateSeq, aff);
 
@@ -1625,12 +1647,12 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
      * @return Checks if any of the local partitions need to be evicted.
      */
     private boolean checkEvictions(long updateSeq, List<List<ClusterNode>> aff) {
-        if (!cctx.kernalContext().state().active())
+        if (!ctx.kernalContext().state().active())
             return false;
 
         boolean changed = false;
 
-        UUID locId = cctx.nodeId();
+        UUID locId = ctx.localNodeId();
 
         for (int p = 0; p < locParts.length(); p++) {
             GridDhtLocalPartition part = locParts.get(p);
@@ -1643,7 +1665,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             if (state.active()) {
                 List<ClusterNode> affNodes = aff.get(p);
 
-                if (!affNodes.contains(cctx.localNode())) {
+                if (!affNodes.contains(ctx.localNode())) {
                     List<ClusterNode> nodes = nodes(p, topVer, OWNING);
                     Collection<UUID> nodeIds = F.nodeIds(nodes);
 
@@ -1710,10 +1732,10 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
     private long updateLocal(int p, GridDhtPartitionState state, long updateSeq) {
         ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache();
 
-        assert oldest != null || cctx.kernalContext().clientNode();
+        assert oldest != null || ctx.kernalContext().clientNode();
 
         // If this node became the oldest node.
-        if (cctx.localNode().equals(oldest)) {
+        if (ctx.localNode().equals(oldest)) {
             long seq = node2part.updateSequence();
 
             if (seq != updateSeq) {
@@ -1740,7 +1762,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         }
 
         if (node2part != null) {
-            UUID locNodeId = cctx.localNodeId();
+            UUID locNodeId = ctx.localNodeId();
 
         GridDhtPartitionMap map = node2part.get(locNodeId);
 
@@ -1777,9 +1799,9 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
         ClusterNode oldest = discoCache.oldestAliveServerNode();
 
-        assert oldest != null || cctx.kernalContext().clientNode();
+        assert oldest != null || ctx.kernalContext().clientNode();
 
-        ClusterNode loc = cctx.localNode();
+        ClusterNode loc = ctx.localNode();
 
         if (node2part != null) {
             if (loc.equals(oldest) && !node2part.nodeId().equals(loc.id())) {
@@ -1927,11 +1949,10 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 
         try {
             assert node2part != null && node2part.valid() : "Invalid node2part [node2part: " + node2part +
-                ", cache=" + cctx.name() +
-                ", started=" + cctx.started() +
+                ", grp=" + grp.cacheOrGroupName() +
                 ", stopping=" + stopping +
-                ", locNodeId=" + cctx.localNode().id() +
-                ", locName=" + cctx.igniteInstanceName() + ']';
+                ", locNodeId=" + ctx.localNodeId() +
+                ", locName=" + ctx.igniteInstanceName() + ']';
 
             for (GridDhtPartitionMap map : node2part.values()) {
                 if (map.hasMovingPartitions())
@@ -1945,10 +1966,25 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
         }
     }
 
+    /**
+     * @param cacheId Cache ID.
+     */
+    public void onCacheStopped(int cacheId) {
+        if (!grp.sharedGroup())
+            return;
+
+        for (int i = 0; i < locParts.length(); i++) {
+            GridDhtLocalPartition part = locParts.get(i);
+
+            if (part != null)
+                part.onCacheStopped(cacheId);
+        }
+    }
+
     /** {@inheritDoc} */
     @Override public void printMemoryStats(int threshold) {
-        X.println(">>>  Cache partition topology stats [igniteInstanceName=" + cctx.igniteInstanceName() +
-            ", cache=" + cctx.name() + ']');
+        X.println(">>>  Cache partition topology stats [igniteInstanceName=" + ctx.igniteInstanceName() +
+            ", grp=" + grp.cacheOrGroupName() + ']');
 
         lock.readLock().lock();
 
@@ -1959,7 +1995,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
                 if (part == null)
                     continue;
 
-                int size = part.dataStore().size();
+                int size = part.dataStore().fullSize();
 
                 if (size >= threshold)
                     X.println(">>>   Local partition [part=" + part.id() + ", size=" + size + ']');
@@ -1976,7 +2012,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
      * @return {@code True} if given partition belongs to local node.
      */
     private boolean localNode(int part, List<List<ClusterNode>> aff) {
-        return aff.get(part).contains(cctx.localNode());
+        return aff.get(part).contains(ctx.localNode());
     }
 
     /**
@@ -1987,7 +2023,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             if (node2part == null || !node2part.valid())
                 return;
 
-            for (int i = 0; i < cctx.affinity().partitions(); i++) {
+            for (int i = 0; i < grp.affinity().partitions(); i++) {
                 List<ClusterNode> affNodes = aff.get(i);
 
                 // Topology doesn't contain server nodes (just clients).
@@ -2003,7 +2039,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
             rebalancedTopVer = topVer;
 
             if (log.isDebugEnabled())
-                log.debug("Updated rebalanced version [cache=" + cctx.name() + ", ver=" + rebalancedTopVer + ']');
+                log.debug("Updated rebalanced version [cache=" + grp.cacheOrGroupName() + ", ver=" + rebalancedTopVer + ']');
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
index c91eb7a..d607ff1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTransactionalCacheAdapter.java
@@ -47,7 +47,8 @@ import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheEntry;
 import org.apache.ignite.internal.processors.cache.distributed.GridDistributedLockCancelledException;
 import org.apache.ignite.internal.processors.cache.distributed.GridDistributedUnlockRequest;
-import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysResponse;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLockRequest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearLockResponse;
@@ -118,51 +119,61 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
     @Override public void start() throws IgniteCheckedException {
         super.start();
 
-        preldr = new GridDhtPreloader(ctx);
-
-        preldr.start();
-
-        ctx.io().addHandler(ctx.cacheId(), GridNearGetRequest.class, new CI2<UUID, GridNearGetRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearGetRequest.class, new CI2<UUID, GridNearGetRequest>() {
             @Override public void apply(UUID nodeId, GridNearGetRequest req) {
                 processNearGetRequest(nodeId, req);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearSingleGetRequest.class, new CI2<UUID, GridNearSingleGetRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearSingleGetRequest.class, new CI2<UUID, GridNearSingleGetRequest>() {
             @Override public void apply(UUID nodeId, GridNearSingleGetRequest req) {
                 processNearSingleGetRequest(nodeId, req);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearLockRequest.class, new CI2<UUID, GridNearLockRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearLockRequest.class, new CI2<UUID, GridNearLockRequest>() {
             @Override public void apply(UUID nodeId, GridNearLockRequest req) {
                 processNearLockRequest(nodeId, req);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridDhtLockRequest.class, new CI2<UUID, GridDhtLockRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridDhtLockRequest.class, new CI2<UUID, GridDhtLockRequest>() {
             @Override public void apply(UUID nodeId, GridDhtLockRequest req) {
                 processDhtLockRequest(nodeId, req);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridDhtLockResponse.class, new CI2<UUID, GridDhtLockResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridDhtLockResponse.class, new CI2<UUID, GridDhtLockResponse>() {
             @Override public void apply(UUID nodeId, GridDhtLockResponse req) {
                 processDhtLockResponse(nodeId, req);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearUnlockRequest.class, new CI2<UUID, GridNearUnlockRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearUnlockRequest.class, new CI2<UUID, GridNearUnlockRequest>() {
             @Override public void apply(UUID nodeId, GridNearUnlockRequest req) {
                 processNearUnlockRequest(nodeId, req);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridDhtUnlockRequest.class, new CI2<UUID, GridDhtUnlockRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridDhtUnlockRequest.class, new CI2<UUID, GridDhtUnlockRequest>() {
             @Override public void apply(UUID nodeId, GridDhtUnlockRequest req) {
                 processDhtUnlockRequest(nodeId, req);
             }
         });
+
+        ctx.io().addCacheHandler(ctx.cacheId(), GridDhtForceKeysRequest.class,
+            new MessageHandler<GridDhtForceKeysRequest>() {
+                @Override public void onMessage(ClusterNode node, GridDhtForceKeysRequest msg) {
+                    processForceKeysRequest(node, msg);
+                }
+            });
+
+        ctx.io().addCacheHandler(ctx.cacheId(), GridDhtForceKeysResponse.class,
+            new MessageHandler<GridDhtForceKeysResponse>() {
+                @Override public void onMessage(ClusterNode node, GridDhtForceKeysResponse msg) {
+                    processForceKeyResponse(node, msg);
+                }
+            });
     }
 
     /** {@inheritDoc} */
@@ -382,7 +393,7 @@ public abstract class GridDhtTransactionalCacheAdapter<K, V> extends GridDhtCach
         }
 
         IgniteInternalFuture<Object> keyFut = F.isEmpty(req.keys()) ? null :
-            ctx.dht().dhtPreloader().request(req.keys(), req.topologyVersion());
+            ctx.group().preloader().request(ctx, req.keys(), req.topologyVersion());
 
         if (keyFut == null || keyFut.isDone()) {
             if (keyFut != null) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishResponse.java
index d777a22..6d717eb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishResponse.java
@@ -173,19 +173,19 @@ public class GridDhtTxFinishResponse extends GridDistributedTxFinishResponse {
         }
 
         switch (writer.state()) {
-            case 7:
+            case 6:
                 if (!writer.writeByteArray("checkCommittedErrBytes", checkCommittedErrBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 8:
+            case 7:
                 if (!writer.writeInt("miniId", miniId))
                     return false;
 
                 writer.incrementState();
 
-            case 9:
+            case 8:
                 if (!writer.writeMessage("retVal", retVal))
                     return false;
 
@@ -207,7 +207,7 @@ public class GridDhtTxFinishResponse extends GridDistributedTxFinishResponse {
             return false;
 
         switch (reader.state()) {
-            case 7:
+            case 6:
                 checkCommittedErrBytes = reader.readByteArray("checkCommittedErrBytes");
 
                 if (!reader.isLastRead())
@@ -215,7 +215,7 @@ public class GridDhtTxFinishResponse extends GridDistributedTxFinishResponse {
 
                 reader.incrementState();
 
-            case 8:
+            case 7:
                 miniId = reader.readInt("miniId");
 
                 if (!reader.isLastRead())
@@ -223,7 +223,7 @@ public class GridDhtTxFinishResponse extends GridDistributedTxFinishResponse {
 
                 reader.incrementState();
 
-            case 9:
+            case 8:
                 retVal = reader.readMessage("retVal");
 
                 if (!reader.isLastRead())
@@ -243,7 +243,7 @@ public class GridDhtTxFinishResponse extends GridDistributedTxFinishResponse {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 10;
+        return 9;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxOnePhaseCommitAckRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxOnePhaseCommitAckRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxOnePhaseCommitAckRequest.java
index c483408..67eacd3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxOnePhaseCommitAckRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxOnePhaseCommitAckRequest.java
@@ -47,6 +47,16 @@ public class GridDhtTxOnePhaseCommitAckRequest extends GridCacheMessage {
         // No-op.
     }
 
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
     /**
      *
      * @param vers Near Tx xid Versions.
@@ -87,7 +97,7 @@ public class GridDhtTxOnePhaseCommitAckRequest extends GridCacheMessage {
         }
 
         switch (writer.state()) {
-            case 3:
+            case 2:
                 if (!writer.writeCollection("vers", vers, MessageCollectionItemType.MSG))
                     return false;
 
@@ -109,7 +119,7 @@ public class GridDhtTxOnePhaseCommitAckRequest extends GridCacheMessage {
             return false;
 
         switch (reader.state()) {
-            case 3:
+            case 2:
                 vers = reader.readCollection("vers", MessageCollectionItemType.MSG);
 
                 if (!reader.isLastRead())
@@ -129,6 +139,6 @@ public class GridDhtTxOnePhaseCommitAckRequest extends GridCacheMessage {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 4;
+        return 3;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
index e2b7803..bd238d9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
@@ -1086,7 +1086,9 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
 
             Collection<KeyCacheObject> keys = entry.getValue();
 
-            lastForceFut = cctx.cacheContext(cacheId).preloader().request(keys, tx.topologyVersion());
+            GridCacheContext ctx = cctx.cacheContext(cacheId);
+
+            lastForceFut = ctx.group().preloader().request(ctx, keys, tx.topologyVersion());
 
             if (compFut != null && lastForceFut != null)
                 compFut.add(lastForceFut);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
index 6452abc..f29c507 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedGetFuture.java
@@ -458,7 +458,7 @@ public class GridPartitionedGetFuture<K, V> extends CacheDistributedGetFutureAda
                 GridCacheVersion ver = null;
 
                 if (readNoEntry) {
-                    CacheDataRow row = cctx.offheap().read(key);
+                    CacheDataRow row = cctx.offheap().read(cctx, key);
 
                     if (row != null) {
                         long expireTime = row.expireTime();

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
index e5df64e..9811f8d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridPartitionedSingleGetFuture.java
@@ -359,7 +359,7 @@ public class GridPartitionedSingleGetFuture extends GridCacheFutureAdapter<Objec
                 boolean skipEntry = readNoEntry;
 
                 if (readNoEntry) {
-                    CacheDataRow row = cctx.offheap().read(key);
+                    CacheDataRow row = cctx.offheap().read(cctx, key);
 
                     if (row != null) {
                         long expireTime = row.expireTime();

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
index 579796d..d2dc817 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
@@ -27,7 +27,7 @@ import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -40,7 +40,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  *
  */
-public abstract class GridDhtAtomicAbstractUpdateRequest extends GridCacheMessage implements GridCacheDeployable {
+public abstract class GridDhtAtomicAbstractUpdateRequest extends GridCacheIdMessage implements GridCacheDeployable {
     /** Skip store flag bit mask. */
     private static final int DHT_ATOMIC_SKIP_STORE_FLAG_MASK = 0x01;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index 0dafa2b..2f99033 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -70,7 +70,8 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvali
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedGetFuture;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridPartitionedSingleGetFuture;
-import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysResponse;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearAtomicCache;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
@@ -193,19 +194,6 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
     }
 
     /** {@inheritDoc} */
-    @Override protected GridCacheMapEntryFactory entryFactory() {
-        return new GridCacheMapEntryFactory() {
-            @Override public GridCacheMapEntry create(
-                GridCacheContext ctx,
-                AffinityTopologyVersion topVer,
-                KeyCacheObject key
-            ) {
-                return new GridDhtAtomicCacheEntry(ctx, topVer, key);
-            }
-        };
-    }
-
-    /** {@inheritDoc} */
     @Override protected void init() {
         super.init();
 
@@ -239,11 +227,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
 
         metrics = m;
 
-        preldr = new GridDhtPreloader(ctx);
-
-        preldr.start();
-
-        ctx.io().addHandler(
+        ctx.io().addCacheHandler(
             ctx.cacheId(),
             GridNearGetRequest.class,
             new CI2<UUID, GridNearGetRequest>() {
@@ -257,7 +241,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(
+        ctx.io().addCacheHandler(
             ctx.cacheId(),
             GridNearSingleGetRequest.class,
             new CI2<UUID, GridNearSingleGetRequest>() {
@@ -271,7 +255,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(
+        ctx.io().addCacheHandler(
             ctx.cacheId(),
             GridNearAtomicAbstractUpdateRequest.class,
             new CI2<UUID, GridNearAtomicAbstractUpdateRequest>() {
@@ -290,7 +274,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(ctx.cacheId(),
+        ctx.io().addCacheHandler(
+            ctx.cacheId(),
             GridNearAtomicUpdateResponse.class,
             new CI2<UUID, GridNearAtomicUpdateResponse>() {
                 @Override public void apply(
@@ -308,7 +293,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(
+        ctx.io().addCacheHandler(
             ctx.cacheId(),
             GridDhtAtomicAbstractUpdateRequest.class,
             new CI2<UUID, GridDhtAtomicAbstractUpdateRequest>() {
@@ -327,7 +312,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(
+        ctx.io().addCacheHandler(
             ctx.cacheId(),
             GridDhtAtomicUpdateResponse.class,
             new CI2<UUID, GridDhtAtomicUpdateResponse>() {
@@ -346,7 +331,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(ctx.cacheId(),
+        ctx.io().addCacheHandler(
+            ctx.cacheId(),
             GridDhtAtomicDeferredUpdateResponse.class,
             new CI2<UUID, GridDhtAtomicDeferredUpdateResponse>() {
                 @Override public void apply(
@@ -364,7 +350,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
-        ctx.io().addHandler(ctx.cacheId(),
+        ctx.io().addCacheHandler(
+            ctx.cacheId(),
             GridDhtAtomicNearResponse.class,
             new CI2<UUID, GridDhtAtomicNearResponse>() {
             @Override public void apply(UUID uuid, GridDhtAtomicNearResponse msg) {
@@ -377,7 +364,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(),
+        ctx.io().addCacheHandler(
+            ctx.cacheId(),
             GridNearAtomicCheckUpdateRequest.class,
             new CI2<UUID, GridNearAtomicCheckUpdateRequest>() {
                 @Override public void apply(UUID uuid, GridNearAtomicCheckUpdateRequest msg) {
@@ -390,8 +378,26 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 }
             });
 
+        ctx.io().addCacheHandler(
+            ctx.cacheId(),
+            GridDhtForceKeysRequest.class,
+            new MessageHandler<GridDhtForceKeysRequest>() {
+                @Override public void onMessage(ClusterNode node, GridDhtForceKeysRequest msg) {
+                    processForceKeysRequest(node, msg);
+                }
+            });
+
+        ctx.io().addCacheHandler(
+            ctx.cacheId(),
+            GridDhtForceKeysResponse.class,
+            new MessageHandler<GridDhtForceKeysResponse>() {
+                @Override public void onMessage(ClusterNode node, GridDhtForceKeysResponse msg) {
+                    processForceKeyResponse(node, msg);
+                }
+            });
+
         if (near == null) {
-            ctx.io().addHandler(
+            ctx.io().addCacheHandler(
                 ctx.cacheId(),
                 GridNearGetResponse.class,
                 new CI2<UUID, GridNearGetResponse>() {
@@ -405,7 +411,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                     }
                 });
 
-            ctx.io().addHandler(
+            ctx.io().addCacheHandler(
                 ctx.cacheId(),
                 GridNearSingleGetResponse.class,
                 new CI2<UUID, GridNearSingleGetResponse>() {
@@ -1486,7 +1492,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                 // Optimistically expect that all keys are available locally (avoid creation of get future).
                 for (KeyCacheObject key : keys) {
                     if (readNoEntry) {
-                        CacheDataRow row = ctx.offheap().read(key);
+                        CacheDataRow row = ctx.offheap().read(ctx, key);
 
                         if (row != null) {
                             long expireTime = row.expireTime();
@@ -1662,7 +1668,7 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
         final GridNearAtomicAbstractUpdateRequest req,
         final UpdateReplyClosure completionCb
     ) {
-        IgniteInternalFuture<Object> forceFut = preldr.request(req, req.topologyVersion());
+        IgniteInternalFuture<Object> forceFut = ctx.group().preloader().request(ctx, req, req.topologyVersion());
 
         if (forceFut == null || forceFut.isDone()) {
             try {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCacheEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCacheEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCacheEntry.java
deleted file mode 100644
index b0c9a64..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCacheEntry.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.distributed.dht.atomic;
-
-import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.KeyCacheObject;
-import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
-import org.apache.ignite.internal.util.typedef.internal.CU;
-import org.apache.ignite.internal.util.typedef.internal.S;
-
-/**
- * DHT atomic cache entry.
- */
-public class GridDhtAtomicCacheEntry extends GridDhtCacheEntry {
-    /**
-     * @param ctx Cache context.
-     * @param topVer Topology version at the time of creation (if negative, then latest topology is assumed).
-     * @param key Cache key.
-     */
-    GridDhtAtomicCacheEntry(
-        GridCacheContext ctx,
-        AffinityTopologyVersion topVer,
-        KeyCacheObject key
-    ) {
-        super(ctx, topVer, key);
-    }
-
-    /** {@inheritDoc} */
-    @Override protected String cacheName() {
-        return CU.isNearEnabled(cctx) ? super.cacheName() : cctx.dht().name();
-    }
-
-    /** {@inheritDoc} */
-    @Override public synchronized String toString() {
-        return S.toString(GridDhtAtomicCacheEntry.class, this, super.toString());
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicDeferredUpdateResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicDeferredUpdateResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicDeferredUpdateResponse.java
index 92ef149..0c069da 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicDeferredUpdateResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicDeferredUpdateResponse.java
@@ -22,7 +22,7 @@ import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutObject;
 import org.apache.ignite.internal.util.GridLongList;
@@ -35,7 +35,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Deferred dht atomic update response.
  */
-public class GridDhtAtomicDeferredUpdateResponse extends GridCacheMessage implements GridCacheDeployable {
+public class GridDhtAtomicDeferredUpdateResponse extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicNearResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicNearResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicNearResponse.java
index d6e2db0..71d2321 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicNearResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicNearResponse.java
@@ -20,7 +20,7 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.atomic;
 import java.nio.ByteBuffer;
 import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheReturn;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -36,7 +36,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.atomic
 /**
  * Message sent from DHT nodes to near node in FULL_SYNC mode.
  */
-public class GridDhtAtomicNearResponse extends GridCacheMessage {
+public class GridDhtAtomicNearResponse extends GridCacheIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateResponse.java
index 693d658..7b2547a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateResponse.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.atomic;
 
 import java.io.Externalizable;
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import org.apache.ignite.IgniteCheckedException;
@@ -27,7 +26,7 @@ import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.GridDirectCollection;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -39,7 +38,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * DHT atomic cache backup update response.
  */
-public class GridDhtAtomicUpdateResponse extends GridCacheMessage implements GridCacheDeployable {
+public class GridDhtAtomicUpdateResponse extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateRequest.java
index 4b3ea5bc..bb47af4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicAbstractUpdateRequest.java
@@ -29,7 +29,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryPredicate;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheOperation;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -44,7 +44,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  *
  */
-public abstract class GridNearAtomicAbstractUpdateRequest extends GridCacheMessage implements GridCacheDeployable {
+public abstract class GridNearAtomicAbstractUpdateRequest extends GridCacheIdMessage implements GridCacheDeployable {
     /** Message index. */
     public static final int CACHE_MSG_IDX = nextIndexId();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicCheckUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicCheckUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicCheckUpdateRequest.java
index 4b9109e..96be023 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicCheckUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicCheckUpdateRequest.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.atomic;
 
 import java.nio.ByteBuffer;
 import org.apache.ignite.internal.GridDirectTransient;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
 import org.apache.ignite.plugin.extensions.communication.MessageWriter;
@@ -27,7 +27,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  *
  */
-public class GridNearAtomicCheckUpdateRequest extends GridCacheMessage {
+public class GridNearAtomicCheckUpdateRequest extends GridCacheIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateResponse.java
index 55953ea..5ba024f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateResponse.java
@@ -30,7 +30,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheReturn;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
@@ -45,7 +45,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  * DHT atomic cache near update response.
  */
-public class GridNearAtomicUpdateResponse extends GridCacheMessage implements GridCacheDeployable {
+public class GridNearAtomicUpdateResponse extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
index 12a3912..708df49 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCache.java
@@ -38,7 +38,6 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
 import org.apache.ignite.internal.processors.cache.GridCacheLockTimeoutException;
-import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntryFactory;
 import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
 import org.apache.ignite.internal.processors.cache.GridCacheReturn;
@@ -116,35 +115,22 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
     }
 
     /** {@inheritDoc} */
-    @Override protected GridCacheMapEntryFactory entryFactory() {
-        return new GridCacheMapEntryFactory() {
-            @Override public GridCacheMapEntry create(
-                GridCacheContext ctx,
-                AffinityTopologyVersion topVer,
-                KeyCacheObject key
-            ) {
-                return new GridDhtColocatedCacheEntry(ctx, topVer, key);
-            }
-        };
-    }
-
-    /** {@inheritDoc} */
     @Override public void start() throws IgniteCheckedException {
         super.start();
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearGetResponse.class, new CI2<UUID, GridNearGetResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearGetResponse.class, new CI2<UUID, GridNearGetResponse>() {
             @Override public void apply(UUID nodeId, GridNearGetResponse res) {
                 processNearGetResponse(nodeId, res);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearSingleGetResponse.class, new CI2<UUID, GridNearSingleGetResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearSingleGetResponse.class, new CI2<UUID, GridNearSingleGetResponse>() {
             @Override public void apply(UUID nodeId, GridNearSingleGetResponse res) {
                 processNearSingleGetResponse(nodeId, res);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearLockResponse.class, new CI2<UUID, GridNearLockResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearLockResponse.class, new CI2<UUID, GridNearLockResponse>() {
             @Override public void apply(UUID nodeId, GridNearLockResponse res) {
                 processLockResponse(nodeId, res);
             }
@@ -467,7 +453,7 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
 
                 for (KeyCacheObject key : keys) {
                     if (readNoEntry) {
-                        CacheDataRow row = ctx.offheap().read(key);
+                        CacheDataRow row = ctx.offheap().read(ctx, key);
 
                         if (row != null) {
                             long expireTime = row.expireTime();
@@ -941,7 +927,7 @@ public class GridDhtColocatedCache<K, V> extends GridDhtTransactionalCacheAdapte
     ) {
         assert keys != null;
 
-        IgniteInternalFuture<Object> keyFut = ctx.dht().dhtPreloader().request(keys, topVer);
+        IgniteInternalFuture<Object> keyFut = ctx.group().preloader().request(cacheCtx, keys, topVer);
 
         // Prevent embedded future creation if possible.
         if (keyFut == null || keyFut.isDone()) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCacheEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCacheEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCacheEntry.java
deleted file mode 100644
index f7cc5a7..0000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/colocated/GridDhtColocatedCacheEntry.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.distributed.dht.colocated;
-
-import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.KeyCacheObject;
-import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
-import org.apache.ignite.internal.util.typedef.internal.S;
-
-/**
- * Cache entry for colocated cache.
- */
-public class GridDhtColocatedCacheEntry extends GridDhtCacheEntry {
-    /**
-     * @param ctx Cache context.
-     * @param topVer Topology version at the time of creation (if negative, then latest topology is assumed).
-     * @param key Cache key.
-     */
-    GridDhtColocatedCacheEntry(
-        GridCacheContext ctx,
-        AffinityTopologyVersion topVer,
-        KeyCacheObject key
-    ) {
-        super(ctx, topVer, key);
-    }
-
-    /** {@inheritDoc} */
-    @Override protected String cacheName() {
-        return cctx.colocated().name();
-    }
-
-    /** {@inheritDoc} */
-    @Override public synchronized String toString() {
-        return S.toString(GridDhtColocatedCacheEntry.class, this, super.toString());
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
index d72d8db..763b43b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysFuture.java
@@ -102,9 +102,6 @@ public final class GridDhtForceKeysFuture<K, V> extends GridCompoundFuture<Objec
     /** Future ID. */
     private IgniteUuid futId = IgniteUuid.randomUuid();
 
-    /** Preloader. */
-    private GridDhtPreloader preloader;
-
     /** Trackable flag. */
     private boolean trackable;
 
@@ -112,21 +109,19 @@ public final class GridDhtForceKeysFuture<K, V> extends GridCompoundFuture<Objec
      * @param cctx Cache context.
      * @param topVer Topology version.
      * @param keys Keys.
-     * @param preloader Preloader.
      */
     public GridDhtForceKeysFuture(
         GridCacheContext<K, V> cctx,
         AffinityTopologyVersion topVer,
-        Collection<KeyCacheObject> keys,
-        GridDhtPreloader preloader
+        Collection<KeyCacheObject> keys
     ) {
         assert topVer.topologyVersion() != 0 : topVer;
         assert !F.isEmpty(keys) : keys;
+        assert !cctx.isNear();
 
         this.cctx = cctx;
         this.keys = keys;
         this.topVer = topVer;
-        this.preloader = preloader;
 
         top = cctx.dht().topology();
 
@@ -158,7 +153,7 @@ public final class GridDhtForceKeysFuture<K, V> extends GridCompoundFuture<Objec
     @Override public boolean onDone(@Nullable Collection<K> res, @Nullable Throwable err) {
         if (super.onDone(res, err)) {
             if (trackable)
-                preloader.remoteFuture(this);
+                cctx.dht().removeFuture(this);
 
             return true;
         }
@@ -170,7 +165,7 @@ public final class GridDhtForceKeysFuture<K, V> extends GridCompoundFuture<Objec
      * @param evt Discovery event.
      */
     @SuppressWarnings( {"unchecked"})
-    void onDiscoveryEvent(DiscoveryEvent evt) {
+    public void onDiscoveryEvent(DiscoveryEvent evt) {
         topCntr.incrementAndGet();
 
         int type = evt.type();
@@ -244,7 +239,7 @@ public final class GridDhtForceKeysFuture<K, V> extends GridCompoundFuture<Objec
 
             int curTopVer = topCntr.get();
 
-            if (!preloader.addFuture(this)) {
+            if (!cctx.dht().addFuture(this)) {
                 assert isDone() : this;
 
                 return false;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysRequest.java
index d129ae8..124ae44 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysRequest.java
@@ -25,7 +25,7 @@ import org.apache.ignite.internal.GridDirectCollection;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -40,7 +40,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
  * Force keys request. This message is sent by node while preloading to force
  * another node to put given keys into the next batch of transmitting entries.
  */
-public class GridDhtForceKeysRequest extends GridCacheMessage implements GridCacheDeployable {
+public class GridDhtForceKeysRequest extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 


[05/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java
new file mode 100644
index 0000000..ec96e4d
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsTest.java
@@ -0,0 +1,3765 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.concurrent.locks.Lock;
+import javax.cache.Cache;
+import javax.cache.CacheException;
+import javax.cache.configuration.Factory;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryUpdatedListener;
+import javax.cache.integration.CacheLoaderException;
+import javax.cache.integration.CacheWriterException;
+import javax.cache.processor.EntryProcessorException;
+import javax.cache.processor.MutableEntry;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheEntryProcessor;
+import org.apache.ignite.cache.CacheExistsException;
+import org.apache.ignite.cache.CacheInterceptor;
+import org.apache.ignite.cache.CacheInterceptorAdapter;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.affinity.AffinityKeyMapper;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.cache.query.ContinuousQuery;
+import org.apache.ignite.cache.query.QueryCursor;
+import org.apache.ignite.cache.query.ScanQuery;
+import org.apache.ignite.cache.store.CacheStore;
+import org.apache.ignite.cache.store.CacheStoreAdapter;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.binary.BinaryMarshaller;
+import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
+import org.apache.ignite.internal.processors.platform.cache.expiry.PlatformExpiryPolicyFactory;
+import org.apache.ignite.internal.util.lang.GridAbsPredicate;
+import org.apache.ignite.internal.util.lang.GridIterator;
+import org.apache.ignite.internal.util.lang.GridPlainCallable;
+import org.apache.ignite.internal.util.typedef.PA;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiPredicate;
+import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+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.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.transactions.TransactionConcurrency.OPTIMISTIC;
+import static org.apache.ignite.transactions.TransactionConcurrency.PESSIMISTIC;
+import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ;
+import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE;
+
+/**
+ *
+ */
+@SuppressWarnings("unchecked")
+public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String GROUP1 = "grp1";
+
+    /** */
+    private static final String GROUP2 = "grp2";
+
+    /** */
+    private static final String CACHE1 = "cache1";
+
+    /** */
+    private static final String CACHE2 = "cache2";
+
+    /** */
+    private boolean client;
+
+    /** */
+    private CacheConfiguration[] ccfgs;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
+
+        cfg.setClientMode(client);
+
+        if (ccfgs != null) {
+            cfg.setCacheConfiguration(ccfgs);
+
+            ccfgs = null;
+        }
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected long getTestTimeout() {
+        return 10 * 60_000;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        super.afterTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCloseCache1() throws Exception {
+        startGrid(0);
+
+        client = true;
+
+        Ignite client = startGrid(1);
+
+        IgniteCache c1 = client.createCache(cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 0, false));
+
+        checkCacheGroup(0, GROUP1, true);
+        checkCacheGroup(0, GROUP1, true);
+
+        checkCache(0, "c1", 10);
+        checkCache(1, "c1", 10);
+
+        c1.close();
+
+        checkCacheGroup(0, GROUP1, true);
+        checkCacheGroup(1, GROUP1, false);
+
+        checkCache(0, "c1", 10);
+
+        assertNotNull(client.cache("c1"));
+
+        checkCacheGroup(0, GROUP1, true);
+        checkCacheGroup(1, GROUP1, true);
+
+        checkCache(0, "c1", 10);
+        checkCache(1, "c1", 10);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDestroyCaches1() throws Exception {
+        createDestroyCaches(1);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDestroyCaches2() throws Exception {
+        createDestroyCaches(5);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateCacheWithSameNameInAnotherGroup() throws Exception {
+        startGridsMultiThreaded(2);
+
+        final Ignite ignite = ignite(0);
+
+        ignite.createCache(cacheConfiguration(GROUP1, CACHE1, PARTITIONED, ATOMIC, 2, false));
+
+        GridTestUtils.assertThrows(null, new GridPlainCallable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite(1).createCache(cacheConfiguration(GROUP2, CACHE1, PARTITIONED, ATOMIC, 2, false));
+                return null;
+            }
+        }, CacheExistsException.class, "a cache with the same name is already started");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDestroyCachesAtomicPartitioned() throws Exception {
+        createDestroyCaches(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDestroyCachesTxPartitioned() throws Exception {
+        createDestroyCaches(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDestroyCachesAtomicReplicated() throws Exception {
+        createDestroyCaches(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDestroyCachesTxReplicated() throws Exception {
+        createDestroyCaches(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryAtomicPartitioned() throws Exception {
+        scanQuery(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryTxPartitioned() throws Exception {
+        scanQuery(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryAtomicReplicated() throws Exception {
+        scanQuery(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryTxReplicated() throws Exception {
+        scanQuery(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryAtomicLocal() throws Exception {
+        scanQuery(LOCAL, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryTxLocal() throws Exception {
+        scanQuery(LOCAL, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntriesTtlAtomicPartitioned() throws Exception {
+        entriesTtl(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntriesTtlTxPartitioned() throws Exception {
+        entriesTtl(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntriesTtlAtomicReplicated() throws Exception {
+        entriesTtl(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntriesTtlTxReplicated() throws Exception {
+        entriesTtl(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntriesTtlAtomicLocal() throws Exception {
+        entriesTtl(LOCAL, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testEntriesTtlTxLocal() throws Exception {
+        entriesTtl(LOCAL, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIteratorAtomicPartitioned() throws Exception {
+        cacheIterator(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIteratorTxPartitioned() throws Exception {
+        cacheIterator(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIteratorAtomicReplicated() throws Exception {
+        cacheIterator(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIteratorTxReplicated() throws Exception {
+        cacheIterator(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIteratorAtomicLocal() throws Exception {
+        cacheIterator(LOCAL, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIteratorTxLocal() throws Exception {
+        cacheIterator(LOCAL, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryMultiplePartitionsAtomicPartitioned() throws Exception {
+        scanQueryMultiplePartitions(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryMultiplePartitionsTxPartitioned() throws Exception {
+        scanQueryMultiplePartitions(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryMultiplePartitionsAtomicReplicated() throws Exception {
+        scanQueryMultiplePartitions(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testScanQueryMultiplePartitionsTxReplicated() throws Exception {
+        scanQueryMultiplePartitions(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContinuousQueryTxReplicated() throws Exception {
+        continuousQuery(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContinuousQueryTxPartitioned() throws Exception {
+        continuousQuery(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContinuousQueryTxLocal() throws Exception {
+        continuousQuery(LOCAL, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContinuousQueryAtomicReplicated() throws Exception {
+        continuousQuery(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContinuousQueryAtomicPartitioned() throws Exception {
+        continuousQuery(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testContinuousQueryAtomicLocal() throws Exception {
+        continuousQuery(LOCAL, ATOMIC);
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Cache atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void scanQuery(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        int keys = 10_000;
+
+        Integer[] data1 = generateData(keys);
+        Integer[] data2 = generateData(keys);
+
+        boolean loc = cacheMode == LOCAL;
+
+        if (loc)
+            startGrid(0);
+        else
+            startGridsMultiThreaded(4);
+
+        Ignite srv0 = ignite(0);
+
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE1, cacheMode, atomicityMode, 2, false));
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE2, cacheMode, atomicityMode, 2, false));
+
+        IgniteCache<Integer, Integer> cache1;
+        IgniteCache<Integer, Integer> cache2;
+
+        if (atomicityMode == TRANSACTIONAL) {
+            Ignite ignite = ignite(loc ? 0 : 1);
+
+            cache1 = ignite.cache(CACHE1);
+            cache2 = ignite.cache(CACHE2);
+
+            try (Transaction tx = ignite.transactions().txStart()) {
+                for (int i = 0; i < keys ; i++) {
+                    cache1.put(i, data1[i]);
+                    cache2.put(i, data2[i]);
+                }
+
+                tx.commit();
+            }
+        }
+        else {
+            // Async put ops.
+            int ldrs = 4;
+
+            List<Callable<?>> cls = new ArrayList<>(ldrs * 2);
+
+            for (int i = 0; i < ldrs ; i++) {
+                cls.add(putOperation(loc ? 0 : 1, ldrs, i, CACHE1, data1));
+                cls.add(putOperation(loc ? 0 : 2, ldrs, i, CACHE2, data2));
+            }
+
+            GridTestUtils.runMultiThreaded(cls, "loaders");
+        }
+
+        ScanQuery<Integer, Integer> qry = new ScanQuery<>();
+
+        Set<Integer> keysSet = sequence(keys);
+
+        for (Cache.Entry<Integer, Integer> entry : ignite(loc ? 0 : 3).cache(CACHE1).query(qry)) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data1[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+
+        srv0.destroyCache(CACHE1);
+
+        keysSet = sequence(keys);
+
+        for (Cache.Entry<Integer, Integer> entry : ignite(loc ? 0 : 3).cache(CACHE2).query(qry)) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data2[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Cache atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void continuousQuery(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        final int keys = 10_000;
+
+        Integer[] data1 = generateData(keys);
+        Integer[] data2 = generateData(keys);
+
+        boolean loc = cacheMode == LOCAL;
+
+        if (loc)
+            startGrid(0);
+        else
+            startGridsMultiThreaded(4);
+
+        Ignite srv0 = ignite(0);
+
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE1, cacheMode, atomicityMode, 2, false));
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE2, cacheMode, atomicityMode, 2, false));
+
+        final AtomicInteger cntr1 = new AtomicInteger();
+        final AtomicInteger cntr2 = new AtomicInteger();
+
+        CacheEntryUpdatedListener lsnr1 = new CacheEntryUpdatedListener<Integer, Integer>() {
+            @Override public void onUpdated(
+                Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> evts) {
+                for (CacheEntryEvent<? extends Integer, ? extends Integer> ignored : evts)
+                    cntr1.incrementAndGet();
+            }
+        };
+
+        CacheEntryUpdatedListener lsnr2 = new CacheEntryUpdatedListener<Integer, Integer>() {
+            @Override public void onUpdated(
+                Iterable<CacheEntryEvent<? extends Integer, ? extends Integer>> evts) {
+                for (CacheEntryEvent<? extends Integer, ? extends Integer> ignored : evts)
+                    cntr2.incrementAndGet();
+            }
+        };
+
+        QueryCursor qry1 = ignite(loc ? 0 : 2).cache(CACHE1).query(new ContinuousQuery<>().setLocalListener(lsnr1));
+        QueryCursor qry2 = ignite(loc ? 0 : 3).cache(CACHE2).query(new ContinuousQuery<>().setLocalListener(lsnr2));
+
+        if (atomicityMode == TRANSACTIONAL) {
+            Ignite ignite = ignite(loc ? 0 : 1);
+
+            IgniteCache<Integer, Integer> cache1 = ignite.cache(CACHE1);
+            IgniteCache<Integer, Integer> cache2 = ignite.cache(CACHE2);
+
+            try (Transaction tx = ignite.transactions().txStart()) {
+                for (int i = 0; i < keys ; i++) {
+                    cache1.put(i, data1[i]);
+                    cache2.put(i, data2[i]);
+                }
+
+                tx.commit();
+            }
+        }
+        else {
+            int ldrs = 4;
+
+            List<Callable<?>> cls = new ArrayList<>(ldrs * 2);
+
+            for (int i = 0; i < ldrs ; i++) {
+                cls.add(putOperation(loc ? 0 : 1, ldrs, i, CACHE1, data1));
+                cls.add(putOperation(loc ? 0 : 2, ldrs, i, CACHE2, data2));
+            }
+
+            GridTestUtils.runMultiThreaded(cls, "loaders");
+        }
+
+        GridTestUtils.waitForCondition(new PA() {
+            @Override public boolean apply() {
+                return cntr1.get() == keys && cntr2.get() == keys;
+            }
+        }, 2000);
+
+        assertEquals(cntr1.get(), keys);
+        assertEquals(cntr2.get(), keys);
+
+        qry1.close();
+
+        Map<Integer, Integer> map = generateDataMap(10);
+
+        srv0.cache(CACHE1).putAll(map);
+        srv0.cache(CACHE2).putAll(map);
+
+        GridTestUtils.waitForCondition(new PA() {
+            @Override public boolean apply() {
+                return cntr2.get() == keys + 10;
+            }
+        }, 2000);
+
+        assertEquals(keys + 10, cntr2.get());
+
+        assertEquals(keys, cntr1.get());
+
+        qry2.close();
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Cache atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void scanQueryMultiplePartitions(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        int keys = 10000;
+
+        Integer[] data1 = generateData(keys);
+        Integer[] data2 = generateData(keys);
+
+        startGridsMultiThreaded(4);
+
+        Ignite srv0 = ignite(0);
+
+        srv0.createCache(
+            cacheConfiguration(GROUP1, CACHE1, cacheMode, atomicityMode, 2, false)
+                .setAffinity(new RendezvousAffinityFunction().setPartitions(32)));
+        srv0.createCache(
+            cacheConfiguration(GROUP1, CACHE2, cacheMode, atomicityMode, 2, false)
+                .setAffinity(new RendezvousAffinityFunction().setPartitions(32)));
+
+        awaitPartitionMapExchange();
+
+        IgniteCache<Integer, Integer> cache1;
+        IgniteCache<Integer, Integer> cache2;
+
+        if (atomicityMode == TRANSACTIONAL) {
+            Ignite ignite = ignite(1);
+
+            cache1 = ignite.cache(CACHE1);
+            cache2 = ignite.cache(CACHE2);
+
+            try (Transaction tx = ignite.transactions().txStart()) {
+                for (int i = 0; i < keys ; i++) {
+                    cache1.put(i, data1[i]);
+                    cache2.put(i, data2[i]);
+                }
+
+                tx.commit();
+            }
+        }
+        else {
+            // Async put ops.
+            int ldrs = 4;
+
+            List<Callable<?>> cls = new ArrayList<>(ldrs * 2);
+
+            for (int i = 0; i < ldrs ; i++) {
+                cls.add(putOperation(1, ldrs, i, CACHE1, data1));
+                cls.add(putOperation(2, ldrs, i, CACHE2, data2));
+            }
+
+            GridTestUtils.runMultiThreaded(cls, "loaders");
+        }
+
+
+        int p = ThreadLocalRandom.current().nextInt(32);
+
+        ScanQuery<Integer, Integer> qry = new ScanQuery().setPartition(p);
+
+        Set<Integer> keysSet = new TreeSet<>();
+
+        cache1 = ignite(3).cache(CACHE1);
+
+        Affinity<Integer> aff = affinity(cache1);
+
+        for(int i = 0; i < keys; i++) {
+            if (aff.partition(i) == p) {
+                keysSet.add(i);
+            }
+        }
+
+        for (Cache.Entry<Integer, Integer> entry : cache1.query(qry)) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data1[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+
+        srv0.destroyCache(CACHE1);
+
+        keysSet = new TreeSet<>();
+
+        cache2 = ignite(3).cache(CACHE2);
+
+        aff = affinity(cache2);
+
+        for(int i = 0; i < keys; i++) {
+            if (aff.partition(i) == p) {
+                keysSet.add(i);
+            }
+        }
+
+        for (Cache.Entry<Integer, Integer> entry : cache2.query(qry)) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data2[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Cache atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void cacheIterator(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        int keys = 10000;
+
+        Integer[] data1 = generateData(keys);
+        Integer[] data2 = generateData(keys);
+
+        boolean local = cacheMode == LOCAL;
+
+        if (local)
+            startGrid(0);
+        else
+            startGridsMultiThreaded(4);
+
+        Ignite srv0 = ignite(0);
+
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE1, cacheMode, atomicityMode, 2, false));
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE2, cacheMode, atomicityMode, 2, false));
+
+        if(!local)
+            awaitPartitionMapExchange();
+
+        if (atomicityMode == TRANSACTIONAL) {
+            Ignite ignite = ignite(local ? 0 : 1);
+
+            IgniteCache cache1 = ignite.cache(CACHE1);
+            IgniteCache cache2 = ignite.cache(CACHE2);
+
+            try (Transaction tx = ignite.transactions().txStart()) {
+                for (int i = 0; i < keys ; i++) {
+                    cache1.put(i, data1[i]);
+                    cache2.put(i, data2[i]);
+                }
+
+                tx.commit();
+            }
+        }
+        else {
+            // Async put ops.
+            int ldrs = 4;
+
+            List<Callable<?>> cls = new ArrayList<>(ldrs * 2);
+
+            for (int i = 0; i < ldrs ; i++) {
+                cls.add(putOperation(local ? 0 : 1, ldrs, i, CACHE1, data1));
+                cls.add(putOperation(local ? 0 : 2, ldrs, i, CACHE2, data2));
+            }
+
+            GridTestUtils.runMultiThreaded(cls, "loaders");
+        }
+
+
+        Set<Integer> keysSet = sequence(keys);
+
+        for (Cache.Entry<Integer, Integer> entry : ignite(local ? 0 : 3).<Integer, Integer>cache(CACHE1)) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data1[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+
+        srv0.destroyCache(CACHE1);
+
+        keysSet = sequence(keys);
+
+        for (Cache.Entry<Integer, Integer> entry : ignite(local ? 0 : 3).<Integer, Integer>cache(CACHE2)) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data2[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    private void entriesTtl(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        int keys = 10000;
+
+        final int ttl = 10000;
+
+        Integer[] data1 = generateData(keys);
+        Integer[] data2 = generateData(keys);
+
+        boolean local = cacheMode == LOCAL;
+
+        if (local)
+            startGrid(0);
+        else
+            startGridsMultiThreaded(4);
+
+
+        Ignite srv0 = ignite(0);
+
+        srv0.createCache(
+            cacheConfiguration(GROUP1, CACHE1, cacheMode, atomicityMode, 2, false)
+                // -1 = ETERNAL       just created entries are not expiring
+                // -2 = NOT_CHANGED   not to change ttl on entry update
+                .setExpiryPolicyFactory(new PlatformExpiryPolicyFactory(-1, -2, ttl)).setEagerTtl(true)
+        );
+
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE2, cacheMode, atomicityMode, 2, false));
+
+        if (!local)
+            awaitPartitionMapExchange();
+
+        if (atomicityMode == TRANSACTIONAL) {
+            Ignite ignite = ignite(local ? 0 : 1);
+
+            IgniteCache cache1 = ignite.cache(CACHE1);
+            IgniteCache cache2 = ignite.cache(CACHE2);
+
+            try (Transaction tx = ignite.transactions().txStart()) {
+                for (int i = 0; i < keys ; i++) {
+                    cache1.put(i, data1[i]);
+                    cache2.put(i, data2[i]);
+                }
+
+                tx.commit();
+            }
+        }
+        else {
+            // async put ops
+            int ldrs = 4;
+
+            List<Callable<?>> cls = new ArrayList<>(ldrs * 2);
+
+            for (int i = 0; i < ldrs ; i++) {
+                cls.add(putOperation(local ? 0 : 1, ldrs, i, CACHE1, data1));
+                cls.add(putOperation(local ? 0 : 2, ldrs, i, CACHE2, data2));
+            }
+
+            GridTestUtils.runMultiThreaded(cls, "loaders");
+        }
+
+        checkData(local ? 0 : 3, CACHE1, data1);
+        checkData(local ? 0 : 3, CACHE2, data2);
+
+        srv0.destroyCache(CACHE2);
+
+        checkData(local ? 0 : 3, CACHE1, data1);
+
+        // Wait for expiration
+
+        Thread.sleep((long)(ttl * 1.2));
+
+        assertEquals(0, ignite(local ? 0 : 3).cache(CACHE1).size());
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Cache atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void createDestroyCaches(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        int keys = 10000;
+
+        Integer[] data1 = generateData(keys);
+        Integer[] data2 = generateData(keys);
+
+        startGridsMultiThreaded(4);
+
+        Ignite srv0 = ignite(0);
+
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE1, cacheMode, atomicityMode, 2, false));
+        srv0.createCache(cacheConfiguration(GROUP1, CACHE2, cacheMode, atomicityMode, 2, false));
+
+        awaitPartitionMapExchange();
+
+        if (atomicityMode == TRANSACTIONAL) {
+            Ignite ignite = ignite(1);
+
+            IgniteCache cache1 = ignite.cache(CACHE1);
+            IgniteCache cache2 = ignite.cache(CACHE2);
+
+            try (Transaction tx = ignite.transactions().txStart()) {
+                for (int i = 0; i < keys ; i++) {
+                    cache1.put(i, data1[i]);
+                    cache2.put(i, data2[i]);
+                }
+
+                tx.commit();
+            }
+        }
+        else {
+            int ldrs = 4;
+
+            List<Callable<?>> cls = new ArrayList<>(ldrs * 2);
+
+            for (int i = 0; i < ldrs ; i++) {
+                cls.add(putOperation(1, ldrs, i, CACHE1, data1));
+                cls.add(putOperation(2, ldrs, i, CACHE2, data2));
+            }
+
+            GridTestUtils.runMultiThreaded(cls, "loaders");
+        }
+
+        checkLocalData(3, CACHE1, data1);
+        checkLocalData(0, CACHE2, data2);
+
+        checkData(0, CACHE1, data1);
+        checkData(3, CACHE2, data2);
+
+        ignite(1).destroyCache(CACHE2);
+
+        startGrid(5);
+
+        awaitPartitionMapExchange();
+
+        checkData(5, CACHE1, data1);
+        checkLocalData(5, CACHE1, data1);
+
+        ignite(1).destroyCache(CACHE1);
+
+        checkCacheGroup(5, GROUP1, false);
+    }
+
+    /**
+     * @param idx Node index.
+     * @param ldrs Loaders count.
+     * @param ldrIdx Loader index.
+     * @param cacheName Cache name.
+     * @param data Data.
+     * @return Callable for put operation.
+     */
+    private Callable<Void> putOperation(
+            final int idx,
+            final int ldrs,
+            final int ldrIdx,
+            final String cacheName,
+            final Integer[] data) {
+        return new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                IgniteCache cache = ignite(idx).cache(cacheName);
+
+                for (int j = 0, size = data.length; j < size ; j++) {
+                    if (j % ldrs == ldrIdx)
+                        cache.put(j, data[j]);
+                }
+
+                return null;
+            }
+        };
+    }
+
+    /**
+     * Creates an array of random integers.
+     *
+     * @param cnt Array length.
+     * @return Array of random integers.
+     */
+    private Integer[] generateData(int cnt) {
+        Random rnd = ThreadLocalRandom.current();
+
+        Integer[] data = new Integer[cnt];
+
+        for (int i = 0; i < data.length; i++)
+            data[i] = rnd.nextInt();
+
+        return data;
+    }
+
+    /**
+     * Creates a map with random integers.
+     *
+     * @param cnt Map size length.
+     * @return Map with random integers.
+     */
+    private Map<Integer, Integer> generateDataMap(int cnt) {
+        return generateDataMap(0, cnt);
+    }
+
+    /**
+     * Creates a map with random integers.
+     *
+     * @param startKey Start key.
+     * @param cnt Map size length.
+     * @return Map with random integers.
+     */
+    private Map<Integer, Integer> generateDataMap(int startKey, int cnt) {
+        Random rnd = ThreadLocalRandom.current();
+
+        Map<Integer, Integer> data = U.newHashMap(cnt);
+
+        for (int i = 0; i < cnt; i++)
+            data.put(startKey++, rnd.nextInt());
+
+        return data;
+    }
+
+    /**
+     * @param cnt Sequence length.
+     * @return Sequence of integers.
+     */
+    private Set<Integer> sequence(int cnt) {
+        Set<Integer> res = new TreeSet<>();
+
+        for (int i = 0; i < cnt; i++)
+            res.add(i);
+
+        return res;
+    }
+
+    /**
+     * @param idx Node index.
+     * @param cacheName Cache name.
+     * @param data Expected data.
+     * @throws Exception If failed.
+     */
+    private void checkData(int idx, String cacheName, Integer[] data) throws Exception {
+        Set<Integer> keys = sequence(data.length);
+
+        Set<Map.Entry<Integer, Integer>> entries =
+            ignite(idx).<Integer, Integer>cache(cacheName).getAll(keys).entrySet();
+
+        for (Map.Entry<Integer, Integer> entry : entries) {
+            assertTrue(keys.remove(entry.getKey()));
+            assertEquals(data[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keys.isEmpty());
+    }
+
+    /**
+     * @param idx Node index.
+     * @param cacheName Cache name.
+     * @param data Expected data.
+     * @throws Exception If failed.
+     */
+    private void checkLocalData(int idx, String cacheName, Integer[] data) throws Exception {
+        Ignite ignite = ignite(idx);
+        ClusterNode node = ignite.cluster().localNode();
+        IgniteCache cache = ignite.<Integer, Integer>cache(cacheName);
+
+        Affinity aff = affinity(cache);
+
+        Set<Integer> localKeys = new TreeSet<>();
+
+        for (int key = 0; key < data.length; key++) {
+            if(aff.isPrimaryOrBackup(node, key))
+                localKeys.add(key);
+        }
+
+        Iterable<Cache.Entry<Integer, Integer>> localEntries = cache.localEntries(CachePeekMode.OFFHEAP);
+
+        for (Cache.Entry<Integer, Integer> entry : localEntries) {
+            assertTrue(localKeys.remove(entry.getKey()));
+            assertEquals(data[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(localKeys.isEmpty());
+    }
+
+    /**
+     * @param srvs Number of server nodes.
+     * @throws Exception If failed.
+     */
+    private void createDestroyCaches(int srvs) throws Exception {
+        startGridsMultiThreaded(srvs);
+
+        checkCacheDiscoveryDataConsistent();
+
+        Ignite srv0 = ignite(0);
+
+        for (int i = 0; i < srvs; i++)
+            checkCacheGroup(i, GROUP1, false);
+
+        for (int iter = 0; iter < 3; iter++) {
+            log.info("Iteration: " + iter);
+
+            srv0.createCache(cacheConfiguration(GROUP1, CACHE1, PARTITIONED, ATOMIC, 2, false));
+
+            checkCacheDiscoveryDataConsistent();
+
+            for (int i = 0; i < srvs; i++) {
+                checkCacheGroup(i, GROUP1, true);
+
+                checkCache(i, CACHE1, 10);
+            }
+
+            srv0.createCache(cacheConfiguration(GROUP1, CACHE2, PARTITIONED, ATOMIC, 2, false));
+
+            checkCacheDiscoveryDataConsistent();
+
+            for (int i = 0; i < srvs; i++) {
+                checkCacheGroup(i, GROUP1, true);
+
+                checkCache(i, CACHE2, 10);
+            }
+
+            srv0.destroyCache(CACHE1);
+
+            checkCacheDiscoveryDataConsistent();
+
+            for (int i = 0; i < srvs; i++) {
+                checkCacheGroup(i, GROUP1, true);
+
+                checkCache(i, CACHE2, 10);
+            }
+
+            srv0.destroyCache(CACHE2);
+
+            checkCacheDiscoveryDataConsistent();
+
+            for (int i = 0; i < srvs; i++)
+                checkCacheGroup(i, GROUP1, false);
+        }
+    }
+
+    /**
+     * @param idx Node index.
+     * @param cacheName Cache name.
+     * @param ops Number of operations to execute.
+     */
+    private void checkCache(int idx, String cacheName, int ops) {
+        IgniteCache cache = ignite(idx).cache(cacheName);
+
+        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+        for (int i = 0; i < ops; i++) {
+            Integer key = rnd.nextInt();
+
+            cache.put(key, i);
+
+            assertEquals(i, cache.get(key));
+        }
+    }
+
+    /**
+     * @param cache3 {@code True} if add last cache.
+     * @return Cache configurations.
+     */
+    private CacheConfiguration[] staticConfigurations1(boolean cache3) {
+        CacheConfiguration[] ccfgs = new CacheConfiguration[cache3 ? 3 : 2];
+
+        ccfgs[0] = cacheConfiguration(null, "cache1", PARTITIONED, ATOMIC, 2, false);
+        ccfgs[1] = cacheConfiguration(GROUP1, "cache2", PARTITIONED, ATOMIC, 2, false);
+
+        if (cache3)
+            ccfgs[2] = cacheConfiguration(GROUP1, "cache3", PARTITIONED, ATOMIC, 2, false);
+
+        return ccfgs;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testDiscoveryDataConsistency1() throws Exception {
+        ccfgs = staticConfigurations1(true);
+        Ignite srv0 = startGrid(0);
+
+        ccfgs = staticConfigurations1(true);
+        startGrid(1);
+
+        checkCacheDiscoveryDataConsistent();
+
+        ccfgs = null;
+        startGrid(2);
+
+        checkCacheDiscoveryDataConsistent();
+
+        srv0.createCache(cacheConfiguration(null, "cache4", PARTITIONED, ATOMIC, 2, false));
+
+        checkCacheDiscoveryDataConsistent();
+
+        ccfgs = staticConfigurations1(true);
+        startGrid(3);
+
+        checkCacheDiscoveryDataConsistent();
+
+        srv0.createCache(cacheConfiguration(GROUP1, "cache5", PARTITIONED, ATOMIC, 2, false));
+
+        ccfgs = staticConfigurations1(true);
+        startGrid(4);
+
+        checkCacheDiscoveryDataConsistent();
+
+        for (int i = 0; i < 5; i++)
+            checkCacheGroup(i, GROUP1, true);
+
+        srv0.destroyCache("cache1");
+        srv0.destroyCache("cache2");
+        srv0.destroyCache("cache3");
+
+        checkCacheDiscoveryDataConsistent();
+
+        ccfgs = staticConfigurations1(true);
+        startGrid(5);
+
+        checkCacheDiscoveryDataConsistent();
+
+        for (int i = 0; i < 6; i++)
+            checkCacheGroup(i, GROUP1, true);
+
+        srv0.destroyCache("cache1");
+        srv0.destroyCache("cache2");
+        srv0.destroyCache("cache3");
+        srv0.destroyCache("cache4");
+        srv0.destroyCache("cache5");
+
+        ccfgs = staticConfigurations1(true);
+        startGrid(6);
+
+        checkCacheDiscoveryDataConsistent();
+
+        srv0.createCache(cacheConfiguration(null, "cache4", PARTITIONED, ATOMIC, 2, false));
+        srv0.createCache(cacheConfiguration(GROUP1, "cache5", PARTITIONED, ATOMIC, 2, false));
+
+        checkCacheDiscoveryDataConsistent();
+
+        ccfgs = staticConfigurations1(false);
+        startGrid(7);
+
+        checkCacheDiscoveryDataConsistent();
+
+        awaitPartitionMapExchange();
+    }
+
+    /**
+     * @param cnt Caches number.
+     * @param grp Cache groups.
+     * @param baseName Caches name prefix.
+     * @return Cache configurations.
+     */
+    private CacheConfiguration[] cacheConfigurations(int cnt, String grp, String baseName) {
+        CacheConfiguration[] ccfgs = new CacheConfiguration[cnt];
+
+        for (int i = 0; i < cnt; i++) {
+            ccfgs[i] = cacheConfiguration(grp,
+                baseName + i, PARTITIONED,
+                i % 2 == 0 ? TRANSACTIONAL : ATOMIC,
+                2,
+                false).setAffinity(new RendezvousAffinityFunction(false, 256));
+        }
+
+        return ccfgs;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testStartManyCaches() throws Exception {
+        final int CACHES = 5_000;
+
+        final int NODES = 4;
+
+        for (int i = 0; i < NODES; i++) {
+            ccfgs = cacheConfigurations(CACHES, GROUP1, "testCache1-");
+
+            client = i == NODES - 1;
+
+            startGrid(i);
+        }
+
+        Ignite client = ignite(NODES - 1);
+
+        client.createCaches(Arrays.asList(cacheConfigurations(CACHES, GROUP2, "testCache2-")));
+
+        checkCacheDiscoveryDataConsistent();
+
+        for (int i = 0; i < NODES; i++) {
+            for (int c = 0; c < CACHES; c++) {
+                if (c % 100 == 0)
+                    log.info("Check node: " + i);
+
+                checkCache(i, "testCache1-" + c, 1);
+                checkCache(i, "testCache2-" + c, 1);
+            }
+        }
+
+        log.info("Stop nodes.");
+
+        GridTestUtils.runMultiThreaded(new IgniteInClosure<Integer>() {
+            @Override public void apply(Integer idx) {
+                stopGrid(idx);
+            }
+        }, NODES, "stopThread");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testRebalance1() throws Exception {
+        Ignite srv0 = startGrid(0);
+
+        IgniteCache<Object, Object> srv0Cache1 =
+            srv0.createCache(cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 2, false));
+        IgniteCache<Object, Object> srv0Cache2 =
+            srv0.createCache(cacheConfiguration(GROUP1, "c2", PARTITIONED, ATOMIC, 2, false));
+        IgniteCache<Object, Object> srv0Cache3 =
+            srv0.createCache(cacheConfiguration(GROUP2, "c3", PARTITIONED, TRANSACTIONAL, 2, false));
+        IgniteCache<Object, Object> srv0Cache4 =
+            srv0.createCache(cacheConfiguration(GROUP2, "c4", PARTITIONED, TRANSACTIONAL, 2, false));
+
+        final int ITEMS = 1_000;
+
+        for (int i = 0; i < ITEMS; i++) {
+            srv0Cache1.put(new Key1(i), i);
+
+            srv0Cache3.put(new Key1(i), i);
+            srv0Cache4.put(new Key1(i), -i);
+        }
+
+        assertEquals(ITEMS, srv0Cache1.size());
+        assertEquals(ITEMS, srv0Cache1.localSize());
+        assertEquals(0, srv0Cache2.size());
+        assertEquals(ITEMS, srv0Cache3.size());
+        assertEquals(ITEMS, srv0Cache4.localSize());
+
+        startGrid(1);
+
+        awaitPartitionMapExchange();
+
+        for (int i = 0; i < 2; i++) {
+            Ignite node = ignite(i);
+
+            IgniteCache<Object, Object> cache1 = node.cache("c1");
+            IgniteCache<Object, Object> cache2 = node.cache("c2");
+            IgniteCache<Object, Object> cache3 = node.cache("c3");
+            IgniteCache<Object, Object> cache4 = node.cache("c4");
+
+            assertEquals(ITEMS * 2, cache1.size(CachePeekMode.ALL));
+            assertEquals(ITEMS, cache1.localSize(CachePeekMode.ALL));
+            assertEquals(0, cache2.size(CachePeekMode.ALL));
+            assertEquals(0, cache2.localSize(CachePeekMode.ALL));
+
+            assertEquals(ITEMS * 2, cache3.size(CachePeekMode.ALL));
+            assertEquals(ITEMS, cache3.localSize(CachePeekMode.ALL));
+
+            assertEquals(ITEMS * 2, cache4.size(CachePeekMode.ALL));
+            assertEquals(ITEMS, cache4.localSize(CachePeekMode.ALL));
+
+            for (int k = 0; k < ITEMS; k++) {
+                assertEquals(i, cache1.localPeek(new Key1(i)));
+                assertNull(cache2.localPeek(new Key1(i)));
+                assertEquals(i, cache3.localPeek(new Key1(i)));
+                assertEquals(-i, cache4.localPeek(new Key1(i)));
+            }
+        }
+
+        for (int i = 0; i < ITEMS * 2; i++)
+            srv0Cache2.put(new Key1(i), i + 1);
+
+        Ignite srv2 = startGrid(2);
+
+        awaitPartitionMapExchange();
+
+        for (int i = 0; i < 3; i++) {
+            Ignite node = ignite(i);
+
+            IgniteCache<Object, Object> cache1 = node.cache("c1");
+            IgniteCache<Object, Object> cache2 = node.cache("c2");
+            IgniteCache<Object, Object> cache3 = node.cache("c3");
+            IgniteCache<Object, Object> cache4 = node.cache("c4");
+
+            assertEquals(ITEMS * 3, cache1.size(CachePeekMode.ALL));
+            assertEquals(ITEMS, cache1.localSize(CachePeekMode.ALL));
+            assertEquals(ITEMS * 6, cache2.size(CachePeekMode.ALL));
+            assertEquals(ITEMS * 2, cache2.localSize(CachePeekMode.ALL));
+            assertEquals(ITEMS, cache3.localSize(CachePeekMode.ALL));
+            assertEquals(ITEMS, cache4.localSize(CachePeekMode.ALL));
+        }
+
+        IgniteCache<Object, Object> srv2Cache1 = srv2.cache("c1");
+        IgniteCache<Object, Object> srv2Cache2 = srv2.cache("c2");
+
+        for (int i = 0; i < ITEMS; i++)
+            assertEquals(i, srv2Cache1.localPeek(new Key1(i)));
+
+        for (int i = 0; i < ITEMS * 2; i++)
+            assertEquals(i + 1, srv2Cache2.localPeek(new Key1(i)));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testRebalance2() throws Exception {
+        Ignite srv0 = startGrid(0);
+
+        IgniteCache<Object, Object> srv0Cache1 =
+            srv0.createCache(cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 0, false));
+        IgniteCache<Object, Object> srv0Cache2 =
+            srv0.createCache(cacheConfiguration(GROUP1, "c2", PARTITIONED, ATOMIC, 0, false));
+
+        Affinity aff = srv0.affinity("c1");
+
+        final int ITEMS = 2_000;
+
+        Map<Integer, Integer> c1Data = new HashMap<>();
+        Map<Integer, Integer> c2Data = new HashMap<>();
+
+        for (int i = 0; i < ITEMS; i++) {
+            srv0Cache1.put(i, i);
+            c1Data.put(i, i);
+
+            if (i % 2 == 0) {
+                srv0Cache2.put(i, i);
+                c2Data.put(i, i);
+            }
+        }
+
+        assertEquals(ITEMS, srv0Cache1.size());
+        assertEquals(ITEMS / 2, srv0Cache2.size());
+
+        Ignite srv1 = startGrid(1);
+
+        awaitPartitionMapExchange();
+
+        assertEquals(ITEMS, srv0Cache1.size());
+        assertEquals(ITEMS / 2, srv0Cache2.size());
+
+        checkCacheData(c1Data, "c1");
+        checkCacheData(c2Data, "c2");
+
+        Set<Integer> srv1Parts = new HashSet<>();
+
+        for (Integer p : aff.primaryPartitions(srv1.cluster().localNode()))
+            srv1Parts.add(p);
+
+        CacheGroupContext grpSrv0 = cacheGroup(srv0, GROUP1);
+        CacheGroupContext grpSrv1 = cacheGroup(srv1, GROUP1);
+
+        for (int p = 0; p < aff.partitions(); p++) {
+            if (srv1Parts.contains(p)) {
+                GridIterator<CacheDataRow> it = grpSrv0.offheap().partitionIterator(p);
+                assertFalse(it.hasNext());
+
+                it = grpSrv1.offheap().partitionIterator(p);
+                assertTrue(it.hasNext());
+            }
+            else {
+                GridIterator<CacheDataRow> it = grpSrv0.offheap().partitionIterator(p);
+                assertTrue(it.hasNext());
+
+                it = grpSrv1.offheap().partitionIterator(p);
+                assertFalse(it.hasNext());
+            }
+        }
+
+        c1Data = new HashMap<>();
+        c2Data = new HashMap<>();
+
+        for (int i = 0; i < ITEMS; i++) {
+            srv0Cache1.put(i, i + 1);
+            c1Data.put(i, i + 1);
+
+            if (i % 2 == 0) {
+                srv0Cache2.put(i, i + 1);
+                c2Data.put(i, i + 1);
+            }
+        }
+
+        checkCacheData(c1Data, "c1");
+        checkCacheData(c2Data, "c2");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testNoKeyIntersectTx() throws Exception {
+        testNoKeyIntersect(TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testNoKeyIntersectAtomic() throws Exception {
+        testNoKeyIntersect(ATOMIC);
+    }
+
+    /**
+     * @param atomicityMode Atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void testNoKeyIntersect(CacheAtomicityMode atomicityMode) throws Exception {
+        startGrid(0);
+
+        testNoKeyIntersect(atomicityMode, false);
+
+        testNoKeyIntersect(atomicityMode, true);
+
+        startGridsMultiThreaded(1, 4);
+
+        testNoKeyIntersect(atomicityMode, false);
+
+        testNoKeyIntersect(atomicityMode, true);
+    }
+
+    /**
+     * @param keys Keys.
+     * @param rnd Random.
+     * @return Added key.
+     */
+    private Integer addKey(Set<Integer> keys, ThreadLocalRandom rnd) {
+        for (;;) {
+            Integer key = rnd.nextInt(100_000);
+
+            if (keys.add(key))
+                return key;
+        }
+    }
+
+    /**
+     * @param atomicityMode Cache atomicity mode.
+     * @param heapCache On heap cache flag.
+     * @throws Exception If failed.
+     */
+    private void testNoKeyIntersect(CacheAtomicityMode atomicityMode, boolean heapCache) throws Exception {
+        Ignite srv0 = ignite(0);
+
+        try {
+            IgniteCache cache1 = srv0.
+                createCache(cacheConfiguration(GROUP1, "c1", PARTITIONED, atomicityMode, 1, heapCache));
+
+            Set<Integer> keys = new LinkedHashSet<>(30);
+
+            ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+            for (int i = 0; i < 10; i++) {
+                Integer key = addKey(keys, rnd);
+
+                cache1.put(key, key);
+                cache1.put(new Key1(key), new Value1(key));
+                cache1.put(new Key2(key), new Value2(key));
+            }
+
+            assertEquals(30, cache1.size());
+
+            IgniteCache cache2 = srv0.
+                createCache(cacheConfiguration(GROUP1, "c2", PARTITIONED, atomicityMode, 1, heapCache));
+
+            assertEquals(30, cache1.size());
+            assertEquals(0 , cache2.size());
+
+            for (Integer key : keys) {
+                assertNull(cache2.get(key));
+                assertNull(cache2.get(new Key1(key)));
+                assertNull(cache2.get(new Key2(key)));
+
+                cache2.put(key, key + 1);
+                cache2.put(new Key1(key), new Value1(key + 1));
+                cache2.put(new Key2(key), new Value2(key + 1));
+            }
+
+            assertEquals(30, cache1.size());
+            assertEquals(30, cache2.size());
+
+            for (int i = 0; i < 10; i++) {
+                Integer key = addKey(keys, rnd);
+
+                cache2.put(key, key + 1);
+                cache2.put(new Key1(key), new Value1(key + 1));
+                cache2.put(new Key2(key), new Value2(key + 1));
+            }
+
+            assertEquals(30, cache1.size());
+            assertEquals(60, cache2.size());
+
+            int i = 0;
+
+            for (Integer key : keys) {
+                if (i++ < 10) {
+                    assertEquals(key, cache1.get(key));
+                    assertEquals(new Value1(key), cache1.get(new Key1(key)));
+                    assertEquals(new Value2(key), cache1.get(new Key2(key)));
+                }
+                else {
+                    assertNull(cache1.get(key));
+                    assertNull(cache1.get(new Key1(key)));
+                    assertNull(cache1.get(new Key2(key)));
+                }
+
+                assertEquals(key + 1, cache2.get(key));
+                assertEquals(new Value1(key + 1), cache2.get(new Key1(key)));
+                assertEquals(new Value2(key + 1), cache2.get(new Key2(key)));
+            }
+
+            IgniteCache cache3 = srv0.
+                createCache(cacheConfiguration(GROUP1, "c3", PARTITIONED, atomicityMode, 1, heapCache));
+
+            assertEquals(30, cache1.size());
+            assertEquals(60, cache2.size());
+            assertEquals(0, cache3.size());
+
+            for (Integer key : keys) {
+                assertNull(cache3.get(key));
+                assertNull(cache3.get(new Key1(key)));
+                assertNull(cache3.get(new Key2(key)));
+            }
+
+            for (Integer key : keys) {
+                cache3.put(key, key);
+                cache3.put(new Key1(key), new Value1(key));
+                cache3.put(new Key2(key), new Value2(key));
+            }
+
+            i = 0;
+
+            for (Integer key : keys) {
+                if (i++ < 10) {
+                    assertEquals(key, cache1.get(key));
+                    assertEquals(new Value1(key), cache1.get(new Key1(key)));
+                    assertEquals(new Value2(key), cache1.get(new Key2(key)));
+                }
+                else {
+                    assertNull(cache1.get(key));
+                    assertNull(cache1.get(new Key1(key)));
+                    assertNull(cache1.get(new Key2(key)));
+                }
+
+                assertEquals(key + 1, cache2.get(key));
+                assertEquals(new Value1(key + 1), cache2.get(new Key1(key)));
+                assertEquals(new Value2(key + 1), cache2.get(new Key2(key)));
+
+                assertEquals(key, cache3.get(key));
+                assertEquals(new Value1(key), cache3.get(new Key1(key)));
+                assertEquals(new Value2(key), cache3.get(new Key2(key)));
+            }
+
+            i = 0;
+
+            for (Integer key : keys) {
+                if (i++ == 3)
+                    break;
+
+                cache1.remove(key);
+                cache1.remove(new Key1(key));
+                cache1.remove(new Key2(key));
+
+                assertNull(cache1.get(key));
+                assertNull(cache1.get(new Key1(key)));
+                assertNull(cache1.get(new Key2(key)));
+
+                assertEquals(key + 1, cache2.get(key));
+                assertEquals(new Value1(key + 1), cache2.get(new Key1(key)));
+                assertEquals(new Value2(key + 1), cache2.get(new Key2(key)));
+
+                assertEquals(key, cache3.get(key));
+                assertEquals(new Value1(key), cache3.get(new Key1(key)));
+                assertEquals(new Value2(key), cache3.get(new Key2(key)));
+            }
+
+            cache1.removeAll();
+
+            for (Integer key : keys) {
+                assertNull(cache1.get(key));
+                assertNull(cache1.get(new Key1(key)));
+                assertNull(cache1.get(new Key2(key)));
+
+                assertEquals(key + 1, cache2.get(key));
+                assertEquals(new Value1(key + 1), cache2.get(new Key1(key)));
+                assertEquals(new Value2(key + 1), cache2.get(new Key2(key)));
+
+                assertEquals(key, cache3.get(key));
+                assertEquals(new Value1(key), cache3.get(new Key1(key)));
+                assertEquals(new Value2(key), cache3.get(new Key2(key)));
+            }
+
+            cache2.removeAll();
+
+            for (Integer key : keys) {
+                assertNull(cache1.get(key));
+                assertNull(cache1.get(new Key1(key)));
+                assertNull(cache1.get(new Key2(key)));
+
+                assertNull(cache2.get(key));
+                assertNull(cache2.get(new Key1(key)));
+                assertNull(cache2.get(new Key2(key)));
+
+                assertEquals(key, cache3.get(key));
+                assertEquals(new Value1(key), cache3.get(new Key1(key)));
+                assertEquals(new Value2(key), cache3.get(new Key2(key)));
+            }
+
+            if (atomicityMode == TRANSACTIONAL)
+                testNoKeyIntersectTxLocks(cache1, cache2);
+        }
+        finally {
+            srv0.destroyCaches(Arrays.asList("c1", "c2", "c3"));
+        }
+    }
+
+    /**
+     * @param cache1 Cache1.
+     * @param cache2 Cache2.
+     * @throws Exception If failed.
+     */
+    private void testNoKeyIntersectTxLocks(final IgniteCache cache1, final IgniteCache cache2) throws Exception {
+        final Ignite node = (Ignite)cache1.unwrap(Ignite.class);
+
+        for (int i = 0; i < 5; i++) {
+            final Integer key = ThreadLocalRandom.current().nextInt(1000);
+
+            Lock lock = cache1.lock(key);
+
+            lock.lock();
+
+            try {
+                IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() {
+                    @Override public Object call() throws Exception {
+                        Lock lock1 = cache1.lock(key);
+
+                        assertFalse(lock1.tryLock());
+
+                        Lock lock2 = cache2.lock(key);
+
+                        assertTrue(lock2.tryLock());
+
+                        lock2.unlock();
+
+                        return null;
+                    }
+                }, "lockThread");
+
+                fut.get(10_000);
+            }
+            finally {
+                lock.unlock();
+            }
+
+            try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+                cache1.put(key, 1);
+
+                IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() {
+                    @Override public Object call() throws Exception {
+                        try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+                            cache2.put(key, 2);
+
+                            tx.commit();
+                        }
+
+                        assertEquals(2, cache2.get(key));
+
+                        return null;
+                    }
+                }, "txThread");
+
+                fut.get(10_000);
+
+                tx.commit();
+            }
+
+            assertEquals(1, cache1.get(key));
+            assertEquals(2, cache2.get(key));
+
+            try (Transaction tx = node.transactions().txStart(OPTIMISTIC, SERIALIZABLE)) {
+                Integer val = (Integer)cache1.get(key);
+
+                cache1.put(key, val + 10);
+
+                IgniteInternalFuture fut = GridTestUtils.runAsync(new Callable() {
+                    @Override public Object call() throws Exception {
+                        try (Transaction tx = node.transactions().txStart(PESSIMISTIC, REPEATABLE_READ)) {
+                            cache2.put(key, 3);
+
+                            tx.commit();
+                        }
+
+                        assertEquals(3, cache2.get(key));
+
+                        return null;
+                    }
+                }, "txThread");
+
+                fut.get(10_000);
+
+                tx.commit();
+            }
+
+            assertEquals(11, cache1.get(key));
+            assertEquals(3, cache2.get(key));
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheApiTxPartitioned() throws Exception {
+        cacheApiTest(PARTITIONED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheApiTxReplicated() throws Exception {
+        cacheApiTest(REPLICATED, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheApiAtomicPartitioned() throws Exception {
+        cacheApiTest(PARTITIONED, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheApiAtomicReplicated() throws Exception {
+        cacheApiTest(REPLICATED, ATOMIC);
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void cacheApiTest(CacheMode cacheMode, CacheAtomicityMode atomicityMode) throws Exception {
+        startGridsMultiThreaded(4);
+
+        client = true;
+
+        startGrid(4);
+
+        int[] backups = cacheMode == REPLICATED ? new int[]{Integer.MAX_VALUE} : new int[]{0, 1, 2, 3};
+
+        for (int backups0 : backups)
+            cacheApiTest(cacheMode, atomicityMode, backups0, false, false, false);
+
+        int backups0 = cacheMode == REPLICATED ? Integer.MAX_VALUE :
+            backups[ThreadLocalRandom.current().nextInt(backups.length)];
+
+        cacheApiTest(cacheMode, atomicityMode, backups0, true, false, false);
+
+        if (cacheMode == PARTITIONED) {
+            // Here the f variable is used as a bit set where 2 last bits
+            // determine whether a near cache is used on server/client side.
+            // The case without near cache is already tested at this point.
+            for (int f : new int[]{1, 2, 3}) {
+                cacheApiTest(cacheMode, atomicityMode, backups0, false, nearSrv(f), nearClient(f));
+                cacheApiTest(cacheMode, atomicityMode, backups0, true, nearSrv(f), nearClient(f));
+            }
+        }
+    }
+
+    /**
+     * @param flag Flag.
+     * @return {@code True} if near cache should be used on a client side.
+     */
+    private boolean nearClient(int flag) {
+        return (flag & 0b01) == 0b01;
+    }
+
+    /**
+     * @param flag Flag.
+     * @return {@code True} if near cache should be used on a server side.
+     */
+    private boolean nearSrv(int flag) {
+        return (flag & 0b10) == 0b10;
+    }
+
+    /**
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Atomicity mode.
+     * @param backups Number of backups.
+     * @param heapCache On heap cache flag.
+     * @param nearSrv {@code True} if near cache should be used on a server side.
+     * @param nearClient {@code True} if near cache should be used on a client side.
+     * @throws Exception If failed.
+     */
+    private void cacheApiTest(CacheMode cacheMode,
+        CacheAtomicityMode atomicityMode,
+        int backups,
+        boolean heapCache,
+        boolean nearSrv,
+        boolean nearClient) throws Exception {
+        Ignite srv0 = ignite(0);
+
+        NearCacheConfiguration nearCfg = nearSrv ? new NearCacheConfiguration() : null;
+
+        srv0.createCache(cacheConfiguration(GROUP1, "cache-0", cacheMode, atomicityMode, backups, heapCache)
+            .setNearConfiguration(nearCfg));
+
+        srv0.createCache(cacheConfiguration(GROUP1, "cache-1", cacheMode, atomicityMode, backups, heapCache));
+
+        srv0.createCache(cacheConfiguration(GROUP2, "cache-2", cacheMode, atomicityMode, backups, heapCache)
+            .setNearConfiguration(nearCfg));
+
+        srv0.createCache(cacheConfiguration(null, "cache-3", cacheMode, atomicityMode, backups, heapCache));
+
+        if (nearClient) {
+            Ignite clientNode = ignite(4);
+
+            clientNode.createNearCache("cache-0", new NearCacheConfiguration());
+            clientNode.createNearCache("cache-2", new NearCacheConfiguration());
+        }
+
+        try {
+            for (final Ignite node : Ignition.allGrids()) {
+                List<Callable<?>> ops = new ArrayList<>();
+
+                for (int i = 0; i < 4; i++)
+                    ops.add(testSet(node.cache("cache-" + i), cacheMode, atomicityMode, backups, heapCache, node));
+
+                // Async operations.
+                GridTestUtils.runMultiThreaded(ops, "cacheApiTest");
+            }
+        }
+        finally {
+            for (int i = 0; i < 4; i++)
+                srv0.destroyCache("cache-" + i);
+        }
+    }
+
+    /**
+     * @param cache Cache.
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Atomicity mode.
+     * @param backups Number of backups.
+     * @param heapCache On heap cache flag.
+     * @param node Ignite node.
+     * @return Callable for the test operations.
+     */
+    private Callable<?> testSet(
+        final IgniteCache<Object, Object> cache,
+        final CacheMode cacheMode,
+        final CacheAtomicityMode atomicityMode,
+        final int backups,
+        final boolean heapCache,
+        final Ignite node) {
+        return new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                log.info("Test cache [node=" + node.name() +
+                    ", cache=" + cache.getName() +
+                    ", mode=" + cacheMode +
+                    ", atomicity=" + atomicityMode +
+                    ", backups=" + backups +
+                    ", heapCache=" + heapCache +
+                    ']');
+
+                cacheApiTest(cache);
+
+                return null;
+            }
+        };
+    }
+
+    /**
+     * @param cache Cache.
+     * @throws Exception If failed.
+     */
+    private void cacheApiTest(IgniteCache cache) throws Exception {
+        cachePutAllGetAll(cache);
+
+        cachePutRemove(cache);
+
+        cachePutGet(cache);
+
+        cachePutGetAndPut(cache);
+
+        cacheQuery(cache);
+
+        cacheInvokeAll(cache);
+
+        cacheInvoke(cache);
+
+        cacheDataStreamer(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void tearDown(IgniteCache cache) {
+        cache.clear();
+        cache.removeAll();
+    }
+
+    /**
+     * @param cache Cache.
+     * @throws Exception If failed.
+     */
+    private void cacheDataStreamer(final IgniteCache cache) throws Exception {
+        final int keys = 1000;
+        final int loaders = 4;
+
+        final Integer[] data = generateData(keys * loaders);
+
+        // Stream through a client node.
+        Ignite clientNode = ignite(4);
+
+        List<Callable<?>> cls = new ArrayList<>(loaders);
+
+        for (final int i : sequence(loaders)) {
+            final IgniteDataStreamer ldr = clientNode.dataStreamer(cache.getName());
+
+            ldr.autoFlushFrequency(0);
+
+            cls.add(new Callable<Void>() {
+                @Override public Void call() throws Exception {
+                    List<IgniteFuture> futs = new ArrayList<>(keys);
+
+                    for (int j = 0, size = keys * loaders; j < size; j++) {
+                        if (j % loaders == i)
+                            futs.add(ldr.addData(j, data[j]));
+
+                        if(j % (100 * loaders) == 0)
+                            ldr.flush();
+                    }
+
+                    ldr.flush();
+
+                    for (IgniteFuture fut : futs)
+                        fut.get();
+
+                    return null;
+                }
+            });
+        }
+
+        GridTestUtils.runMultiThreaded(cls, "loaders");
+
+        Set<Integer> keysSet = sequence(data.length);
+
+        for (Cache.Entry<Integer, Integer> entry : (IgniteCache<Integer, Integer>)cache) {
+            assertTrue(keysSet.remove(entry.getKey()));
+            assertEquals(data[entry.getKey()], entry.getValue());
+        }
+
+        assertTrue(keysSet.isEmpty());
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cachePutAllGetAll(IgniteCache cache) {
+        Map<Integer, Integer> data = generateDataMap(1000);
+
+        cache.putAll(data);
+
+        Map data0 = cache.getAll(data.keySet());
+
+        assertEquals(data.size(), data0.size());
+
+        for (Map.Entry<Integer, Integer> entry : data.entrySet()) {
+            assertEquals(entry.getValue(), data0.get(entry.getKey()));
+        }
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cachePutRemove(IgniteCache cache) {
+        Random rnd = ThreadLocalRandom.current();
+
+        Integer key = rnd.nextInt();
+        Integer val = rnd.nextInt();
+
+        cache.put(key, val);
+
+        assertTrue(cache.remove(key));
+
+        assertNull(cache.get(key));
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cachePutGet(IgniteCache cache) {
+        Random rnd = ThreadLocalRandom.current();
+
+        Integer key = rnd.nextInt();
+        Integer val = rnd.nextInt();
+
+        cache.put(key, val);
+
+        Object val0 = cache.get(key);
+
+        assertEquals(val, val0);
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cachePutGetAndPut(IgniteCache cache) {
+        Random rnd = ThreadLocalRandom.current();
+
+        Integer key = rnd.nextInt();
+        Integer val1 = rnd.nextInt();
+        Integer val2 = rnd.nextInt();
+
+        cache.put(key, val1);
+
+        Object val0 = cache.getAndPut(key, val2);
+
+        assertEquals(val1, val0);
+
+        val0 = cache.get(key);
+
+        assertEquals(val2, val0);
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cacheQuery(IgniteCache cache) {
+        Map<Integer, Integer> data = generateDataMap(1000);
+
+        cache.putAll(data);
+
+        ScanQuery<Integer, Integer> qry = new ScanQuery<>(new IgniteBiPredicate<Integer, Integer>() {
+            @Override public boolean apply(Integer key, Integer val) {
+                return key % 2 == 0;
+            }
+        });
+
+        List<Cache.Entry<Integer, Integer>> all = cache.query(qry).getAll();
+
+        assertEquals(all.size(), data.size() / 2);
+
+        for (Cache.Entry<Integer, Integer> entry : all) {
+            assertEquals(0, entry.getKey() % 2);
+            assertEquals(entry.getValue(), data.get(entry.getKey()));
+        }
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cacheInvokeAll(IgniteCache cache) {
+        int keys = 1000;
+        Map<Integer, Integer> data = generateDataMap(keys);
+
+        cache.putAll(data);
+
+        Random rnd = ThreadLocalRandom.current();
+
+        int one = rnd.nextInt();
+        int two = rnd.nextInt();
+
+        Map<Integer, CacheInvokeResult<Integer>> res = cache.invokeAll(data.keySet(), new CacheEntryProcessor<Integer, Integer, Integer>() {
+            @Override public Integer process(MutableEntry<Integer, Integer> entry, Object... arguments) throws EntryProcessorException {
+                Object expected = ((Map)arguments[0]).get(entry.getKey());
+
+                assertEquals(expected, entry.getValue());
+
+                // Some calculation.
+                return (Integer)arguments[1] + (Integer)arguments[2];
+            }
+        }, data, one, two);
+
+        assertEquals(keys, res.size());
+        assertEquals(one + two, (Object)res.get(0).get());
+
+        tearDown(cache);
+    }
+
+    /**
+     * @param cache Cache.
+     */
+    private void cacheInvoke(IgniteCache cache) {
+        Random rnd = ThreadLocalRandom.current();
+
+        Integer key = rnd.nextInt();
+        Integer val = rnd.nextInt();
+
+        cache.put(key, val);
+
+        int one = rnd.nextInt();
+        int two = rnd.nextInt();
+
+        Object res = cache.invoke(key, new CacheEntryProcessor<Integer, Integer, Integer>() {
+            @Override public Integer process(MutableEntry<Integer, Integer> entry, Object... arguments) throws EntryProcessorException {
+                assertEquals(arguments[0], entry.getValue());
+
+                // Some calculation.
+                return (Integer)arguments[1] + (Integer)arguments[2];
+            }
+        }, val, one, two);
+
+        assertEquals(one + two, res);
+
+        tearDown(cache);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConcurrentOperationsSameKeys() throws Exception {
+        final int SRVS = 4;
+        final int CLIENTS = 4;
+        final int NODES = SRVS + CLIENTS;
+
+        startGrid(0);
+
+        Ignite srv0 = startGridsMultiThreaded(1, SRVS - 1);
+
+        client = true;
+
+        startGridsMultiThreaded(SRVS, CLIENTS);
+
+        srv0.createCache(cacheConfiguration(GROUP1, "a0", PARTITIONED, ATOMIC, 1, false));
+        srv0.createCache(cacheConfiguration(GROUP1, "a1", PARTITIONED, ATOMIC, 1, false));
+        srv0.createCache(cacheConfiguration(GROUP1, "t0", PARTITIONED, TRANSACTIONAL, 1, false));
+        srv0.createCache(cacheConfiguration(GROUP1, "t1", PARTITIONED, TRANSACTIONAL, 1, false));
+
+        final List<Integer> keys = new ArrayList<>();
+
+        for (int i = 0; i < 50; i++)
+            keys.add(i);
+
+        final AtomicBoolean err = new AtomicBoolean();
+
+        final AtomicBoolean stop = new AtomicBoolean();
+
+        IgniteInternalFuture fut1 = updateFuture(NODES, "a0", keys, false, stop, err);
+        IgniteInternalFuture fut2 = updateFuture(NODES, "a1", keys, true, stop, err);
+        IgniteInternalFuture fut3 = updateFuture(NODES, "t0", keys, false, stop, err);
+        IgniteInternalFuture fut4 = updateFuture(NODES, "t1", keys, true, stop, err);
+
+        try {
+            for (int i = 0; i < 15 && !stop.get(); i++)
+                U.sleep(1_000);
+        }
+        finally {
+            stop.set(true);
+        }
+
+        fut1.get();
+        fut2.get();
+        fut3.get();
+        fut4.get();
+
+        assertFalse("Unexpected error, see log for details", err.get());
+    }
+
+    /**
+     * @param nodes Total number of nodes.
+     * @param cacheName Cache name.
+     * @param keys Keys to update.
+     * @param reverse {@code True} if update in reverse order.
+     * @param stop Stop flag.
+     * @param err Error flag.
+     * @return Update future.
+     */
+    private IgniteInternalFuture updateFuture(final int nodes,
+        final String cacheName,
+        final List<Integer> keys,
+        final boolean reverse,
+        final AtomicBoolean stop,
+        final AtomicBoolean err) {
+        final AtomicInteger idx = new AtomicInteger();
+
+        return GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                try {
+                    Ignite node = ignite(idx.getAndIncrement() % nodes);
+
+                    log.info("Start thread [node=" + node.name() + ']');
+
+                    IgniteCache cache = node.cache(cacheName);
+
+                    Map<Integer, Integer> map = new LinkedHashMap<>();
+
+                    if (reverse) {
+                        for (int i = keys.size() - 1; i >= 0; i--)
+                            map.put(keys.get(i), 2);
+                    }
+                    else {
+                        for (Integer key : keys)
+                            map.put(key, 1);
+                    }
+
+                    while (!stop.get())
+                        cache.putAll(map);
+                }
+                catch (Exception e) {
+                    err.set(true);
+
+                    log.error("Unexpected error: " + e, e);
+
+                    stop.set(true);
+                }
+
+                return null;
+            }
+        }, nodes * 2, "update-" + cacheName + "-" + reverse);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConcurrentOperationsAndCacheDestroy() throws Exception {
+        final int SRVS = 4;
+        final int CLIENTS = 4;
+        final int NODES = SRVS + CLIENTS;
+
+        startGrid(0);
+
+        Ignite srv0 = startGridsMultiThreaded(1, SRVS - 1);
+
+        client = true;
+
+        startGridsMultiThreaded(SRVS, CLIENTS);
+
+        final int CACHES = 8;
+
+        final int grp1Backups = ThreadLocalRandom.current().nextInt(3);
+        final int grp2Backups = ThreadLocalRandom.current().nextInt(3);
+
+        log.info("Start test [grp1Backups=" + grp1Backups + ", grp2Backups=" + grp2Backups + ']');
+
+        for (int i = 0; i < CACHES; i++) {
+            srv0.createCache(
+                cacheConfiguration(GROUP1, GROUP1 + "-" + i, PARTITIONED, ATOMIC, grp1Backups, i % 2 == 0));
+
+            srv0.createCache(
+                cacheConfiguration(GROUP2, GROUP2 + "-" + i, PARTITIONED, TRANSACTIONAL, grp2Backups, i % 2 == 0));
+        }
+
+        final AtomicInteger idx = new AtomicInteger();
+
+        final AtomicBoolean err = new AtomicBoolean();
+
+        final AtomicBoolean stop = new AtomicBoolean();
+
+        IgniteInternalFuture opFut = GridTestUtils.runMultiThreadedAsync(new Runnable() {
+            @Override public void run() {
+                try {
+                    Ignite node = ignite(idx.getAndIncrement() % NODES);
+
+                    log.info("Start thread [node=" + node.name() + ']');
+
+                    ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+                    while (!stop.get()) {
+                        String grp = rnd.nextBoolean() ? GROUP1 : GROUP2;
+                        int cacheIdx = rnd.nextInt(CACHES);
+
+                        IgniteCache cache = node.cache(grp + "-" + cacheIdx);
+
+                        for (int i = 0; i < 10; i++)
+                            cacheOperation(rnd, cache);
+                    }
+                }
+                catch (Exception e) {
+                    err.set(true);
+
+                    log.error("Unexpected error(1): " + e, e);
+
+                    stop.set(true);
+                }
+            }
+        }, (SRVS + CLIENTS) * 2, "op-thread");
+
+        IgniteInternalFuture cacheFut = GridTestUtils.runAsync(new Runnable() {
+            @Override public void run() {
+                try {
+                    int cntr = 0;
+
+                    while (!stop.get()) {
+                        ThreadLocalRandom rnd = ThreadLocalRandom.current();
+
+                        String grp;
+                        int backups;
+
+                        if (rnd.nextBoolean()) {
+                            grp = GROUP1;
+                            backups = grp1Backups;
+                        }
+                        else {
+                            grp = GROUP2;
+                            backups = grp2Backups;
+                        }
+
+                        Ignite node = ignite(rnd.nextInt(NODES));
+
+                        log.info("Create cache [node=" + node.name() + ", grp=" + grp + ']');
+
+                        IgniteCache cache = node.createCache(cacheConfiguration(grp, "tmpCache-" + cntr++,
+                            PARTITIONED,
+                            rnd.nextBoolean() ? ATOMIC : TRANSACTIONAL,
+                            backups,
+                            rnd.nextBoolean()));
+
+                        for (int i = 0; i < 10; i++)
+                            cacheOperation(rnd, cache);
+
+                        log.info("Destroy cache [node=" + node.name() + ", grp=" + grp + ']');
+
+                        node.destroyCache(cache.getName());
+                    }
+                }
+                catch (Exception e) {
+                    err.set(true);
+
+                    log.error("Unexpected error(2): " + e, e);
+
+                    stop.set(true);
+                }
+            }
+        }, "cache-destroy-thread");
+
+        try {
+            for (int i = 0; i < 30 && !stop.get(); i++)
+                U.sleep(1_000);
+        }
+        finally {
+            stop.set(true);
+        }
+
+        opFut.get();
+        cacheFut.get();
+
+        assertFalse("Unexpected error, see log for details", err.get());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testStaticConfigurationsValidation() throws Exception {
+        ccfgs = new CacheConfiguration[2];
+
+        ccfgs[0] = new CacheConfiguration(CACHE1);
+        ccfgs[0].setGroupName(GROUP1);
+        ccfgs[0].setAffinity(new RendezvousAffinityFunction(false, 1024));
+
+        ccfgs[1] = new CacheConfiguration(CACHE2);
+        ccfgs[1].setGroupName(GROUP1);
+        ccfgs[1].setAffinity(new RendezvousAffinityFunction(false, 512));
+
+        try {
+            startGrid(0);
+
+            fail();
+        }
+        catch (IgniteCheckedException ignore) {
+            // Expected exception.
+        }
+
+        ccfgs = new CacheConfiguration[3];
+
+        ccfgs[0] = new CacheConfiguration(CACHE1);
+        ccfgs[0].setGroupName(GROUP1);
+        ccfgs[0].setAffinity(new RendezvousAffinityFunction(false, 16));
+
+        ccfgs[1] = new CacheConfiguration(CACHE2);
+        ccfgs[1].setGroupName(GROUP2);
+        ccfgs[1].setAffinity(new RendezvousAffinityFunction(false, 512));
+
+        ccfgs[2] = new CacheConfiguration("cache3");
+        ccfgs[2].setGroupName(GROUP1);
+        ccfgs[2].setAffinity(new RendezvousAffinityFunction(false, 16));
+
+        startGrid(0);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheIdConflict() throws Exception {
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("AaAaAa"), new CacheConfiguration("AaAaBB")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(0);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("AaAaAa")};
+
+        startGrid(0);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("AaAaBB")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(1);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("AaAaBB"));
+
+        final Ignite ignite1 = startGrid(1);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite1.createCache(new CacheConfiguration("AaAaBB"));
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("AaAaBB"));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheGroupIdConflict1() throws Exception {
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration(CACHE1).setGroupName("AaAaAa"),
+            new CacheConfiguration(CACHE2).setGroupName("AaAaBB")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(0);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration(CACHE1).setGroupName("AaAaAa")};
+
+        startGrid(0);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration(CACHE2).setGroupName("AaAaBB")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(1);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains(CACHE2));
+
+        final Ignite ignite1 = startGrid(1);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite1.createCache(new CacheConfiguration(CACHE2).setGroupName("AaAaBB"));
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains(CACHE2));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheGroupIdConflict2() throws Exception {
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("AaAaAa"),
+            new CacheConfiguration(CACHE2).setGroupName("AaAaBB")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(0);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("AaAaAa")};
+
+        startGrid(0);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration(CACHE2).setGroupName("AaAaBB")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(1);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains(CACHE2));
+
+        final Ignite ignite1 = startGrid(1);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite1.createCache(new CacheConfiguration(CACHE2).setGroupName("AaAaBB"));
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains(CACHE2));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheGroupIdConflict3() throws Exception {
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration(CACHE2).setGroupName("AaAaBB"),
+            new CacheConfiguration("AaAaAa")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(0);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration(CACHE2).setGroupName("AaAaBB")};
+
+        startGrid(0);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("AaAaAa")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(1);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("AaAaAa"));
+
+        final Ignite ignite1 = startGrid(1);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite1.createCache(new CacheConfiguration("AaAaAa"));
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("AaAaAa"));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheGroupNameConflict1() throws Exception {
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("cache1"), new CacheConfiguration("cache2").setGroupName("cache1")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(0);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("cache1")};
+
+        startGrid(0);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("cache2").setGroupName("cache1")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(1);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("cache2"));
+
+        final Ignite ignite1 = startGrid(1);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite1.createCache(new CacheConfiguration("cache2").setGroupName("cache1"));
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("cache2"));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCacheGroupNameConflict2() throws Exception {
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("cache2").setGroupName("cache1"), new CacheConfiguration("cache1")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(0);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("cache2").setGroupName("cache1")};
+
+        startGrid(0);
+
+        ccfgs = new CacheConfiguration[]{new CacheConfiguration("cache1")};
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                startGrid(1);
+
+                return null;
+            }
+        }, IgniteCheckedException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("cache1"));
+
+        final Ignite ignite1 = startGrid(1);
+
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                ignite1.createCache(new CacheConfiguration("cache1"));
+
+                return null;
+            }
+        }, CacheException.class, null);
+
+        assertFalse(ignite(0).cacheNames().contains("cache1"));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void 

<TRUNCATED>

[07/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
index d3042e9..88dc863 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
@@ -57,6 +57,8 @@ import org.apache.ignite.internal.pagemem.snapshot.StartSnapshotOperationAckDisc
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache;
 import org.apache.ignite.internal.processors.cache.CacheAffinityChangeMessage;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.CacheInvalidStateException;
 import org.apache.ignite.internal.processors.cache.CachePartitionExchangeWorkerTask;
 import org.apache.ignite.internal.processors.cache.ClusterState;
@@ -198,8 +200,8 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
     /** */
     private CacheAffinityChangeMessage affChangeMsg;
 
-    /** Cache validation results. */
-    private volatile Map<Integer, CacheValidation> cacheValidRes;
+    /** Cache groups validation results. */
+    private volatile Map<Integer, CacheValidation> grpValidRes;
 
     /** Skip preload flag. */
     private boolean skipPreload;
@@ -237,6 +239,7 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
     /** */
     private volatile IgniteDhtPartitionsToReloadMap partsToReload = new IgniteDhtPartitionsToReloadMap();
 
+    /** */
     private final AtomicBoolean done = new AtomicBoolean();
 
     /**
@@ -404,8 +407,8 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
     }
 
     /**
-     * @param cacheId Cache ID to check.
-     * @param rcvdFrom Topology version.
+     * @param cacheId Cache ID.
+     * @param rcvdFrom Node ID cache was received from.
      * @return {@code True} if cache was added during this exchange.
      */
     public boolean cacheAddedOnExchange(int cacheId, UUID rcvdFrom) {
@@ -413,14 +416,32 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
     }
 
     /**
+     * @param grpId Cache group ID.
+     * @param rcvdFrom Node ID cache group was received from.
+     * @return {@code True} if cache group was added during this exchange.
+     */
+    public boolean cacheGroupAddedOnExchange(int grpId, UUID rcvdFrom) {
+        return dynamicCacheGroupStarted(grpId) ||
+            (exchId.isJoined() && exchId.nodeId().equals(rcvdFrom));
+    }
+
+    /**
      * @param cacheId Cache ID.
      * @return {@code True} if non-client cache was added during this exchange.
      */
-    public boolean dynamicCacheStarted(int cacheId) {
+    private boolean dynamicCacheStarted(int cacheId) {
         return exchActions != null && exchActions.cacheStarted(cacheId);
     }
 
     /**
+     * @param grpId Cache group ID.
+     * @return {@code True} if non-client cache group was added during this exchange.
+     */
+    public boolean dynamicCacheGroupStarted(int grpId) {
+        return exchActions != null && exchActions.cacheGroupStarting(grpId);
+    }
+
+    /**
      * @return {@code True}
      */
     public boolean onAdded() {
@@ -550,8 +571,24 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
                             exchActions = new ExchangeActions();
 
+                            Set<String> cacheNames = new HashSet<>();
+
+                            Collection<CacheGroupDescriptor> grpDescs = new ArrayList<>();
+
+                            for (Integer grpId : op.cacheGroupIds()) {
+                                CacheGroupContext cacheGrp = cctx.cache().cacheGroup(grpId);
+
+                                if (cacheGrp == null)
+                                    continue;
+
+                                grpDescs.add(cctx.cache().cacheGroupDescriptors().get(grpId));
+
+                                for (Integer cacheId : cacheGrp.cacheIds())
+                                    cacheNames.add(cctx.cacheContext(cacheId).name());
+                            }
+
                             List<DynamicCacheChangeRequest> destroyRequests = getStopCacheRequests(
-                                cctx.cache(), op.cacheNames(), cctx.localNodeId());
+                                cctx.cache(), cacheNames, cctx.localNodeId());
 
                             if (!F.isEmpty(destroyRequests)) { //Emulate destroy cache request
                                 for (DynamicCacheChangeRequest req : destroyRequests) {
@@ -559,6 +596,9 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
                                         .cacheDescriptor(CU.cacheId(req.cacheName())));
                                 }
 
+                                for (CacheGroupDescriptor grpDesc : grpDescs)
+                                    exchActions.addCacheGroupToStop(grpDesc);
+
                                 if (op.type() == SnapshotOperationType.RESTORE)
                                     cctx.cache().onCustomEvent(new DynamicCacheChangeBatch(destroyRequests), topVer);
 
@@ -688,11 +728,11 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
         try {
             if (crd != null) {
-                for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                    if (cacheCtx.isLocal())
+                for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                    if (grp.isLocal())
                         continue;
 
-                    cacheCtx.topology().beforeExchange(this, !centralizedAff);
+                    grp.topology().beforeExchange(this, !centralizedAff);
                 }
             }
         }
@@ -706,28 +746,28 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
      * @throws IgniteCheckedException If failed.
      */
     private void updateTopologies(boolean crd) throws IgniteCheckedException {
-        for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (cacheCtx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            GridClientPartitionTopology clientTop = cctx.exchange().clearClientTopology(cacheCtx.cacheId());
+            GridClientPartitionTopology clientTop = cctx.exchange().clearClientTopology(grp.groupId());
 
             long updSeq = clientTop == null ? -1 : clientTop.lastUpdateSequence();
 
-            GridDhtPartitionTopology top = cacheCtx.topology();
+            GridDhtPartitionTopology top = grp.topology();
 
             if (crd) {
-                boolean updateTop = exchId.topologyVersion().equals(cacheCtx.startTopologyVersion());
+                boolean updateTop = exchId.topologyVersion().equals(grp.localStartVersion());
 
                 if (updateTop && clientTop != null)
                     top.update(this, clientTop.partitionMap(true), clientTop.updateCounters(false), Collections.<Integer>emptySet());
             }
 
-            top.updateTopologyVersion(exchId, this, updSeq, stopping(cacheCtx.cacheId()));
+            top.updateTopologyVersion(exchId, this, updSeq, cacheGroupStopping(grp.groupId()));
         }
 
         for (GridClientPartitionTopology top : cctx.exchange().clientTopologies())
-            top.updateTopologyVersion(exchId, this, -1, stopping(top.cacheId()));
+            top.updateTopologyVersion(exchId, this, -1, cacheGroupStopping(top.groupId()));
     }
 
     /**
@@ -821,15 +861,19 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
         if (crd != null) {
             if (crd.isLocal()) {
-                for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                    boolean updateTop = !cacheCtx.isLocal() &&
-                        exchId.topologyVersion().equals(cacheCtx.startTopologyVersion());
+                for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                    boolean updateTop = !grp.isLocal() &&
+                        exchId.topologyVersion().equals(grp.localStartVersion());
 
                     if (updateTop) {
                         for (GridClientPartitionTopology top : cctx.exchange().clientTopologies()) {
-                            if (top.cacheId() == cacheCtx.cacheId()) {
-                                cacheCtx.topology().update(this,
-                                    top.partitionMap(true),
+                            if (top.groupId() == grp.groupId()) {
+                                GridDhtPartitionFullMap fullMap = top.partitionMap(true);
+
+                                assert fullMap != null;
+
+                                grp.topology().update(this,
+                                    fullMap,
                                     top.updateCounters(false),
                                     Collections.<Integer>emptySet());
 
@@ -850,8 +894,8 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
         }
         else {
             if (centralizedAff) { // Last server node failed.
-                for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                    GridAffinityAssignmentCache aff = cacheCtx.affinity().affinityCache();
+                for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                    GridAffinityAssignmentCache aff = grp.affinity();
 
                     aff.initialize(topologyVersion(), aff.idealAssignment());
                 }
@@ -869,11 +913,11 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
         assert !cctx.kernalContext().clientNode();
 
-        for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (cacheCtx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            cacheCtx.preloader().onTopologyChanged(this);
+            grp.preloader().onTopologyChanged(this);
         }
 
         cctx.database().releaseHistoryForPreloading();
@@ -886,15 +930,20 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
         boolean topChanged = discoEvt.type() != EVT_DISCOVERY_CUSTOM_EVT || affChangeMsg != null;
 
         for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (cacheCtx.isLocal() || stopping(cacheCtx.cacheId()))
+            if (cacheCtx.isLocal() || cacheStopping(cacheCtx.cacheId()))
                 continue;
 
             if (topChanged) {
                 // Partition release future is done so we can flush the write-behind store.
                 cacheCtx.store().forceFlush();
             }
+        }
+
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal() || cacheGroupStopping(grp.groupId()))
+                continue;
 
-            cacheCtx.topology().beforeExchange(this, !centralizedAff);
+            grp.topology().beforeExchange(this, !centralizedAff);
         }
 
         cctx.database().beforeExchange(this);
@@ -1018,11 +1067,11 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
      *
      */
     private void onLeft() {
-        for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (cacheCtx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            cacheCtx.preloader().unwindUndeploys();
+            grp.preloader().unwindUndeploys();
         }
 
         cctx.mvcc().removeExplicitNodeLocks(exchId.nodeId(), exchId.topologyVersion());
@@ -1034,17 +1083,17 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
     private void warnNoAffinityNodes() {
         List<String> cachesWithoutNodes = null;
 
-        for (String name : cctx.cache().cacheNames()) {
-            if (discoCache.cacheAffinityNodes(name).isEmpty()) {
+        for (DynamicCacheDescriptor cacheDesc : cctx.cache().cacheDescriptors().values()) {
+            if (discoCache.cacheGroupAffinityNodes(cacheDesc.groupId()).isEmpty()) {
                 if (cachesWithoutNodes == null)
                     cachesWithoutNodes = new ArrayList<>();
 
-                cachesWithoutNodes.add(name);
+                cachesWithoutNodes.add(cacheDesc.cacheName());
 
                 // Fire event even if there is no client cache started.
                 if (cctx.gridEvents().isRecordable(EventType.EVT_CACHE_NODES_LEFT)) {
                     Event evt = new CacheEvent(
-                        name,
+                        cacheDesc.cacheName(),
                         cctx.localNode(),
                         cctx.localNode(),
                         "All server nodes have left the cluster.",
@@ -1106,10 +1155,18 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
     }
 
     /**
+     * @param grpId Cache group ID to check.
+     * @return {@code True} if cache group us stopping by this exchange.
+     */
+    private boolean cacheGroupStopping(int grpId) {
+        return exchActions != null && exchActions.cacheGroupStopping(grpId);
+    }
+
+    /**
      * @param cacheId Cache ID to check.
      * @return {@code True} if cache is stopping by this exchange.
      */
-    public boolean stopping(int cacheId) {
+    private boolean cacheStopping(int cacheId) {
         return exchActions != null && exchActions.cacheStopped(cacheId);
     }
 
@@ -1235,18 +1292,20 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
        }
 
         if (err == null && realExchange) {
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (cacheCtx.isLocal())
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (grp.isLocal())
                     continue;
 
                 try {
                     if (centralizedAff)
-                        cacheCtx.topology().initPartitions(this);
+                        grp.topology().initPartitions(this);
                 }
                 catch (IgniteInterruptedCheckedException e) {
                     U.error(log, "Failed to initialize partitions.", e);
                 }
+            }
 
+            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
                 GridCacheContext drCacheCtx = cacheCtx.isNear() ? cacheCtx.near().dht().context() : cacheCtx;
 
                 if (drCacheCtx.isDrEnabled()) {
@@ -1264,21 +1323,21 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
                 discoEvt.type() == EVT_NODE_JOINED)
                 detectLostPartitions();
 
-            Map<Integer, CacheValidation> m = new HashMap<>(cctx.cacheContexts().size());
+            Map<Integer, CacheValidation> m = new HashMap<>(cctx.cache().cacheGroups().size());
 
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                Collection<Integer> lostParts = cacheCtx.isLocal() ?
-                    Collections.<Integer>emptyList() : cacheCtx.topology().lostPartitions();
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                Collection<Integer> lostParts = grp.isLocal() ?
+                    Collections.<Integer>emptyList() : grp.topology().lostPartitions();
 
                 boolean valid = true;
 
-                if (cacheCtx.config().getTopologyValidator() != null && !CU.isSystemCache(cacheCtx.name()))
-                    valid = cacheCtx.config().getTopologyValidator().validate(discoEvt.topologyNodes());
+                if (grp.topologyValidator() != null && !grp.systemCache())
+                    valid = grp.topologyValidator().validate(discoEvt.topologyNodes());
 
-                m.put(cacheCtx.cacheId(), new CacheValidation(valid, lostParts));
+                m.put(grp.groupId(), new CacheValidation(valid, lostParts));
             }
 
-            cacheValidRes = m;
+            grpValidRes = m;
         }
 
         cctx.cache().onExchangeDone(exchId.topologyVersion(), exchActions, err, false);
@@ -1329,8 +1388,8 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
             initFut.onDone(err == null);
 
             if (exchId.isLeft()) {
-                for (GridCacheContext cacheCtx : cctx.cacheContexts())
-                    cacheCtx.config().getAffinity().removeNode(exchId.nodeId());
+                for (CacheGroupContext grp : cctx.cache().cacheGroups())
+                    grp.affinityFunction().removeNode(exchId.nodeId());
             }
 
             exchActions = null;
@@ -1379,16 +1438,18 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
             return new CacheInvalidStateException(
                 "Failed to perform cache operation (cluster is not activated): " + cctx.name());
 
-        PartitionLossPolicy partLossPlc = cctx.config().getPartitionLossPolicy();
+        CacheGroupContext grp = cctx.group();
 
-        if (cctx.needsRecovery() && !recovery) {
+        PartitionLossPolicy partLossPlc = grp.config().getPartitionLossPolicy();
+
+        if (grp.needsRecovery() && !recovery) {
             if (!read && (partLossPlc == READ_ONLY_SAFE || partLossPlc == READ_ONLY_ALL))
                 return new IgniteCheckedException("Failed to write to cache (cache is moved to a read-only state): " +
                     cctx.name());
         }
 
-        if (cctx.needsRecovery() || cctx.config().getTopologyValidator() != null) {
-            CacheValidation validation = cacheValidRes.get(cctx.cacheId());
+        if (grp.needsRecovery() || grp.topologyValidator() != null) {
+            CacheValidation validation = grpValidRes.get(grp.groupId());
 
             if (validation == null)
                 return null;
@@ -1397,7 +1458,7 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
                 return new IgniteCheckedException("Failed to perform cache operation " +
                     "(cache topology is not valid): " + cctx.name());
 
-            if (recovery || !cctx.needsRecovery())
+            if (recovery || !grp.needsRecovery())
                 return null;
 
             if (key != null) {
@@ -1629,9 +1690,9 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
         Map<Integer, Long> minCntrs = new HashMap<>();
 
         for (Map.Entry<UUID, GridDhtPartitionsSingleMessage> e : msgs.entrySet()) {
-            assert e.getValue().partitionUpdateCounters(top.cacheId()) != null;
+            assert e.getValue().partitionUpdateCounters(top.groupId()) != null;
 
-            for (Map.Entry<Integer, T2<Long, Long>> e0 : e.getValue().partitionUpdateCounters(top.cacheId()).entrySet()) {
+            for (Map.Entry<Integer, T2<Long, Long>> e0 : e.getValue().partitionUpdateCounters(top.groupId()).entrySet()) {
                 int p = e0.getKey();
 
                 UUID uuid = e.getKey();
@@ -1702,7 +1763,7 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
         Map<Integer, Map<Integer, Long>> partHistReserved0 = partHistReserved;
 
-        Map<Integer, Long> localReserved = partHistReserved0 != null ? partHistReserved0.get(top.cacheId()) : null;
+        Map<Integer, Long> localReserved = partHistReserved0 != null ? partHistReserved0.get(top.groupId()) : null;
 
         Set<Integer> haveHistory = new HashSet<>();
 
@@ -1723,7 +1784,7 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
                 if (localCntr != null && localCntr <= minCntr &&
                     maxCntrObj.nodes.contains(cctx.localNodeId())) {
-                    partHistSuppliers.put(cctx.localNodeId(), top.cacheId(), p, minCntr);
+                    partHistSuppliers.put(cctx.localNodeId(), top.groupId(), p, minCntr);
 
                     haveHistory.add(p);
 
@@ -1732,10 +1793,10 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
             }
 
             for (Map.Entry<UUID, GridDhtPartitionsSingleMessage> e0 : msgs.entrySet()) {
-                Long histCntr = e0.getValue().partitionHistoryCounters(top.cacheId()).get(p);
+                Long histCntr = e0.getValue().partitionHistoryCounters(top.groupId()).get(p);
 
                 if (histCntr != null && histCntr <= minCntr && maxCntrObj.nodes.contains(e0.getKey())) {
-                    partHistSuppliers.put(e0.getKey(), top.cacheId(), p, minCntr);
+                    partHistSuppliers.put(e0.getKey(), top.groupId(), p, minCntr);
 
                     haveHistory.add(p);
 
@@ -1756,7 +1817,7 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
             Set<UUID> nodesToReload = top.setOwners(p, e.getValue().nodes, haveHistory.contains(p), entryLeft == 0);
 
             for (UUID nodeId : nodesToReload)
-                partsToReload.put(nodeId, top.cacheId(), p);
+                partsToReload.put(nodeId, top.groupId(), p);
         }
     }
 
@@ -1768,24 +1829,32 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
             if (Thread.currentThread().isInterrupted())
                 return;
 
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (!cacheCtx.isLocal())
-                    cacheCtx.topology().detectLostPartitions(discoEvt);
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (!grp.isLocal())
+                    grp.topology().detectLostPartitions(discoEvt);
             }
         }
     }
 
     /**
-     *
+     * @param cacheNames Cache names.
      */
     private void resetLostPartitions(Collection<String> cacheNames) {
         synchronized (cctx.exchange().interruptLock()) {
             if (Thread.currentThread().isInterrupted())
                 return;
 
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (!cacheCtx.isLocal() && cacheNames.contains(cacheCtx.name()))
-                    cacheCtx.topology().resetLostPartitions();
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (grp.isLocal())
+                    continue;
+
+                for (String cacheName : cacheNames) {
+                    if (grp.hasCache(cacheName)) {
+                        grp.topology().resetLostPartitions();
+
+                        break;
+                    }
+                }
             }
         }
     }
@@ -1800,9 +1869,9 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
             assert partHistSuppliers.isEmpty();
 
             if (!crd.equals(discoCache.serverNodes().get(0))) {
-                for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                    if (!cacheCtx.isLocal())
-                        cacheCtx.topology().beforeExchange(this, !centralizedAff);
+                for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                    if (!grp.isLocal())
+                        grp.topology().beforeExchange(this, !centralizedAff);
                 }
             }
 
@@ -1811,13 +1880,13 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
                     GridDhtPartitionsSingleMessage msg0 = (GridDhtPartitionsSingleMessage)msg;
 
                     for (Map.Entry<Integer, GridDhtPartitionMap> entry : msg0.partitions().entrySet()) {
-                        Integer cacheId = entry.getKey();
-                        GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+                        Integer grpId = entry.getKey();
+                        CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-                        GridDhtPartitionTopology top = cacheCtx != null ? cacheCtx.topology() :
-                            cctx.exchange().clientTopology(cacheId, this);
+                        GridDhtPartitionTopology top = grp != null ? grp.topology() :
+                            cctx.exchange().clientTopology(grpId, this);
 
-                        Map<Integer, T2<Long, Long>> cntrs = msg0.partitionUpdateCounters(cacheId);
+                        Map<Integer, T2<Long, Long>> cntrs = msg0.partitionUpdateCounters(grpId);
 
                         if (cntrs != null)
                             top.applyUpdateCounters(cntrs);
@@ -1895,11 +1964,11 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
      */
     private void assignPartitionsStates() {
         if (cctx.database().persistenceEnabled()) {
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (cacheCtx.isLocal())
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (grp.isLocal())
                     continue;
 
-                assignPartitionStates(cacheCtx.topology());
+                assignPartitionStates(grp.topology());
             }
         }
     }
@@ -2028,20 +2097,20 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
         partHistSuppliers.putAll(msg.partitionHistorySuppliers());
 
         for (Map.Entry<Integer, GridDhtPartitionFullMap> entry : msg.partitions().entrySet()) {
-            Integer cacheId = entry.getKey();
+            Integer grpId = entry.getKey();
 
-            Map<Integer, T2<Long, Long>> cntrMap = msg.partitionUpdateCounters(cacheId);
+            Map<Integer, T2<Long, Long>> cntrMap = msg.partitionUpdateCounters(grpId);
 
-            GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+            CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-            if (cacheCtx != null)
-                cacheCtx.topology().update(this, entry.getValue(), cntrMap,
-                    msg.partsToReload(cctx.localNodeId(), cacheId));
+            if (grp != null)
+                grp.topology().update(this, entry.getValue(), cntrMap,
+                    msg.partsToReload(cctx.localNodeId(), grpId));
             else {
                 ClusterNode oldest = cctx.discovery().oldestAliveCacheServerNode(AffinityTopologyVersion.NONE);
 
                 if (oldest != null && oldest.isLocal())
-                    cctx.exchange().clientTopology(cacheId, this).update(this, entry.getValue(), cntrMap, Collections.<Integer>emptySet());
+                    cctx.exchange().clientTopology(grpId, this).update(this, entry.getValue(), cntrMap, Collections.<Integer>emptySet());
             }
         }
     }
@@ -2055,11 +2124,11 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
         msgs.put(node.id(), msg);
 
         for (Map.Entry<Integer, GridDhtPartitionMap> entry : msg.partitions().entrySet()) {
-            Integer cacheId = entry.getKey();
-            GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+            Integer grpId = entry.getKey();
+            CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-            GridDhtPartitionTopology top = cacheCtx != null ? cacheCtx.topology() :
-                cctx.exchange().clientTopology(cacheId, this);
+            GridDhtPartitionTopology top = grp != null ? grp.topology() :
+                cctx.exchange().clientTopology(grpId, this);
 
             top.update(exchId, entry.getValue());
         }
@@ -2211,13 +2280,13 @@ public class GridDhtPartitionsExchangeFuture extends GridFutureAdapter<AffinityT
 
                             List<ClusterNode> empty = Collections.emptyList();
 
-                            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                                List<List<ClusterNode>> affAssignment = new ArrayList<>(cacheCtx.affinity().partitions());
+                            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                                List<List<ClusterNode>> affAssignment = new ArrayList<>(grp.affinity().partitions());
 
-                                for (int i = 0; i < cacheCtx.affinity().partitions(); i++)
+                                for (int i = 0; i < grp.affinity().partitions(); i++)
                                     affAssignment.add(empty);
 
-                                cacheCtx.affinity().affinityCache().initialize(topologyVersion(), affAssignment);
+                                grp.affinity().initialize(topologyVersion(), affAssignment);
                             }
 
                             onDone(topologyVersion());

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java
index 94ad21e..2f8a531 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsFullMessage.java
@@ -125,6 +125,11 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
         this.partsToReload = partsToReload;
     }
 
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
     /**
      * @param compress {@code True} if it is possible to use compression for message.
      */
@@ -143,24 +148,26 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @return {@code True} if message contains full map for given cache.
      */
-    public boolean containsCache(int cacheId) {
-        return parts != null && parts.containsKey(cacheId);
+    public boolean containsGroup(int grpId) {
+        return parts != null && parts.containsKey(grpId);
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param fullMap Full partitions map.
      * @param dupDataCache Optional ID of cache with the same partition state map.
      */
-    public void addFullPartitionsMap(int cacheId, GridDhtPartitionFullMap fullMap, @Nullable Integer dupDataCache) {
+    public void addFullPartitionsMap(int grpId, GridDhtPartitionFullMap fullMap, @Nullable Integer dupDataCache) {
+        assert fullMap != null;
+
         if (parts == null)
             parts = new HashMap<>();
 
-        if (!parts.containsKey(cacheId)) {
-            parts.put(cacheId, fullMap);
+        if (!parts.containsKey(grpId)) {
+            parts.put(grpId, fullMap);
 
             if (dupDataCache != null) {
                 assert compress;
@@ -169,30 +176,29 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
                 if (dupPartsData == null)
                     dupPartsData = new HashMap<>();
 
-                dupPartsData.put(cacheId, dupDataCache);
+                dupPartsData.put(grpId, dupDataCache);
             }
         }
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param cntrMap Partition update counters.
      */
-    public void addPartitionUpdateCounters(int cacheId, Map<Integer, T2<Long, Long>> cntrMap) {
+    public void addPartitionUpdateCounters(int grpId, Map<Integer, T2<Long, Long>> cntrMap) {
         if (partCntrs == null)
             partCntrs = new IgniteDhtPartitionCountersMap();
 
-        partCntrs.putIfAbsent(cacheId, cntrMap);
+        partCntrs.putIfAbsent(grpId, cntrMap);
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @return Partition update counters.
      */
-    @Override public Map<Integer, T2<Long, Long>> partitionUpdateCounters(int cacheId) {
-        if (partCntrs != null) {
-            return partCntrs.get(cacheId);
-        }
+    @Override public Map<Integer, T2<Long, Long>> partitionUpdateCounters(int grpId) {
+        if (partCntrs != null)
+            return partCntrs.get(grpId);
 
         return Collections.emptyMap();
     }
@@ -207,11 +213,11 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
         return partHistSuppliers;
     }
 
-    public Set<Integer> partsToReload(UUID nodeId, int cacheId) {
+    public Set<Integer> partsToReload(UUID nodeId, int grpId) {
         if (partsToReload == null)
             return Collections.emptySet();
 
-        return partsToReload.get(nodeId, cacheId);
+        return partsToReload.get(nodeId, grpId);
     }
 
     /**
@@ -396,43 +402,43 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
         }
 
         switch (writer.state()) {
-            case 6:
+            case 5:
                 if (!writer.writeMap("dupPartsData", dupPartsData, MessageCollectionItemType.INT, MessageCollectionItemType.INT))
                     return false;
 
                 writer.incrementState();
 
-            case 7:
+            case 6:
                 if (!writer.writeByteArray("exsBytes", exsBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 8:
+            case 7:
                 if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 9:
+            case 8:
                 if (!writer.writeByteArray("partHistSuppliersBytes", partHistSuppliersBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 10:
+            case 9:
                 if (!writer.writeByteArray("partsBytes", partsBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 11:
+            case 10:
                 if (!writer.writeByteArray("partsToReloadBytes", partsToReloadBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 12:
+            case 11:
                 if (!writer.writeMessage("topVer", topVer))
                     return false;
 
@@ -454,7 +460,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
             return false;
 
         switch (reader.state()) {
-            case 6:
+            case 5:
                 dupPartsData = reader.readMap("dupPartsData", MessageCollectionItemType.INT, MessageCollectionItemType.INT, false);
 
                 if (!reader.isLastRead())
@@ -462,7 +468,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
 
                 reader.incrementState();
 
-            case 7:
+            case 6:
                 exsBytes = reader.readByteArray("exsBytes");
 
                 if (!reader.isLastRead())
@@ -470,7 +476,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
 
                 reader.incrementState();
 
-            case 8:
+            case 7:
                 partCntrsBytes = reader.readByteArray("partCntrsBytes");
 
                 if (!reader.isLastRead())
@@ -478,7 +484,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
 
                 reader.incrementState();
 
-            case 9:
+            case 8:
                 partHistSuppliersBytes = reader.readByteArray("partHistSuppliersBytes");
 
                 if (!reader.isLastRead())
@@ -486,7 +492,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
 
                 reader.incrementState();
 
-            case 10:
+            case 9:
                 partsBytes = reader.readByteArray("partsBytes");
 
                 if (!reader.isLastRead())
@@ -494,7 +500,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
 
                 reader.incrementState();
 
-            case 11:
+            case 10:
                 partsToReloadBytes = reader.readByteArray("partsToReloadBytes");
 
                 if (!reader.isLastRead())
@@ -502,7 +508,7 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
 
                 reader.incrementState();
 
-            case 12:
+            case 11:
                 topVer = reader.readMessage("topVer");
 
                 if (!reader.isLastRead())
@@ -520,11 +526,9 @@ public class GridDhtPartitionsFullMessage extends GridDhtPartitionsAbstractMessa
         return 46;
     }
 
-    //todo
-
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 13;
+        return 12;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
index 9222251..1e5ea14 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleMessage.java
@@ -111,6 +111,11 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
         this.compress = compress;
     }
 
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
     /**
      * @return {@code True} if sent from client node.
      */
@@ -142,23 +147,23 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param cntrMap Partition update counters.
      */
-    public void partitionUpdateCounters(int cacheId, Map<Integer, T2<Long, Long>> cntrMap) {
+    public void partitionUpdateCounters(int grpId, Map<Integer, T2<Long, Long>> cntrMap) {
         if (partCntrs == null)
             partCntrs = new HashMap<>();
 
-        partCntrs.put(cacheId, cntrMap);
+        partCntrs.put(grpId, cntrMap);
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @return Partition update counters.
      */
-    @Override public Map<Integer, T2<Long, Long>> partitionUpdateCounters(int cacheId) {
+    @Override public Map<Integer, T2<Long, Long>> partitionUpdateCounters(int grpId) {
         if (partCntrs != null) {
-            Map<Integer, T2<Long, Long>> res = partCntrs.get(cacheId);
+            Map<Integer, T2<Long, Long>> res = partCntrs.get(grpId);
 
             return res != null ? res : Collections.<Integer, T2<Long, Long>>emptyMap();
         }
@@ -167,35 +172,34 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param cntrMap Partition history counters.
      */
-    public void partitionHistoryCounters(int cacheId, Map<Integer, Long> cntrMap) {
+    public void partitionHistoryCounters(int grpId, Map<Integer, Long> cntrMap) {
         if (cntrMap.isEmpty())
             return;
 
         if (partHistCntrs == null)
             partHistCntrs = new HashMap<>();
 
-        partHistCntrs.put(cacheId, cntrMap);
+        partHistCntrs.put(grpId, cntrMap);
     }
 
     /**
      * @param cntrMap Partition history counters.
      */
-    public void partitionHistoryCounters(Map<Integer, Map<Integer, Long>> cntrMap) {
-        for (Map.Entry<Integer, Map<Integer, Long>> e : cntrMap.entrySet()) {
+    void partitionHistoryCounters(Map<Integer, Map<Integer, Long>> cntrMap) {
+        for (Map.Entry<Integer, Map<Integer, Long>> e : cntrMap.entrySet())
             partitionHistoryCounters(e.getKey(), e.getValue());
-        }
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @return Partition history counters.
      */
-    public Map<Integer, Long> partitionHistoryCounters(int cacheId) {
+    Map<Integer, Long> partitionHistoryCounters(int grpId) {
         if (partHistCntrs != null) {
-            Map<Integer, Long> res = partHistCntrs.get(cacheId);
+            Map<Integer, Long> res = partHistCntrs.get(grpId);
 
             return res != null ? res : Collections.<Integer, Long>emptyMap();
         }
@@ -351,37 +355,37 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
         }
 
         switch (writer.state()) {
-            case 6:
+            case 5:
                 if (!writer.writeBoolean("client", client))
                     return false;
 
                 writer.incrementState();
 
-            case 7:
+            case 6:
                 if (!writer.writeMap("dupPartsData", dupPartsData, MessageCollectionItemType.INT, MessageCollectionItemType.INT))
                     return false;
 
                 writer.incrementState();
 
-            case 8:
+            case 7:
                 if (!writer.writeByteArray("exBytes", exBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 9:
+            case 8:
                 if (!writer.writeByteArray("partCntrsBytes", partCntrsBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 10:
+            case 9:
                 if (!writer.writeByteArray("partHistCntrsBytes", partHistCntrsBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 11:
+            case 10:
                 if (!writer.writeByteArray("partsBytes", partsBytes))
                     return false;
 
@@ -403,7 +407,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
             return false;
 
         switch (reader.state()) {
-            case 6:
+            case 5:
                 client = reader.readBoolean("client");
 
                 if (!reader.isLastRead())
@@ -411,7 +415,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
 
                 reader.incrementState();
 
-            case 7:
+            case 6:
                 dupPartsData = reader.readMap("dupPartsData", MessageCollectionItemType.INT, MessageCollectionItemType.INT, false);
 
                 if (!reader.isLastRead())
@@ -419,7 +423,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
 
                 reader.incrementState();
 
-            case 8:
+            case 7:
                 exBytes = reader.readByteArray("exBytes");
 
                 if (!reader.isLastRead())
@@ -427,7 +431,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
 
                 reader.incrementState();
 
-            case 9:
+            case 8:
                 partCntrsBytes = reader.readByteArray("partCntrsBytes");
 
                 if (!reader.isLastRead())
@@ -435,7 +439,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
 
                 reader.incrementState();
 
-            case 10:
+            case 9:
                 partHistCntrsBytes = reader.readByteArray("partHistCntrsBytes");
 
                 if (!reader.isLastRead())
@@ -443,7 +447,7 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
 
                 reader.incrementState();
 
-            case 11:
+            case 10:
                 partsBytes = reader.readByteArray("partsBytes");
 
                 if (!reader.isLastRead())
@@ -461,11 +465,9 @@ public class GridDhtPartitionsSingleMessage extends GridDhtPartitionsAbstractMes
         return 47;
     }
 
-    //todo add ex
-
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 12;
+        return 11;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java
index b4ce939..4b80ee0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsSingleRequest.java
@@ -48,6 +48,11 @@ public class GridDhtPartitionsSingleRequest extends GridDhtPartitionsAbstractMes
     }
 
     /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
     @Override public Map<Integer, T2<Long, Long>> partitionUpdateCounters(int cacheId) {
         return Collections.emptyMap();
     }
@@ -89,7 +94,7 @@ public class GridDhtPartitionsSingleRequest extends GridDhtPartitionsAbstractMes
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 6;
+        return 5;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java
index 957f93b0..82eafc1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloader.java
@@ -23,30 +23,22 @@ import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.events.DiscoveryEvent;
-import org.apache.ignite.events.Event;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.NodeStoppingException;
-import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
-import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
 import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
-import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
 import org.apache.ignite.internal.processors.cache.GridCachePreloaderAdapter;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
-import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffinityAssignmentRequest;
-import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffinityAssignmentResponse;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtFuture;
-import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
@@ -61,11 +53,8 @@ import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.GPC;
 import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.lang.IgniteBiInClosure;
 import org.apache.ignite.lang.IgnitePredicate;
-import org.apache.ignite.lang.IgniteUuid;
 import org.jetbrains.annotations.Nullable;
-import org.jsr166.ConcurrentLinkedDeque8;
 
 import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_PART_DATA_LOST;
 import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_PART_UNLOADED;
@@ -77,7 +66,6 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
 import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.MOVING;
 import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
 import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.RENTING;
-import static org.apache.ignite.internal.util.GridConcurrentFactory.newMap;
 
 /**
  * DHT cache preloader.
@@ -89,9 +77,6 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
     /** */
     private GridDhtPartitionTopology top;
 
-    /** Force key futures. */
-    private final ConcurrentMap<IgniteUuid, GridDhtForceKeysFuture<?, ?>> forceKeyFuts = newMap();
-
     /** Partition suppliers. */
     private GridDhtPartitionSupplier supplier;
 
@@ -114,47 +99,15 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
     private final AtomicInteger partsEvictOwning = new AtomicInteger();
 
     /** */
-    private volatile boolean stopping;
-
-    /** */
     private boolean stopped;
 
-    /** Discovery listener. */
-    private final GridLocalEventListener discoLsnr = new GridLocalEventListener() {
-        @Override public void onEvent(Event evt) {
-            if (!enterBusy())
-                return;
-
-            DiscoveryEvent e = (DiscoveryEvent)evt;
-
-            try {
-                ClusterNode loc = cctx.localNode();
-
-                assert e.type() == EVT_NODE_JOINED || e.type() == EVT_NODE_LEFT || e.type() == EVT_NODE_FAILED;
-
-                final ClusterNode n = e.eventNode();
-
-                assert !loc.id().equals(n.id());
-
-                for (GridDhtForceKeysFuture<?, ?> f : forceKeyFuts.values())
-                    f.onDiscoveryEvent(e);
-
-                assert e.type() != EVT_NODE_JOINED || n.order() > loc.order() : "Node joined with smaller-than-local " +
-                    "order [newOrder=" + n.order() + ", locOrder=" + loc.order() + ']';
-            }
-            finally {
-                leaveBusy();
-            }
-        }
-    };
-
     /**
-     * @param cctx Cache context.
+     * @param grp Cache group.
      */
-    public GridDhtPreloader(GridCacheContext<?, ?> cctx) {
-        super(cctx);
+    public GridDhtPreloader(CacheGroupContext grp) {
+        super(grp);
 
-        top = cctx.dht().topology();
+        top = grp.topology();
 
         startFut = new GridFutureAdapter<>();
     }
@@ -164,37 +117,10 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
         if (log.isDebugEnabled())
             log.debug("Starting DHT rebalancer...");
 
-        cctx.io().addHandler(cctx.cacheId(), GridDhtForceKeysRequest.class,
-            new MessageHandler<GridDhtForceKeysRequest>() {
-                @Override public void onMessage(ClusterNode node, GridDhtForceKeysRequest msg) {
-                    processForceKeysRequest(node, msg);
-                }
-            });
-
-        cctx.io().addHandler(cctx.cacheId(), GridDhtForceKeysResponse.class,
-            new MessageHandler<GridDhtForceKeysResponse>() {
-                @Override public void onMessage(ClusterNode node, GridDhtForceKeysResponse msg) {
-                    processForceKeyResponse(node, msg);
-                }
-            });
-
-        if (!cctx.kernalContext().clientNode()) {
-            cctx.io().addHandler(cctx.cacheId(), GridDhtAffinityAssignmentRequest.class,
-                new MessageHandler<GridDhtAffinityAssignmentRequest>() {
-                    @Override protected void onMessage(ClusterNode node, GridDhtAffinityAssignmentRequest msg) {
-                        processAffinityAssignmentRequest(node, msg);
-                    }
-                });
-        }
-
-        cctx.shared().affinity().onCacheCreated(cctx);
-
-        supplier = new GridDhtPartitionSupplier(cctx);
-        demander = new GridDhtPartitionDemander(cctx);
+        supplier = new GridDhtPartitionSupplier(grp);
+        demander = new GridDhtPartitionDemander(grp);
 
         demander.start();
-
-        cctx.events().addListener(discoLsnr, EVT_NODE_JOINED, EVT_NODE_LEFT, EVT_NODE_FAILED);
     }
 
     /** {@inheritDoc} */
@@ -213,10 +139,6 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
         if (log.isDebugEnabled())
             log.debug("DHT rebalancer onKernalStop callback.");
 
-        stopping = true;
-
-        cctx.events().removeListener(discoLsnr);
-
         // Acquire write busy lock.
         busyLock.writeLock().lock();
 
@@ -227,11 +149,6 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
             if (demander != null)
                 demander.stop();
 
-            IgniteCheckedException err = stopError();
-
-            for (GridDhtForceKeysFuture fut : forceKeyFuts.values())
-                fut.onDone(err);
-
             top = null;
 
             stopped = true;
@@ -266,25 +183,27 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
     /** {@inheritDoc} */
     @Override public GridDhtPreloaderAssignments assign(GridDhtPartitionsExchangeFuture exchFut) {
         // No assignments for disabled preloader.
-        GridDhtPartitionTopology top = cctx.dht().topology();
+        GridDhtPartitionTopology top = grp.topology();
 
-        if (!cctx.rebalanceEnabled() || !cctx.shared().kernalContext().state().active())
+        if (!grp.rebalanceEnabled() || !grp.shared().kernalContext().state().active())
             return new GridDhtPreloaderAssignments(exchFut, top.topologyVersion());
 
-        int partCnt = cctx.affinity().partitions();
+        int partCnt = grp.affinity().partitions();
 
         assert exchFut.forcePreload() || exchFut.dummyReassign() ||
             exchFut.exchangeId().topologyVersion().equals(top.topologyVersion()) :
             "Topology version mismatch [exchId=" + exchFut.exchangeId() +
-                ", cache=" + cctx.name() +
+                ", grp=" + grp.name() +
                 ", topVer=" + top.topologyVersion() + ']';
 
         GridDhtPreloaderAssignments assigns = new GridDhtPreloaderAssignments(exchFut, top.topologyVersion());
 
         AffinityTopologyVersion topVer = assigns.topologyVersion();
 
+        AffinityAssignment aff = grp.affinity().cachedAffinity(topVer);
+
         for (int p = 0; p < partCnt; p++) {
-            if (cctx.shared().exchange().hasPendingExchange()) {
+            if (ctx.exchange().hasPendingExchange()) {
                 if (log.isDebugEnabled())
                     log.debug("Skipping assignments creation, exchange worker has pending assignments: " +
                         exchFut.exchangeId());
@@ -295,7 +214,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
             }
 
             // If partition belongs to local node.
-            if (cctx.affinity().partitionLocalNode(p, topVer)) {
+            if (aff.get(p).contains(ctx.localNode())) {
                 GridDhtLocalPartition part = top.localPartition(p, topVer, true, true);
 
                 assert part != null;
@@ -303,11 +222,11 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
 
                 ClusterNode histSupplier = null;
 
-                if (cctx.shared().database().persistenceEnabled()) {
-                    UUID nodeId = exchFut.partitionHistorySupplier(cctx.cacheId(), p);
+                if (ctx.database().persistenceEnabled()) {
+                    UUID nodeId = exchFut.partitionHistorySupplier(grp.groupId(), p);
 
                     if (nodeId != null)
-                        histSupplier = cctx.discovery().node(nodeId);
+                        histSupplier = ctx.discovery().node(nodeId);
                 }
 
                 if (histSupplier != null) {
@@ -318,7 +237,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                         continue; // For.
                     }
 
-                    assert cctx.shared().database().persistenceEnabled();
+                    assert ctx.database().persistenceEnabled();
                     assert remoteOwners(p, topVer).contains(histSupplier) : remoteOwners(p, topVer);
 
                     GridDhtPartitionDemandMessage msg = assigns.get(histSupplier);
@@ -327,13 +246,13 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                         assigns.put(histSupplier, msg = new GridDhtPartitionDemandMessage(
                             top.updateSequence(),
                             exchFut.exchangeId().topologyVersion(),
-                            cctx.cacheId()));
+                            grp.groupId()));
                     }
 
                     msg.addPartition(p, true);
                 }
                 else {
-                    if (cctx.shared().database().persistenceEnabled()) {
+                    if (ctx.database().persistenceEnabled()) {
                         if (part.state() == RENTING || part.state() == EVICTED) {
                             try {
                                 part.rent(false).get();
@@ -360,13 +279,13 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                     if (picked.isEmpty()) {
                         top.own(part);
 
-                        if (cctx.events().isRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) {
+                        if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_DATA_LOST)) {
                             DiscoveryEvent discoEvt = exchFut.discoveryEvent();
 
-                            cctx.events().addPreloadEvent(p,
-                                EVT_CACHE_REBALANCE_PART_DATA_LOST, discoEvt.eventNode(),
-                                discoEvt.type(), discoEvt.timestamp());
-                        }
+                        grp.addRebalanceEvent(p,
+                            EVT_CACHE_REBALANCE_PART_DATA_LOST, discoEvt.eventNode(),
+                            discoEvt.type(), discoEvt.timestamp());
+                    }
 
                         if (log.isDebugEnabled())
                             log.debug("Owning partition as there are no other owners: " + part);
@@ -376,12 +295,12 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
 
                         GridDhtPartitionDemandMessage msg = assigns.get(n);
 
-                        if (msg == null) {
-                            assigns.put(n, msg = new GridDhtPartitionDemandMessage(
-                                top.updateSequence(),
-                                exchFut.exchangeId().topologyVersion(),
-                                cctx.cacheId()));
-                        }
+                    if (msg == null) {
+                        assigns.put(n, msg = new GridDhtPartitionDemandMessage(
+                            top.updateSequence(),
+                            exchFut.exchangeId().topologyVersion(),
+                            grp.groupId()));
+                    }
 
                         msg.addPartition(p, false);
                     }
@@ -403,7 +322,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
      * @return Picked owners.
      */
     private Collection<ClusterNode> pickedOwners(int p, AffinityTopologyVersion topVer) {
-        Collection<ClusterNode> affNodes = cctx.affinity().nodesByPartition(p, topVer);
+        Collection<ClusterNode> affNodes = grp.affinity().cachedAffinity(topVer).get(p);
 
         int affCnt = affNodes.size();
 
@@ -429,7 +348,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
      * @return Nodes owning this partition.
      */
     private Collection<ClusterNode> remoteOwners(int p, AffinityTopologyVersion topVer) {
-        return F.view(cctx.dht().topology().owners(p, topVer), F.remoteNodes(cctx.nodeId()));
+        return F.view(grp.topology().owners(p, topVer), F.remoteNodes(ctx.localNodeId()));
     }
 
     /** {@inheritDoc} */
@@ -468,7 +387,6 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
     /** {@inheritDoc} */
     @Override public Runnable addAssignments(GridDhtPreloaderAssignments assignments,
         boolean forceRebalance,
-        Collection<String> caches,
         int cnt,
         Runnable next,
         @Nullable GridFutureAdapter<Boolean> forcedRebFut) {
@@ -484,12 +402,12 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
 
     /** {@inheritDoc} */
     @Override public IgniteInternalFuture<?> syncFuture() {
-        return cctx.kernalContext().clientNode() ? startFut : demander.syncFuture();
+        return ctx.kernalContext().clientNode() ? startFut : demander.syncFuture();
     }
 
     /** {@inheritDoc} */
     @Override public IgniteInternalFuture<Boolean> rebalanceFuture() {
-        return cctx.kernalContext().clientNode() ? new GridFinishedFuture<>(true) : demander.rebalanceFuture();
+        return ctx.kernalContext().clientNode() ? new GridFinishedFuture<>(true) : demander.rebalanceFuture();
     }
 
     /**
@@ -516,171 +434,6 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
     }
 
     /**
-     * @param node Node originated request.
-     * @param msg Force keys message.
-     */
-    private void processForceKeysRequest(final ClusterNode node, final GridDhtForceKeysRequest msg) {
-        IgniteInternalFuture<?> fut = cctx.mvcc().finishKeys(msg.keys(), msg.cacheId(), msg.topologyVersion());
-
-        if (fut.isDone())
-            processForceKeysRequest0(node, msg);
-        else
-            fut.listen(new CI1<IgniteInternalFuture<?>>() {
-                @Override public void apply(IgniteInternalFuture<?> t) {
-                    processForceKeysRequest0(node, msg);
-                }
-            });
-    }
-
-    /**
-     * @param node Node originated request.
-     * @param msg Force keys message.
-     */
-    private void processForceKeysRequest0(ClusterNode node, GridDhtForceKeysRequest msg) {
-        if (!enterBusy())
-            return;
-
-        try {
-            ClusterNode loc = cctx.localNode();
-
-            GridDhtForceKeysResponse res = new GridDhtForceKeysResponse(
-                cctx.cacheId(),
-                msg.futureId(),
-                msg.miniId(),
-                cctx.deploymentEnabled());
-
-            for (KeyCacheObject k : msg.keys()) {
-                int p = cctx.affinity().partition(k);
-
-                GridDhtLocalPartition locPart = top.localPartition(p, AffinityTopologyVersion.NONE, false);
-
-                // If this node is no longer an owner.
-                if (locPart == null && !top.owners(p).contains(loc)) {
-                    res.addMissed(k);
-
-                    continue;
-                }
-
-                GridCacheEntryEx entry = null;
-
-                while (true) {
-                    try {
-                        entry = cctx.dht().entryEx(k);
-
-                        entry.unswap();
-
-                        GridCacheEntryInfo info = entry.info();
-
-                        if (info == null) {
-                            assert entry.obsolete() : entry;
-
-                            continue;
-                        }
-
-                        if (!info.isNew())
-                            res.addInfo(info);
-
-                        cctx.evicts().touch(entry, msg.topologyVersion());
-
-                        break;
-                    }
-                    catch (GridCacheEntryRemovedException ignore) {
-                        if (log.isDebugEnabled())
-                            log.debug("Got removed entry: " + k);
-                    }
-                    catch (GridDhtInvalidPartitionException ignore) {
-                        if (log.isDebugEnabled())
-                            log.debug("Local node is no longer an owner: " + p);
-
-                        res.addMissed(k);
-
-                        break;
-                    }
-                }
-            }
-
-            if (log.isDebugEnabled())
-                log.debug("Sending force key response [node=" + node.id() + ", res=" + res + ']');
-
-            cctx.io().send(node, res, cctx.ioPolicy());
-        }
-        catch (ClusterTopologyCheckedException ignore) {
-            if (log.isDebugEnabled())
-                log.debug("Received force key request form failed node (will ignore) [nodeId=" + node.id() +
-                    ", req=" + msg + ']');
-        }
-        catch (IgniteCheckedException e) {
-            U.error(log, "Failed to reply to force key request [nodeId=" + node.id() + ", req=" + msg + ']', e);
-        }
-        finally {
-            leaveBusy();
-        }
-    }
-
-    /**
-     * @param node Node.
-     * @param msg Message.
-     */
-    private void processForceKeyResponse(ClusterNode node, GridDhtForceKeysResponse msg) {
-        if (!enterBusy())
-            return;
-
-        try {
-            GridDhtForceKeysFuture<?, ?> f = forceKeyFuts.get(msg.futureId());
-
-            if (f != null)
-                f.onResult(msg);
-            else if (log.isDebugEnabled())
-                log.debug("Receive force key response for unknown future (is it duplicate?) [nodeId=" + node.id() +
-                    ", res=" + msg + ']');
-        }
-        finally {
-            leaveBusy();
-        }
-    }
-
-    /**
-     * @param node Node.
-     * @param req Request.
-     */
-    private void processAffinityAssignmentRequest(final ClusterNode node,
-        final GridDhtAffinityAssignmentRequest req) {
-        final AffinityTopologyVersion topVer = req.topologyVersion();
-
-        if (log.isDebugEnabled())
-            log.debug("Processing affinity assignment request [node=" + node + ", req=" + req + ']');
-
-        cctx.affinity().affinityReadyFuture(req.topologyVersion()).listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() {
-            @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> fut) {
-                if (log.isDebugEnabled())
-                    log.debug("Affinity is ready for topology version, will send response [topVer=" + topVer +
-                        ", node=" + node + ']');
-
-                AffinityAssignment assignment = cctx.affinity().assignment(topVer);
-
-                GridDhtAffinityAssignmentResponse res = new GridDhtAffinityAssignmentResponse(
-                    req.futureId(),
-                    cctx.cacheId(),
-                    topVer,
-                    assignment.assignment());
-
-                if (cctx.affinity().affinityCache().centralizedAffinityFunction()) {
-                    assert assignment.idealAssignment() != null;
-
-                    res.idealAffinityAssignment(assignment.idealAssignment());
-                }
-
-                try {
-                    cctx.io().send(node, res, AFFINITY_POOL);
-                }
-                catch (IgniteCheckedException e) {
-                    U.error(log, "Failed to send affinity assignment response to remote node [node=" + node + ']', e);
-                }
-            }
-        });
-    }
-
-    /**
      * Resends partitions on partition evict within configured timeout.
      *
      * @param part Evicted partition.
@@ -693,11 +446,11 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
         try {
             top.onEvicted(part, updateSeq);
 
-            if (cctx.events().isRecordable(EVT_CACHE_REBALANCE_PART_UNLOADED))
-                cctx.events().addUnloadEvent(part.id());
+            if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_UNLOADED))
+                grp.addUnloadEvent(part.id());
 
             if (updateSeq)
-                cctx.shared().exchange().scheduleResendPartitions();
+                ctx.exchange().scheduleResendPartitions();
         }
         finally {
             leaveBusy();
@@ -706,7 +459,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
 
     /** {@inheritDoc} */
     @Override public boolean needForceKeys() {
-        if (cctx.rebalanceEnabled()) {
+        if (grp.rebalanceEnabled()) {
             IgniteInternalFuture<Boolean> rebalanceFut = rebalanceFuture();
 
             if (rebalanceFut.isDone() && Boolean.TRUE.equals(rebalanceFut.result()))
@@ -717,12 +470,13 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
     }
 
     /** {@inheritDoc} */
-    @Override public IgniteInternalFuture<Object> request(GridNearAtomicAbstractUpdateRequest req,
+    @Override public GridDhtFuture<Object> request(GridCacheContext cctx,
+        GridNearAtomicAbstractUpdateRequest req,
         AffinityTopologyVersion topVer) {
         if (!needForceKeys())
             return null;
 
-        return request0(req.keys(), topVer);
+        return request0(cctx, req.keys(), topVer);
     }
 
     /**
@@ -730,21 +484,27 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
      * @return Future for request.
      */
     @SuppressWarnings({"unchecked", "RedundantCast"})
-    @Override public GridDhtFuture<Object> request(Collection<KeyCacheObject> keys, AffinityTopologyVersion topVer) {
+    @Override public GridDhtFuture<Object> request(GridCacheContext cctx,
+        Collection<KeyCacheObject> keys,
+        AffinityTopologyVersion topVer) {
         if (!needForceKeys())
             return null;
 
-        return request0(keys, topVer);
+        return request0(cctx, keys, topVer);
     }
 
     /**
+     * @param cctx Cache context.
      * @param keys Keys to request.
      * @param topVer Topology version.
      * @return Future for request.
      */
     @SuppressWarnings({"unchecked", "RedundantCast"})
-    private GridDhtFuture<Object> request0(Collection<KeyCacheObject> keys, AffinityTopologyVersion topVer) {
-        final GridDhtForceKeysFuture<?, ?> fut = new GridDhtForceKeysFuture<>(cctx, topVer, keys, this);
+    private GridDhtFuture<Object> request0(GridCacheContext cctx, Collection<KeyCacheObject> keys, AffinityTopologyVersion topVer) {
+        if (cctx.isNear())
+            cctx = cctx.near().dht().context();
+
+        final GridDhtForceKeysFuture<?, ?> fut = new GridDhtForceKeysFuture<>(cctx, topVer, keys);
 
         IgniteInternalFuture<?> topReadyFut = cctx.affinity().affinityReadyFuturex(topVer);
 
@@ -754,7 +514,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
             if (topReadyFut == null)
                 startFut.listen(new CI1<IgniteInternalFuture<?>>() {
                     @Override public void apply(IgniteInternalFuture<?> syncFut) {
-                        cctx.kernalContext().closure().runLocalSafe(
+                        ctx.kernalContext().closure().runLocalSafe(
                             new GridPlainRunnable() {
                                 @Override public void run() {
                                     fut.init();
@@ -791,46 +551,19 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
         demandLock.writeLock().lock();
 
         try {
-            cctx.deploy().unwind(cctx);
+            grp.unwindUndeploys();
         }
         finally {
             demandLock.writeLock().unlock();
         }
     }
 
-    /**
-     * Adds future to future map.
-     *
-     * @param fut Future to add.
-     * @return {@code False} if node cache is stopping and future was completed with error.
-     */
-    boolean addFuture(GridDhtForceKeysFuture<?, ?> fut) {
-        forceKeyFuts.put(fut.futureId(), fut);
-
-        if (stopping) {
-            fut.onDone(stopError());
-
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Removes future from future map.
-     *
-     * @param fut Future to remove.
-     */
-    void remoteFuture(GridDhtForceKeysFuture<?, ?> fut) {
-        forceKeyFuts.remove(fut.futureId(), fut);
-    }
-
     /** {@inheritDoc} */
     @Override public void evictPartitionAsync(GridDhtLocalPartition part) {
         partsToEvict.putIfAbsent(part.id(), part);
 
         if (partsEvictOwning.get() == 0 && partsEvictOwning.compareAndSet(0, 1)) {
-            cctx.closures().callLocalSafe(new GPC<Boolean>() {
+            ctx.kernalContext().closure().callLocalSafe(new GPC<Boolean>() {
                 @Override public Boolean call() {
                     boolean locked = true;
 
@@ -851,7 +584,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                                         partsToEvict.put(part.id(), part);
                                 }
                                 catch (Throwable ex) {
-                                    if (cctx.kernalContext().isStopping()) {
+                                    if (ctx.kernalContext().isStopping()) {
                                         LT.warn(log, ex, "Partition eviction failed (current node is stopping).",
                                             false,
                                             true);
@@ -886,44 +619,6 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
 
     /** {@inheritDoc} */
     @Override public void dumpDebugInfo() {
-        if (!forceKeyFuts.isEmpty()) {
-            U.warn(log, "Pending force key futures [cache=" + cctx.name() + "]:");
-
-            for (GridDhtForceKeysFuture fut : forceKeyFuts.values())
-                U.warn(log, ">>> " + fut);
-        }
-
         supplier.dumpDebugInfo();
     }
-
-    /**
-     *
-     */
-    private abstract class MessageHandler<M> implements IgniteBiInClosure<UUID, M> {
-        /** */
-        private static final long serialVersionUID = 0L;
-
-        /** {@inheritDoc} */
-        @Override public void apply(UUID nodeId, M msg) {
-            ClusterNode node = cctx.node(nodeId);
-
-            if (node == null) {
-                if (log.isDebugEnabled())
-                    log.debug("Received message from failed node [node=" + nodeId + ", msg=" + msg + ']');
-
-                return;
-            }
-
-            if (log.isDebugEnabled())
-                log.debug("Received message from node [node=" + nodeId + ", msg=" + msg + ']');
-
-            onMessage(node, msg);
-        }
-
-        /**
-         * @param node Node.
-         * @param msg Message.
-         */
-        protected abstract void onMessage(ClusterNode node, M msg);
-    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
index 2d5c8a5..e6c5c10 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
@@ -103,7 +103,7 @@ public class GridNearAtomicCache<K, V> extends GridNearCacheAdapter<K, V> {
     @Override public void start() throws IgniteCheckedException {
         super.start();
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearGetResponse.class, new CI2<UUID, GridNearGetResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearGetResponse.class, new CI2<UUID, GridNearGetResponse>() {
             @Override public void apply(UUID nodeId, GridNearGetResponse res) {
                 processGetResponse(nodeId, res);
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java
index f8240f3..5b53935 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearCacheAdapter.java
@@ -86,11 +86,23 @@ public abstract class GridNearCacheAdapter<K, V> extends GridDistributedCacheAda
      * @param ctx Context.
      */
     protected GridNearCacheAdapter(GridCacheContext<K, V> ctx) {
-        super(ctx, ctx.config().getNearConfiguration().getNearStartSize());
+        super(ctx);
     }
 
     /** {@inheritDoc} */
-    @Override protected GridCacheMapEntryFactory entryFactory() {
+    @Override public void start() throws IgniteCheckedException {
+        if (map == null) {
+            map = new GridCacheLocalConcurrentMap(
+                ctx,
+                entryFactory(),
+                ctx.config().getNearConfiguration().getNearStartSize());
+        }
+    }
+
+    /**
+     * @return Entry factory.
+     */
+    private GridCacheMapEntryFactory entryFactory() {
         return new GridCacheMapEntryFactory() {
             @Override public GridCacheMapEntry create(
                 GridCacheContext ctx,

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
index 757bfbd..6092511 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetRequest.java
@@ -32,7 +32,7 @@ import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -50,7 +50,7 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Get request.
  */
-public class GridNearGetRequest extends GridCacheMessage implements GridCacheDeployable,
+public class GridNearGetRequest extends GridCacheIdMessage implements GridCacheDeployable,
     GridCacheVersionable {
     /** */
     private static final long serialVersionUID = 0L;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetResponse.java
index 50af754..b4e4424 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearGetResponse.java
@@ -28,7 +28,7 @@ import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersionable;
@@ -45,7 +45,7 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Get response.
  */
-public class GridNearGetResponse extends GridCacheMessage implements GridCacheDeployable,
+public class GridNearGetResponse extends GridCacheIdMessage implements GridCacheDeployable,
     GridCacheVersionable {
     /** */
     private static final long serialVersionUID = 0L;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
index f4ce1ac..edddf7d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
@@ -58,6 +58,7 @@ import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiClosure;
+import org.apache.ignite.lang.IgniteInClosure;
 import org.apache.ignite.transactions.TransactionDeadlockException;
 import org.apache.ignite.transactions.TransactionTimeoutException;
 import org.jetbrains.annotations.Nullable;
@@ -159,12 +160,20 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
 
             if (e instanceof IgniteTxRollbackCheckedException) {
                 if (marked) {
-                    try {
-                        tx.rollback();
-                    }
-                    catch (IgniteCheckedException ex) {
-                        U.error(log, "Failed to automatically rollback transaction: " + tx, ex);
-                    }
+                    tx.rollbackAsync().listen(new IgniteInClosure<IgniteInternalFuture<IgniteInternalTx>>() {
+                        @Override public void apply(IgniteInternalFuture<IgniteInternalTx> fut) {
+                            try {
+                                fut.get();
+                            }
+                            catch (IgniteCheckedException e) {
+                                U.error(log, "Failed to automatically rollback transaction: " + tx, e);
+                            }
+
+                            onComplete();
+                        }
+                    });
+
+                    return;
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
index 0faa8ec..00ff4bb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetRequest.java
@@ -23,7 +23,7 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -35,7 +35,7 @@ import org.jetbrains.annotations.NotNull;
 /**
  *
  */
-public class GridNearSingleGetRequest extends GridCacheMessage implements GridCacheDeployable {
+public class GridNearSingleGetRequest extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetResponse.java
index 554fca1..2cb75c2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearSingleGetResponse.java
@@ -25,7 +25,7 @@ import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -37,7 +37,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  *
  */
-public class GridNearSingleGetResponse extends GridCacheMessage implements GridCacheDeployable {
+public class GridNearSingleGetResponse extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -115,7 +115,7 @@ public class GridNearSingleGetResponse extends GridCacheMessage implements GridC
      * @return Topology version.
      */
     @Override public AffinityTopologyVersion topologyVersion() {
-        return topVer;
+        return topVer != null ? topVer : super.topologyVersion();
     }
 
     /**


[11/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
index 955ca69..a25d794 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRowAdapter.java
@@ -19,13 +19,12 @@ package org.apache.ignite.internal.processors.cache.database;
 
 import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.IncompleteCacheObject;
 import org.apache.ignite.internal.processors.cache.IncompleteObject;
@@ -94,25 +93,26 @@ public class CacheDataRowAdapter implements CacheDataRow {
     /**
      * Read row from data pages.
      *
-     * @param cctx Cache context.
+     * @param grp Cache group.
      * @param rowData Required row data.
      * @throws IgniteCheckedException If failed.
      */
-    public final void initFromLink(GridCacheContext<?, ?> cctx, RowData rowData) throws IgniteCheckedException {
-        initFromLink(cctx, cctx.shared(), cctx.memoryPolicy().pageMemory(), rowData);
+    public final void initFromLink(CacheGroupContext grp, RowData rowData) throws IgniteCheckedException {
+        initFromLink(grp, grp.shared(), grp.memoryPolicy().pageMemory(), rowData);
     }
 
     /**
      * Read row from data pages.
      * Can be called with cctx == null, if cache instance is unknown, but its ID is stored in the data row.
      *
-     * @param cctx Cctx.
+     * @param grp Cache group.
      * @param sharedCtx Shared context.
      * @param pageMem Page memory.
      * @param rowData Row data.
+     * @throws IgniteCheckedException If failed.
      */
     public final void initFromLink(
-        @Nullable GridCacheContext<?, ?> cctx,
+        @Nullable CacheGroupContext grp,
         GridCacheSharedContext<?, ?> sharedCtx,
         PageMemory pageMem,
         RowData rowData)
@@ -120,14 +120,9 @@ public class CacheDataRowAdapter implements CacheDataRow {
         assert link != 0 : "link";
         assert key == null : "key";
 
-        CacheObjectContext coctx = null;
-
-        if (cctx != null) {
-            cacheId = cctx.memoryPolicy().config().getPageEvictionMode() == DataPageEvictionMode.DISABLED ?
-                cctx.cacheId() : 0; // Force cacheId reading for evictable memory policies.
+        CacheObjectContext coctx = grp != null ?  grp.cacheObjectContext() : null;
 
-            coctx = cctx.cacheObjectContext();
-        }
+        boolean readCacheId = grp == null || grp.storeCacheIdInDataPage();
 
         long nextLink = link;
         IncompleteObject<?> incomplete = null;
@@ -135,10 +130,16 @@ public class CacheDataRowAdapter implements CacheDataRow {
 
         do {
             final long pageId = pageId(nextLink);
-            final long page = pageMem.acquirePage(cacheId, pageId);
+
+            // Group is null if try evict page, with persistence evictions should be disabled.
+            assert grp != null || !sharedCtx.database().persistenceEnabled();
+
+            int grpId = grp != null ? grp.groupId() : 0;
+
+            final long page = pageMem.acquirePage(grpId, pageId);
 
             try {
-                long pageAddr = pageMem.readLock(cacheId, pageId, page); // Non-empty data page must not be recycled.
+                long pageAddr = pageMem.readLock(grpId, pageId, page); // Non-empty data page must not be recycled.
 
                 assert pageAddr != 0L : nextLink;
 
@@ -154,7 +155,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
                     if (first) {
                         if (nextLink == 0) {
                             // Fast path for a single page row.
-                            readFullRow(sharedCtx, coctx, pageAddr + data.offset(), rowData);
+                            readFullRow(sharedCtx, coctx, pageAddr + data.offset(), rowData, readCacheId);
 
                             return;
                         }
@@ -169,17 +170,17 @@ public class CacheDataRowAdapter implements CacheDataRow {
 
                     boolean keyOnly = rowData == RowData.KEY_ONLY;
 
-                    incomplete = readFragment(sharedCtx, coctx, buf, keyOnly, incomplete);
+                    incomplete = readFragment(sharedCtx, coctx, buf, keyOnly, readCacheId, incomplete);
 
                     if (keyOnly && key != null)
                         return;
                 }
                 finally {
-                    pageMem.readUnlock(cacheId, pageId, page);
+                    pageMem.readUnlock(grpId, pageId, page);
                 }
             }
             finally {
-                pageMem.releasePage(cacheId, pageId, page);
+                pageMem.releasePage(grpId, pageId, page);
             }
         }
         while(nextLink != 0);
@@ -188,9 +189,11 @@ public class CacheDataRowAdapter implements CacheDataRow {
     }
 
     /**
+     * @param sharedCtx Cache shared context.
      * @param coctx Cache object context.
      * @param buf Buffer.
      * @param keyOnly {@code true} If need to read only key object.
+     * @param readCacheId {@code true} If need to read cache ID.
      * @param incomplete Incomplete object.
      * @throws IgniteCheckedException If failed.
      * @return Read object.
@@ -200,9 +203,10 @@ public class CacheDataRowAdapter implements CacheDataRow {
         CacheObjectContext coctx,
         ByteBuffer buf,
         boolean keyOnly,
+        boolean readCacheId,
         IncompleteObject<?> incomplete
     ) throws IgniteCheckedException {
-        if (cacheId == 0) {
+        if (readCacheId && cacheId == 0) {
             incomplete = readIncompleteCacheId(buf, incomplete);
 
             if (cacheId == 0)
@@ -211,8 +215,13 @@ public class CacheDataRowAdapter implements CacheDataRow {
             incomplete = null;
         }
 
-        if (coctx == null)
+        if (coctx == null) {
+            // coctx can be null only when grp is null too, this means that
+            // we are in process of eviction and cacheId is mandatory part of data.
+            assert cacheId != 0;
+
             coctx = sharedCtx.cacheContext(cacheId).cacheObjectContext();
+        }
 
         // Read key.
         if (key == null) {
@@ -251,20 +260,23 @@ public class CacheDataRowAdapter implements CacheDataRow {
     }
 
     /**
+     * @param sharedCtx Cache shared context.
      * @param coctx Cache object context.
      * @param addr Address.
      * @param rowData Required row data.
+     * @param readCacheId {@code true} If need to read cache ID.
      * @throws IgniteCheckedException If failed.
      */
     private void readFullRow(
         GridCacheSharedContext<?, ?> sharedCtx,
         CacheObjectContext coctx,
         long addr,
-        RowData rowData)
+        RowData rowData,
+        boolean readCacheId)
         throws IgniteCheckedException {
         int off = 0;
 
-        if (cacheId == 0) {
+        if (readCacheId) {
             cacheId = PageUtils.getInt(addr, off);
 
             off += 4;
@@ -328,6 +340,8 @@ public class CacheDataRowAdapter implements CacheDataRow {
             if (remaining >= size) {
                 cacheId = buf.getInt();
 
+                assert cacheId != 0;
+
                 return null;
             }
 
@@ -342,6 +356,8 @@ public class CacheDataRowAdapter implements CacheDataRow {
             timeBuf.order(buf.order());
 
             cacheId = timeBuf.getInt();
+
+            assert cacheId != 0;
         }
 
         return incomplete;
@@ -401,7 +417,6 @@ public class CacheDataRowAdapter implements CacheDataRow {
      * @param buf Buffer.
      * @param incomplete Incomplete object.
      * @return Incomplete object.
-     * @throws IgniteCheckedException If failed.
      */
     private IncompleteObject<?> readIncompleteExpireTime(
         ByteBuffer buf,

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheSearchRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheSearchRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheSearchRow.java
index d51cf0e..6e429c4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheSearchRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheSearchRow.java
@@ -37,4 +37,9 @@ public interface CacheSearchRow {
      * @return Key hash code.
      */
     public int hash();
+
+    /**
+     * @return Cache ID or {@code 0} if cache ID is not defined.
+     */
+    public int cacheId();
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
index fd5e2a2..19c25aa 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
@@ -39,6 +39,7 @@ import org.apache.ignite.internal.mem.file.MappedFileMemoryProvider;
 import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedManagerAdapter;
@@ -730,9 +731,9 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     }
 
     /**
-     * @param stoppedCtxs A collection of tuples (cache context, destroy flag).
+     * @param stoppedGrps A collection of tuples (cache group, destroy flag).
      */
-    public void onCachesStopped(Collection<IgniteBiTuple<GridCacheContext, Boolean>> stoppedCtxs) {
+    public void onCacheGroupsStopped(Collection<IgniteBiTuple<CacheGroupContext, Boolean>> stoppedGrps) {
         // No-op.
     }
 
@@ -768,12 +769,12 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
 
     /**
      * Reserve update history for preloading.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param partId Partition Id.
      * @param cntr Update counter.
      * @return True if successfully reserved.
      */
-    public boolean reserveHistoryForPreloading(int cacheId, int partId, long cntr) {
+    public boolean reserveHistoryForPreloading(int grpId, int partId, long cntr) {
         return false;
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheSnapshotManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheSnapshotManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheSnapshotManager.java
index 5b87cf7..91957db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheSnapshotManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheSnapshotManager.java
@@ -64,49 +64,49 @@ public class IgniteCacheSnapshotManager extends GridCacheSharedManagerAdapter {
      *
      */
     public void restoreState() throws IgniteCheckedException {
-
+        // No-op.
     }
 
     /**
      *
      */
     public void onCheckPointBegin() {
-
+        // No-op.
     }
 
     /**
      *
      */
     public void beforeCheckpointPageWritten() {
-
+        // No-op.
     }
 
     /**
      *
      */
     public void afterCheckpointPageWritten() {
-
+        // No-op.
     }
 
     /**
      * @param fullId Full id.
      */
     public void beforePageWrite(FullPageId fullId) {
-
+        // No-op.
     }
 
     /**
      * @param fullId Full id.
      */
     public void onPageWrite(FullPageId fullId, ByteBuffer tmpWriteBuf) {
-
+        // No-op.
     }
 
     /**
      * @param cctx Cctx.
      */
     public void onCacheStop(GridCacheContext cctx) {
-
+        // No-op.
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetaStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetaStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetaStore.java
index 91fed4c..c21b818 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetaStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetaStore.java
@@ -29,7 +29,7 @@ public interface MetaStore {
      * @param idxName Index name.
      * @return {@link RootPage} that keeps pageId, allocated flag that shows whether the page
      *      was newly allocated, and rootId that is counter which increments each time new page allocated.
-     * @throws IgniteCheckedException
+     * @throws IgniteCheckedException If failed.
      */
     public RootPage getOrAllocateForTree(String idxName) throws IgniteCheckedException;
 
@@ -38,14 +38,14 @@ public interface MetaStore {
      *
      * @param idxName Index name.
      * @return Root ID or -1 if no page was removed.
-     * @throws IgniteCheckedException
+     * @throws IgniteCheckedException  If failed.
      */
     public RootPage dropRootPage(String idxName) throws IgniteCheckedException;
 
     /**
      * Destroy this meta store.
      *
-     * @throws IgniteCheckedException
+     * @throws IgniteCheckedException  If failed.
      */
     public void destroy() throws IgniteCheckedException;
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
index ca4ad05..139bf73 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MetadataStorage.java
@@ -53,8 +53,8 @@ public class MetadataStorage implements MetaStore {
     /** Meta page reuse tree. */
     private final ReuseList reuseList;
 
-    /** Cache ID. */
-    private final int cacheId;
+    /** Cache group ID. */
+    private final int grpId;
 
     /** */
     private final int allocPartId;
@@ -70,7 +70,7 @@ public class MetadataStorage implements MetaStore {
         final PageMemory pageMem,
         final IgniteWriteAheadLogManager wal,
         final AtomicLong globalRmvId,
-        final int cacheId,
+        final int grpId,
         final int allocPartId,
         final byte allocSpace,
         final ReuseList reuseList,
@@ -79,12 +79,12 @@ public class MetadataStorage implements MetaStore {
     ) {
         try {
             this.pageMem = pageMem;
-            this.cacheId = cacheId;
+            this.grpId = grpId;
             this.allocPartId = allocPartId;
             this.allocSpace = allocSpace;
             this.reuseList = reuseList;
 
-            metaTree = new MetaTree(cacheId, allocPartId, allocSpace, pageMem, wal, globalRmvId, rootPageId,
+            metaTree = new MetaTree(grpId, allocPartId, allocSpace, pageMem, wal, globalRmvId, rootPageId,
                 reuseList, MetaStoreInnerIO.VERSIONS, MetaStoreLeafIO.VERSIONS, initNew);
         }
         catch (IgniteCheckedException e) {
@@ -111,14 +111,14 @@ public class MetadataStorage implements MetaStore {
                 if (reuseList != null)
                     pageId = reuseList.takeRecycledPage();
 
-                pageId = pageId == 0 ? pageMem.allocatePage(cacheId, allocPartId, allocSpace) : pageId;
+                pageId = pageId == 0 ? pageMem.allocatePage(grpId, allocPartId, allocSpace) : pageId;
 
                 tree.put(new IndexItem(idxNameBytes, pageId));
 
-                return new RootPage(new FullPageId(pageId, cacheId), true);
+                return new RootPage(new FullPageId(pageId, grpId), true);
             }
             else {
-                final FullPageId pageId = new FullPageId(row.pageId, cacheId);
+                final FullPageId pageId = new FullPageId(row.pageId, grpId);
 
                 return new RootPage(pageId, false);
             }
@@ -134,10 +134,10 @@ public class MetadataStorage implements MetaStore {
 
         if (row != null) {
             if (reuseList == null)
-                pageMem.freePage(cacheId, row.pageId);
+                pageMem.freePage(grpId, row.pageId);
         }
 
-        return row != null ? new RootPage(new FullPageId(row.pageId, cacheId), false) : null;
+        return row != null ? new RootPage(new FullPageId(row.pageId, grpId), false) : null;
     }
 
     /** {@inheritDoc} */
@@ -288,7 +288,7 @@ public class MetadataStorage implements MetaStore {
         PageUtils.putByte(dstPageAddr, dstOff, len);
         dstOff++;
 
-        PageHandler.copyMemory(srcPageAddr, srcOff, dstPageAddr, dstOff, len);
+        PageHandler.copyMemory(srcPageAddr, dstPageAddr, srcOff, dstOff, len);
         srcOff += len;
         dstOff += len;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
index 563902b..d707869 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/RowStore.java
@@ -19,8 +19,9 @@ package org.apache.ignite.internal.processors.cache.database;
 
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeList;
 
 /**
@@ -31,27 +32,29 @@ public class RowStore {
     private final FreeList freeList;
 
     /** */
-    protected final PageMemory pageMem;
+    private final GridCacheSharedContext ctx;
 
     /** */
-    protected final GridCacheContext<?,?> cctx;
+    protected final PageMemory pageMem;
 
     /** */
     protected final CacheObjectContext coctx;
 
+
+
     /**
-     * @param cctx Cache context.
+     * @param grp Cache group.
      * @param freeList Free list.
      */
-    public RowStore(GridCacheContext<?,?> cctx, FreeList freeList) {
-        assert cctx != null;
+    public RowStore(CacheGroupContext grp, FreeList freeList) {
+        assert grp != null;
         assert freeList != null;
 
-        this.cctx = cctx;
         this.freeList = freeList;
 
-        coctx = cctx.cacheObjectContext();
-        pageMem = cctx.memoryPolicy().pageMemory();
+        ctx = grp.shared();
+        coctx = grp.cacheObjectContext();
+        pageMem = grp.memoryPolicy().pageMemory();
     }
 
     /**
@@ -60,13 +63,13 @@ public class RowStore {
      */
     public void removeRow(long link) throws IgniteCheckedException {
         assert link != 0;
-        cctx.shared().database().checkpointReadLock();
+        ctx.database().checkpointReadLock();
 
         try {
             freeList.removeDataRowByLink(link);
         }
         finally {
-            cctx.shared().database().checkpointReadUnlock();
+            ctx.database().checkpointReadUnlock();
         }
     }
 
@@ -75,13 +78,13 @@ public class RowStore {
      * @throws IgniteCheckedException If failed.
      */
     public void addRow(CacheDataRow row) throws IgniteCheckedException {
-        cctx.shared().database().checkpointReadLock();
+        ctx.database().checkpointReadLock();
 
         try {
             freeList.insertDataRow(row);
         }
         finally {
-            cctx.shared().database().checkpointReadUnlock();
+            ctx.database().checkpointReadUnlock();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
index 2465d3f..d92f811 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
@@ -884,11 +884,12 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
 
     /**
      * @param upper Upper bound.
+     * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
      * @return Cursor.
      * @throws IgniteCheckedException If failed.
      */
-    private GridCursor<T> findLowerUnbounded(L upper) throws IgniteCheckedException {
-        ForwardCursor cursor = new ForwardCursor(null, upper);
+    private GridCursor<T> findLowerUnbounded(L upper, Object x) throws IgniteCheckedException {
+        ForwardCursor cursor = new ForwardCursor(null, upper, x);
 
         long firstPageId;
 
@@ -933,14 +934,25 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
      * @return Cursor.
      * @throws IgniteCheckedException If failed.
      */
-    @Override public final GridCursor<T> find(L lower, L upper) throws IgniteCheckedException {
+    @Override public GridCursor<T> find(L lower, L upper) throws IgniteCheckedException {
+        return find(lower, upper, null);
+    }
+
+    /**
+     * @param lower Lower bound inclusive or {@code null} if unbounded.
+     * @param upper Upper bound inclusive or {@code null} if unbounded.
+     * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+     * @return Cursor.
+     * @throws IgniteCheckedException If failed.
+     */
+    public final GridCursor<T> find(L lower, L upper, Object x) throws IgniteCheckedException {
         checkDestroyed();
 
         try {
             if (lower == null)
-                return findLowerUnbounded(upper);
+                return findLowerUnbounded(upper, x);
 
-            ForwardCursor cursor = new ForwardCursor(lower, upper);
+            ForwardCursor cursor = new ForwardCursor(lower, upper, x);
 
             cursor.find();
 
@@ -4392,6 +4404,9 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
         /** */
         private final L upperBound;
 
+        /** */
+        private final Object x;
+
         /**
          * @param lowerBound Lower bound.
          * @param upperBound Upper bound.
@@ -4399,6 +4414,18 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
         ForwardCursor(L lowerBound, L upperBound) {
             this.lowerBound = lowerBound;
             this.upperBound = upperBound;
+            this.x = null;
+        }
+
+        /**
+         * @param lowerBound Lower bound.
+         * @param upperBound Upper bound.
+         * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+         */
+        ForwardCursor(L lowerBound, L upperBound, Object x) {
+            this.lowerBound = lowerBound;
+            this.upperBound = upperBound;
+            this.x = x;
         }
 
         /**
@@ -4515,7 +4542,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
                 rows = (T[])new Object[cnt];
 
             for (int i = 0; i < cnt; i++) {
-                T r = getRow(io, pageAddr, startIdx + i);
+                T r = getRow(io, pageAddr, startIdx + i, x);
 
                 rows = GridArrays.set(rows, i, r);
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
index 2586696..e40ed11 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
@@ -157,6 +157,21 @@ public abstract class PageIO {
     /** */
     public static final short T_PAGE_UPDATE_TRACKING = 15;
 
+    /** */
+    public static final short T_CACHE_ID_AWARE_DATA_REF_INNER = 16;
+
+    /** */
+    public static final short T_CACHE_ID_AWARE_DATA_REF_LEAF = 17;
+
+    /** */
+    public static final short T_CACHE_ID_AWARE_PENDING_REF_INNER = 18;
+
+    /** */
+    public static final short T_CACHE_ID_AWARE_PENDING_REF_LEAF = 19;
+
+    /** */
+    public static final short T_PART_CNTRS = 20;
+
     /** Index for payload == 1. */
     public static final short T_H2_EX_REF_LEAF_START = 10000;
 
@@ -430,6 +445,9 @@ public abstract class PageIO {
             case T_PART_META:
                 return (Q)PagePartitionMetaIO.VERSIONS.forVersion(ver);
 
+            case T_PART_CNTRS:
+                return (Q)PagePartitionCountersIO.VERSIONS.forVersion(ver);
+
             case T_PAGE_UPDATE_TRACKING:
                 return (Q)TrackingPageIO.VERSIONS.forVersion(ver);
 
@@ -484,6 +502,12 @@ public abstract class PageIO {
             case T_DATA_REF_LEAF:
                 return (Q)IgniteCacheOffheapManagerImpl.DataLeafIO.VERSIONS.forVersion(ver);
 
+            case T_CACHE_ID_AWARE_DATA_REF_INNER:
+                return (Q)IgniteCacheOffheapManagerImpl.CacheIdAwareDataInnerIO.VERSIONS.forVersion(ver);
+
+            case T_CACHE_ID_AWARE_DATA_REF_LEAF:
+                return (Q)IgniteCacheOffheapManagerImpl.CacheIdAwareDataLeafIO.VERSIONS.forVersion(ver);
+
             case T_METASTORE_INNER:
                 return (Q)MetadataStorage.MetaStoreInnerIO.VERSIONS.forVersion(ver);
 
@@ -496,6 +520,12 @@ public abstract class PageIO {
             case T_PENDING_REF_LEAF:
                 return (Q)IgniteCacheOffheapManagerImpl.PendingEntryLeafIO.VERSIONS.forVersion(ver);
 
+            case T_CACHE_ID_AWARE_PENDING_REF_INNER:
+                return (Q) IgniteCacheOffheapManagerImpl.CacheIdAwarePendingEntryInnerIO.VERSIONS.forVersion(ver);
+
+            case T_CACHE_ID_AWARE_PENDING_REF_LEAF:
+                return (Q)IgniteCacheOffheapManagerImpl.CacheIdAwarePendingEntryLeafIO.VERSIONS.forVersion(ver);
+
             default:
                 // For tests.
                 if (innerTestIO != null && innerTestIO.getType() == type && innerTestIO.getVersion() == ver)

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionCountersIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionCountersIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionCountersIO.java
new file mode 100644
index 0000000..015b8ff
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionCountersIO.java
@@ -0,0 +1,175 @@
+/*
+ * 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.database.tree.io;
+
+import java.util.Map;
+import org.apache.ignite.internal.pagemem.PageUtils;
+
+/**
+ *
+ */
+public class PagePartitionCountersIO extends PageIO {
+    /** */
+    private static final int CNT_OFF = COMMON_HEADER_END;
+
+    /** */
+    private static final int LAST_FLAG_OFF = CNT_OFF + 2;
+
+    /** */
+    private static final int NEXT_COUNTERS_PAGE_OFF = LAST_FLAG_OFF + 1;
+
+    /** */
+    private static final int ITEMS_OFF = NEXT_COUNTERS_PAGE_OFF + 8;
+
+    /** */
+    private static final int ITEM_SIZE = 12;
+
+    /** */
+    private static final byte LAST_FLAG = 0b1;
+
+    /** */
+    public static final IOVersions<PagePartitionCountersIO> VERSIONS = new IOVersions<>(
+        new PagePartitionCountersIO(1)
+    );
+
+    /**
+     * @param ver Page format version.
+     */
+    public PagePartitionCountersIO(int ver) {
+        super(T_PART_CNTRS, ver);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void initNewPage(long pageAddr, long pageId, int pageSize) {
+        super.initNewPage(pageAddr, pageId, pageSize);
+
+        setCount(pageAddr, 0);
+        setNextCountersPageId(pageAddr, 0);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @return Next counters page ID or {@code 0} if it does not exist.
+     */
+    public long getNextCountersPageId(long pageAddr) {
+        return PageUtils.getLong(pageAddr, NEXT_COUNTERS_PAGE_OFF);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param partMetaPageId Next counters page ID.
+     */
+    public void setNextCountersPageId(long pageAddr, long partMetaPageId) {
+        PageUtils.putLong(pageAddr, NEXT_COUNTERS_PAGE_OFF, partMetaPageId);
+    }
+
+    /**
+     * @param pageSize Page size.
+     * @param pageAddr Page address.
+     * @param cacheSizes Serialized cache size items (pairs of cache ID and its size).
+     * @return Number of written pairs.
+     */
+    public int writeCacheSizes(int pageSize, long pageAddr, byte[] cacheSizes, int itemsOff) {
+        assert cacheSizes != null;
+        assert cacheSizes.length % ITEM_SIZE == 0 : cacheSizes.length;
+
+        int cap = getCapacity(pageSize);
+
+        int items = (cacheSizes.length / ITEM_SIZE) - itemsOff;
+        int write = Math.min(cap, items);
+
+        if (write > 0)
+            // This can happen in case there are no items in a given partition for all caches in the group.
+            PageUtils.putBytes(pageAddr, ITEMS_OFF, cacheSizes, itemsOff * ITEM_SIZE, write * ITEM_SIZE);
+
+        setCount(pageAddr, write);
+
+        setLastFlag(pageAddr, write == items);
+
+        return write;
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param res Result map of cache sizes.
+     * @return {@code True} if the map was fully read.
+     */
+    public boolean readCacheSizes(long pageAddr, Map<Integer, Long> res) {
+        int cnt = getCount(pageAddr);
+
+        assert cnt >= 0 && cnt <= Short.MAX_VALUE : cnt;
+
+        if (cnt == 0)
+            return true;
+
+        int off = ITEMS_OFF;
+
+        for (int i = 0; i < cnt; i++) {
+            int cacheId = PageUtils.getInt(pageAddr, off);
+            off += 4;
+
+            assert cacheId != 0;
+
+            long cacheSize = PageUtils.getLong(pageAddr, off);
+            off += 8;
+
+            assert cacheSize > 0 : cacheSize;
+
+            Long old = res.put(cacheId, cacheSize);
+
+            assert old == null;
+        }
+
+        return getLastFlag(pageAddr);
+    }
+
+    private boolean getLastFlag(long pageAddr) {
+        return PageUtils.getByte(pageAddr, LAST_FLAG_OFF) == LAST_FLAG;
+    }
+
+    private void setLastFlag(long pageAddr, boolean last) {
+        PageUtils.putByte(pageAddr, LAST_FLAG_OFF, last ? LAST_FLAG : ~LAST_FLAG);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @return Stored items count.
+     */
+    private int getCount(long pageAddr) {
+        return PageUtils.getShort(pageAddr, CNT_OFF);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param cnt Stored items count.
+     */
+    private void setCount(long pageAddr, int cnt) {
+        assert cnt >= 0 && cnt <= Short.MAX_VALUE : cnt;
+
+        PageUtils.putShort(pageAddr, CNT_OFF, (short)cnt);
+    }
+
+    /**
+     * @param pageSize Page size.
+     * @return Maximum number of items which can be stored in buffer.
+     */
+    private int getCapacity(int pageSize) {
+        return (pageSize - ITEMS_OFF) / ITEM_SIZE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionMetaIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionMetaIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionMetaIO.java
index aca0725..67cc5a3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionMetaIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PagePartitionMetaIO.java
@@ -37,6 +37,9 @@ public class PagePartitionMetaIO extends PageMetaIO {
     private static final int PARTITION_STATE_OFF = GLOBAL_RMV_ID_OFF + 8;
 
     /** */
+    private static final int NEXT_PART_META_PAGE_OFF = PARTITION_STATE_OFF + 1;
+
+    /** */
     public static final IOVersions<PagePartitionMetaIO> VERSIONS = new IOVersions<>(
         new PagePartitionMetaIO(1)
     );
@@ -49,6 +52,7 @@ public class PagePartitionMetaIO extends PageMetaIO {
         setUpdateCounter(pageAddr, 0);
         setGlobalRemoveId(pageAddr, 0);
         setPartitionState(pageAddr, (byte)-1);
+        setCountersPageId(pageAddr, 0);
     }
 
     /**
@@ -120,4 +124,20 @@ public class PagePartitionMetaIO extends PageMetaIO {
     public void setPartitionState(long pageAddr, byte state) {
         PageUtils.putByte(pageAddr, PARTITION_STATE_OFF, state);
     }
+
+    /**
+     * @param pageAddr Page address.
+     * @return Next meta partial page ID or {@code 0} if it does not exist.
+     */
+    public long getCountersPageId(long pageAddr) {
+        return PageUtils.getLong(pageAddr, NEXT_PART_META_PAGE_OFF);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param metaPageId Next partial meta page ID.
+     */
+    public void setCountersPageId(long pageAddr, long metaPageId) {
+        PageUtils.putLong(pageAddr, NEXT_PART_META_PAGE_OFF, metaPageId);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTtlUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTtlUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTtlUpdateRequest.java
index 5d1885e..c092132 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTtlUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridCacheTtlUpdateRequest.java
@@ -24,7 +24,7 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.GridDirectCollection;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
@@ -38,7 +38,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  *
  */
-public class GridCacheTtlUpdateRequest extends GridCacheMessage {
+public class GridCacheTtlUpdateRequest extends GridCacheIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedBaseMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedBaseMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedBaseMessage.java
index 630c79f..fc209aa 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedBaseMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedBaseMessage.java
@@ -24,7 +24,7 @@ import java.util.Collections;
 import org.apache.ignite.internal.GridDirectCollection;
 import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersionable;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -37,7 +37,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Base for all messages in replicated cache.
  */
-public abstract class GridDistributedBaseMessage extends GridCacheMessage implements GridCacheDeployable,
+public abstract class GridDistributedBaseMessage extends GridCacheIdMessage implements GridCacheDeployable,
     GridCacheVersionable {
     /** */
     private static final long serialVersionUID = 0L;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedCacheAdapter.java
index c966877..dc9e4ec 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedCacheAdapter.java
@@ -79,10 +79,9 @@ public abstract class GridDistributedCacheAdapter<K, V> extends GridCacheAdapter
 
     /**
      * @param ctx Cache registry.
-     * @param startSize Start size.
      */
-    protected GridDistributedCacheAdapter(GridCacheContext<K, V> ctx, int startSize) {
-        super(ctx, startSize);
+    protected GridDistributedCacheAdapter(GridCacheContext<K, V> ctx) {
+        super(ctx);
     }
 
     /**
@@ -279,11 +278,11 @@ public abstract class GridDistributedCacheAdapter<K, V> extends GridCacheAdapter
             IgniteCacheOffheapManager offheap = ctx.offheap();
 
             if (modes.offheap)
-                size += offheap.entriesCount(modes.primary, modes.backup, topVer);
+                size += offheap.cacheEntriesCount(ctx.cacheId(), modes.primary, modes.backup, topVer);
             else if (modes.heap) {
                 for (GridDhtLocalPartition locPart : ctx.topology().currentLocalPartitions()) {
                     if ((modes.primary && locPart.primary(topVer)) || (modes.backup && locPart.backup(topVer)))
-                        size += locPart.publicSize();
+                        size += locPart.publicSize(ctx.cacheId());
                 }
             }
         }
@@ -308,7 +307,7 @@ public abstract class GridDistributedCacheAdapter<K, V> extends GridCacheAdapter
 
             if (ctx.affinity().primaryByPartition(ctx.localNode(), part, topVer) && modes.primary ||
                 ctx.affinity().backupByPartition(ctx.localNode(), part, topVer) && modes.backup)
-                size += offheap.entriesCount(part);
+                size += offheap.cacheEntriesCount(ctx.cacheId(), part);
         }
 
         return size;
@@ -460,7 +459,7 @@ public abstract class GridDistributedCacheAdapter<K, V> extends GridCacheAdapter
                             return false;
 
                         try {
-                            GridCloseableIterator<KeyCacheObject> iter = dht.context().offheap().keysIterator(part);
+                            GridCloseableIterator<KeyCacheObject> iter = dht.context().offheap().cacheKeysIterator(ctx.cacheId(), part);
 
                             if (iter != null) {
                                 try {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishResponse.java
index 561c292..c36e633 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxFinishResponse.java
@@ -71,6 +71,16 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
     }
 
     /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
     @Override public final int partition() {
         return part;
     }
@@ -135,25 +145,25 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
         }
 
         switch (writer.state()) {
-            case 3:
+            case 2:
                 if (!writer.writeByte("flags", flags))
                     return false;
 
                 writer.incrementState();
 
-            case 4:
+            case 3:
                 if (!writer.writeIgniteUuid("futId", futId))
                     return false;
 
                 writer.incrementState();
 
-            case 5:
+            case 4:
                 if (!writer.writeInt("part", part))
                     return false;
 
                 writer.incrementState();
 
-            case 6:
+            case 5:
                 if (!writer.writeMessage("txId", txId))
                     return false;
 
@@ -175,7 +185,7 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
             return false;
 
         switch (reader.state()) {
-            case 3:
+            case 2:
                 flags = reader.readByte("flags");
 
                 if (!reader.isLastRead())
@@ -183,7 +193,7 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 4:
+            case 3:
                 futId = reader.readIgniteUuid("futId");
 
                 if (!reader.isLastRead())
@@ -191,7 +201,7 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 5:
+            case 4:
                 part = reader.readInt("part");
 
                 if (!reader.isLastRead())
@@ -199,7 +209,7 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 6:
+            case 5:
                 txId = reader.readMessage("txId");
 
                 if (!reader.isLastRead())
@@ -219,7 +229,7 @@ public class GridDistributedTxFinishResponse extends GridCacheMessage {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 7;
+        return 6;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
index 714d781..5e3020d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/GridDistributedTxRemoteAdapter.java
@@ -594,6 +594,8 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
                                                         dhtVer,
                                                         txEntry.updateCounter());
                                                 else {
+                                                    assert val != null : txEntry;
+
                                                     cached.innerSet(this,
                                                         eventNodeId(),
                                                         nodeId,

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedConcurrentMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedConcurrentMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedConcurrentMap.java
index 76c7a15..3b41ffa 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedConcurrentMap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedConcurrentMap.java
@@ -25,7 +25,7 @@ import java.util.NoSuchElementException;
 import java.util.Set;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryPredicate;
-import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheConcurrentMap;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
@@ -38,24 +38,25 @@ import org.jetbrains.annotations.Nullable;
  * An implementation of GridCacheConcurrentMap that will delegate all method calls to corresponding local partition.
  */
 public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap {
-    /** Context. */
-    private final GridCacheContext ctx;
+    /** Cache group. */
+    private final CacheGroupContext grp;
 
     /**
-     * Constructor.
-     * @param ctx Context.
+     * @param grp Cache group.
      */
-    public GridCachePartitionedConcurrentMap(GridCacheContext ctx) {
-        this.ctx = ctx;
+    GridCachePartitionedConcurrentMap(CacheGroupContext grp) {
+        this.grp = grp;
     }
 
     /**
+     * @param cctx Cache context.
      * @param key Key.
      * @param topVer Topology version.
      * @param create Create flag.
      * @return Local partition.
      */
     @Nullable private GridDhtLocalPartition localPartition(
+        GridCacheContext cctx,
         KeyCacheObject key,
         AffinityTopologyVersion topVer,
         boolean create
@@ -63,33 +64,33 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
         int p = key.partition();
 
         if (p == -1)
-            p = ctx.affinity().partition(key);
+            p = cctx.affinity().partition(key);
 
-        return ctx.topology().localPartition(p, topVer, create);
+        return grp.topology().localPartition(p, topVer, create);
     }
 
     /** {@inheritDoc} */
-    @Nullable @Override public GridCacheMapEntry getEntry(KeyCacheObject key) {
-        GridDhtLocalPartition part = localPartition(key, AffinityTopologyVersion.NONE, false);
+    @Nullable @Override public GridCacheMapEntry getEntry(GridCacheContext ctx, KeyCacheObject key) {
+        GridDhtLocalPartition part = localPartition(ctx, key, AffinityTopologyVersion.NONE, false);
 
         if (part == null)
             return null;
 
-        return part.getEntry(key);
+        return part.getEntry(ctx, key);
     }
 
     /** {@inheritDoc} */
-    @Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(AffinityTopologyVersion topVer,
+    @Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(GridCacheContext ctx, AffinityTopologyVersion topVer,
         KeyCacheObject key,
         boolean create,
         boolean touch) {
         while (true) {
-            GridDhtLocalPartition part = localPartition(key, topVer, create);
+            GridDhtLocalPartition part = localPartition(ctx, key, topVer, create);
 
             if (part == null)
                 return null;
 
-            GridCacheMapEntry res = part.putEntryIfObsoleteOrAbsent(topVer, key, create, touch);
+            GridCacheMapEntry res = part.putEntryIfObsoleteOrAbsent(ctx, topVer, key, create, touch);
 
             if (res != null || !create)
                 return res;
@@ -102,35 +103,35 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
     @Override public int internalSize() {
         int size = 0;
 
-        for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions())
+        for (GridDhtLocalPartition part : grp.topology().currentLocalPartitions())
             size += part.internalSize();
 
         return size;
     }
 
     /** {@inheritDoc} */
-    @Override public int publicSize() {
+    @Override public int publicSize(int cacheId) {
         int size = 0;
 
-        for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions())
-            size += part.publicSize();
+        for (GridDhtLocalPartition part : grp.topology().currentLocalPartitions())
+            size += part.publicSize(cacheId);
 
         return size;
     }
 
     /** {@inheritDoc} */
-    @Override public void incrementPublicSize(GridCacheEntryEx e) {
-        localPartition(e.key(), AffinityTopologyVersion.NONE, true).incrementPublicSize(e);
+    @Override public void incrementPublicSize(CacheMapHolder hld, GridCacheEntryEx e) {
+        localPartition(e.context(), e.key(), AffinityTopologyVersion.NONE, true).incrementPublicSize(hld, e);
     }
 
     /** {@inheritDoc} */
-    @Override public void decrementPublicSize(GridCacheEntryEx e) {
-        localPartition(e.key(), AffinityTopologyVersion.NONE, true).decrementPublicSize(e);
+    @Override public void decrementPublicSize(CacheMapHolder hld, GridCacheEntryEx e) {
+        localPartition(e.context(), e.key(), AffinityTopologyVersion.NONE, true).decrementPublicSize(hld, e);
     }
 
     /** {@inheritDoc} */
     @Override public boolean removeEntry(GridCacheEntryEx entry) {
-        GridDhtLocalPartition part = localPartition(entry.key(), AffinityTopologyVersion.NONE, false);
+        GridDhtLocalPartition part = localPartition(entry.context(), entry.key(), AffinityTopologyVersion.NONE, false);
 
         if (part == null)
             return false;
@@ -139,12 +140,12 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
     }
 
     /** {@inheritDoc} */
-    @Override public Iterable<GridCacheMapEntry> entries(final CacheEntryPredicate... filter) {
+    @Override public Iterable<GridCacheMapEntry> entries(final int cacheId, final CacheEntryPredicate... filter) {
         return new Iterable<GridCacheMapEntry>() {
             @Override public Iterator<GridCacheMapEntry> iterator() {
                 return new PartitionedIterator<GridCacheMapEntry>() {
                     @Override protected Iterator<GridCacheMapEntry> iterator(GridDhtLocalPartition part) {
-                        return part.entries(filter).iterator();
+                        return part.entries(cacheId, filter).iterator();
                     }
                 };
             }
@@ -152,23 +153,10 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
     }
 
     /** {@inheritDoc} */
-    @Override public Iterable<GridCacheMapEntry> allEntries(final CacheEntryPredicate... filter) {
-        return new Iterable<GridCacheMapEntry>() {
-            @Override public Iterator<GridCacheMapEntry> iterator() {
-                return new PartitionedIterator<GridCacheMapEntry>() {
-                    @Override protected Iterator<GridCacheMapEntry> iterator(GridDhtLocalPartition part) {
-                        return part.allEntries(filter).iterator();
-                    }
-                };
-            }
-        };
-    }
-
-    /** {@inheritDoc} */
-    @Override public Set<GridCacheMapEntry> entrySet(final CacheEntryPredicate... filter) {
+    @Override public Set<GridCacheMapEntry> entrySet(final int cacheId, final CacheEntryPredicate... filter) {
         return new PartitionedSet<GridCacheMapEntry>() {
             @Override protected Set<GridCacheMapEntry> set(GridDhtLocalPartition part) {
-                return part.entrySet(filter);
+                return part.entrySet(cacheId, filter);
             }
         };
     }
@@ -178,7 +166,7 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
      */
     private abstract class PartitionedIterator<T> implements Iterator<T> {
         /** Partitions iterator. */
-        private Iterator<GridDhtLocalPartition> partsIter = ctx.topology().currentLocalPartitions().iterator();
+        private Iterator<GridDhtLocalPartition> partsIter = grp.topology().currentLocalPartitions().iterator();
 
         /** Current partition iterator. */
         private Iterator<T> currIter = partsIter.hasNext() ? iterator(partsIter.next()) :
@@ -242,7 +230,7 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
         @Override public int size() {
             int size = 0;
 
-            for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions())
+            for (GridDhtLocalPartition part : grp.topology().currentLocalPartitions())
                 size += set(part).size();
 
             return size;
@@ -250,7 +238,7 @@ public class GridCachePartitionedConcurrentMap implements GridCacheConcurrentMap
 
         /** {@inheritDoc} */
         @Override public boolean contains(Object o) {
-            for (GridDhtLocalPartition part : ctx.topology().currentLocalPartitions()) {
+            for (GridDhtLocalPartition part : grp.topology().currentLocalPartitions()) {
                 if (set(part).contains(o))
                     return true;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
index 1482137..cace4e8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridClientPartitionTopology.java
@@ -73,7 +73,7 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
     private GridCacheSharedContext cctx;
 
     /** Cache ID. */
-    private int cacheId;
+    private int grpId;
 
     /** Logger. */
     private final IgniteLogger log;
@@ -113,18 +113,18 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
 
     /**
      * @param cctx Context.
-     * @param cacheId Cache ID.
+     * @param grpId Group ID.
      * @param exchFut Exchange ID.
      * @param similarAffKey Key to find caches with similar affinity.
      */
     public GridClientPartitionTopology(
         GridCacheSharedContext cctx,
-        int cacheId,
+        int grpId,
         GridDhtPartitionsExchangeFuture exchFut,
         Object similarAffKey
     ) {
         this.cctx = cctx;
-        this.cacheId = cacheId;
+        this.grpId = grpId;
         this.similarAffKey = similarAffKey;
 
         topVer = exchFut.topologyVersion();
@@ -168,8 +168,8 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
     }
 
     /** {@inheritDoc} */
-    @Override public int cacheId() {
-        return cacheId;
+    @Override public int groupId() {
+        return grpId;
     }
 
     /** {@inheritDoc} */
@@ -283,7 +283,7 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
         long updateSeq = this.updateSeq.incrementAndGet();
 
         // If this is the oldest node.
-        if (oldest.id().equals(loc.id()) || exchFut.dynamicCacheStarted(cacheId)) {
+        if (oldest.id().equals(loc.id()) || exchFut.dynamicCacheGroupStarted(grpId)) {
             if (node2part == null) {
                 node2part = new GridDhtPartitionFullMap(oldest.id(), oldest.order(), updateSeq);
 
@@ -361,8 +361,8 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
     }
 
     /** {@inheritDoc} */
-    @Override public GridDhtLocalPartition localPartition(Object key, boolean create) {
-        return localPartition(1, AffinityTopologyVersion.NONE, create);
+    @Override public GridDhtLocalPartition localPartition(int p) {
+        return localPartition(p, AffinityTopologyVersion.NONE, false);
     }
 
     /** {@inheritDoc} */
@@ -550,7 +550,10 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
         lock.readLock().lock();
 
         try {
-            assert node2part != null && node2part.valid() : "Invalid node2part [node2part: " + node2part +
+            if (stopping || node2part == null)
+                return null;
+
+            assert node2part.valid() : "Invalid node2part [node2part: " + node2part +
                 ", locNodeId=" + cctx.localNodeId() +
                 ", igniteInstanceName=" + cctx.igniteInstanceName() + ']';
 
@@ -1035,7 +1038,7 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
     /** {@inheritDoc} */
     @Override public void printMemoryStats(int threshold) {
         X.println(">>>  Cache partition topology stats [igniteInstanceName=" + cctx.igniteInstanceName() +
-            ", cacheId=" + cacheId + ']');
+            ", grpId=" + grpId + ']');
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentRequest.java
index f80adc5..d9d642a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentRequest.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.processors.cache.distributed.dht;
 
 import java.nio.ByteBuffer;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
 import org.apache.ignite.plugin.extensions.communication.MessageWriter;
@@ -27,7 +27,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Affinity assignment request.
  */
-public class GridDhtAffinityAssignmentRequest extends GridCacheMessage {
+public class GridDhtAffinityAssignmentRequest extends GridCacheGroupIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -46,17 +46,17 @@ public class GridDhtAffinityAssignmentRequest extends GridCacheMessage {
 
     /**
      * @param futId Future ID.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param topVer Topology version.
      */
     public GridDhtAffinityAssignmentRequest(
         long futId,
-        int cacheId,
+        int grpId,
         AffinityTopologyVersion topVer) {
         assert topVer != null;
 
         this.futId = futId;
-        this.cacheId = cacheId;
+        this.grpId = grpId;
         this.topVer = topVer;
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentResponse.java
index 5d82171..4df3fc1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAffinityAssignmentResponse.java
@@ -27,20 +27,18 @@ import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
-import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
 import org.apache.ignite.plugin.extensions.communication.MessageWriter;
-import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
 import org.jetbrains.annotations.NotNull;
 
 /**
  * Affinity assignment response.
  */
-public class GridDhtAffinityAssignmentResponse extends GridCacheMessage {
+public class GridDhtAffinityAssignmentResponse extends GridCacheGroupIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -73,17 +71,17 @@ public class GridDhtAffinityAssignmentResponse extends GridCacheMessage {
 
     /**
      * @param futId Future ID.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param topVer Topology version.
      * @param affAssignment Affinity assignment.
      */
     public GridDhtAffinityAssignmentResponse(
         long futId,
-        int cacheId,
+        int grpId,
         @NotNull AffinityTopologyVersion topVer,
         List<List<ClusterNode>> affAssignment) {
         this.futId = futId;
-        this.cacheId = cacheId;
+        this.grpId = grpId;
         this.topVer = topVer;
 
         affAssignmentIds = ids(affAssignment);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAssignmentFetchFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAssignmentFetchFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAssignmentFetchFuture.java
index 20d1722..8746320 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAssignmentFetchFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtAssignmentFetchFuture.java
@@ -33,7 +33,7 @@ import org.apache.ignite.internal.GridNodeOrderComparator;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
 import org.apache.ignite.internal.managers.discovery.DiscoCache;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -74,27 +74,27 @@ public class GridDhtAssignmentFetchFuture extends GridFutureAdapter<GridDhtAffin
     private final AffinityTopologyVersion topVer;
 
     /** */
-    private final int cacheId;
+    private final int grpId;
 
     /**
      * @param ctx Context.
-     * @param cacheDesc Cache descriptor.
+     * @param grpDesc Group descriptor.
      * @param topVer Topology version.
      * @param discoCache Discovery cache.
      */
     public GridDhtAssignmentFetchFuture(
         GridCacheSharedContext ctx,
-        DynamicCacheDescriptor cacheDesc,
+        CacheGroupDescriptor grpDesc,
         AffinityTopologyVersion topVer,
         DiscoCache discoCache
     ) {
-        this.ctx = ctx;
-        cacheId = cacheDesc.cacheId();
         this.topVer = topVer;
+        this.grpId = grpDesc.groupId();
+        this.ctx = ctx;
 
         id = idGen.getAndIncrement();
 
-        Collection<ClusterNode> availableNodes = discoCache.cacheAffinityNodes(cacheDesc.cacheId());
+        Collection<ClusterNode> availableNodes = discoCache.cacheGroupAffinityNodes(grpDesc.groupId());
 
         LinkedList<ClusterNode> tmp = new LinkedList<>();
 
@@ -112,10 +112,10 @@ public class GridDhtAssignmentFetchFuture extends GridFutureAdapter<GridDhtAffin
     }
 
     /**
-     * @return Cache ID.
+     * @return Cache group ID.
      */
-    public int cacheId() {
-        return cacheId;
+    public int groupId() {
+        return grpId;
     }
 
     /**
@@ -195,7 +195,7 @@ public class GridDhtAssignmentFetchFuture extends GridFutureAdapter<GridDhtAffin
                             ", node=" + node + ']');
 
                     ctx.io().send(node,
-                        new GridDhtAffinityAssignmentRequest(id, cacheId, topVer),
+                        new GridDhtAffinityAssignmentRequest(id, grpId, topVer),
                         AFFINITY_POOL);
 
                     // Close window for listener notification.


[18/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
ignite-5075 'logical' caches sharing the same 'physical' cache group


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/7e45010b
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/7e45010b
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/7e45010b

Branch: refs/heads/ignite-5267
Commit: 7e45010b4848d0a570995e6dc938875710d846d8
Parents: c7a7e64
Author: sboikov <sb...@gridgain.com>
Authored: Sun Jun 4 11:02:31 2017 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Sun Jun 4 11:02:37 2017 +0300

----------------------------------------------------------------------
 .../configuration/CacheConfiguration.java       |   21 +
 .../managers/communication/GridIoManager.java   |   12 +-
 .../internal/managers/discovery/DiscoCache.java |   30 +-
 .../discovery/GridDiscoveryManager.java         |  178 +-
 .../ignite/internal/pagemem/PageUtils.java      |   16 +
 .../pagemem/snapshot/SnapshotOperation.java     |   18 +-
 .../pagemem/store/IgnitePageStoreManager.java   |   50 +-
 .../pagemem/wal/record/CheckpointRecord.java    |   22 +-
 .../MetaPageUpdatePartitionDataRecord.java      |   14 +-
 .../affinity/GridAffinityAssignmentCache.java   |   38 +-
 .../cache/CacheAffinitySharedManager.java       |  627 +--
 .../CacheClientReconnectDiscoveryData.java      |   66 +-
 .../internal/processors/cache/CacheData.java    |   22 +-
 .../processors/cache/CacheGroupContext.java     |  964 +++++
 .../processors/cache/CacheGroupData.java        |  147 +
 .../processors/cache/CacheGroupDescriptor.java  |  210 +
 .../cache/CacheJoinNodeDiscoveryData.java       |    4 +-
 .../processors/cache/CacheMetricsImpl.java      |   10 +-
 .../cache/CacheNodeCommonDiscoveryData.java     |   19 +
 .../cache/CacheOffheapEvictionManager.java      |   11 +-
 .../processors/cache/ClusterCachesInfo.java     |  531 ++-
 .../cache/ClusterCachesReconnectResult.java     |   61 +
 .../cache/DynamicCacheDescriptor.java           |   26 +
 .../processors/cache/ExchangeActions.java       |   86 +-
 .../processors/cache/GridCacheAdapter.java      |   63 +-
 .../cache/GridCacheAffinityManager.java         |   62 +-
 .../processors/cache/GridCacheAttributes.java   |   21 +-
 .../cache/GridCacheClearAllRunnable.java        |    2 +-
 .../cache/GridCacheConcurrentMap.java           |   55 +-
 .../cache/GridCacheConcurrentMapImpl.java       |  171 +-
 .../processors/cache/GridCacheContext.java      |  167 +-
 .../processors/cache/GridCacheEntryInfo.java    |   37 +-
 .../processors/cache/GridCacheEventManager.java |   36 -
 .../cache/GridCacheGroupIdMessage.java          |  110 +
 .../processors/cache/GridCacheIdMessage.java    |  117 +
 .../processors/cache/GridCacheIoManager.java    |  282 +-
 .../cache/GridCacheLocalConcurrentMap.java      |   56 +-
 .../processors/cache/GridCacheMapEntry.java     |   76 +-
 .../processors/cache/GridCacheMessage.java      |   97 +-
 .../GridCachePartitionExchangeManager.java      |  298 +-
 .../processors/cache/GridCachePreloader.java    |   23 +-
 .../cache/GridCachePreloaderAdapter.java        |   46 +-
 .../processors/cache/GridCacheProcessor.java    |  476 ++-
 .../cache/GridCacheSharedContext.java           |    4 +-
 .../processors/cache/GridCacheTtlManager.java   |   22 +-
 .../processors/cache/GridCacheUtils.java        |   42 +-
 .../GridChangeGlobalStateMessageResponse.java   |   20 +-
 .../processors/cache/GridNoStorageCacheMap.java |   30 +-
 .../cache/IgniteCacheOffheapManager.java        |  169 +-
 .../cache/IgniteCacheOffheapManagerImpl.java    | 1260 ++++--
 .../cache/affinity/GridCacheAffinityImpl.java   |    9 +-
 .../processors/cache/database/CacheDataRow.java |    5 -
 .../cache/database/CacheDataRowAdapter.java     |   65 +-
 .../cache/database/CacheSearchRow.java          |    5 +
 .../IgniteCacheDatabaseSharedManager.java       |    9 +-
 .../database/IgniteCacheSnapshotManager.java    |   14 +-
 .../processors/cache/database/MetaStore.java    |    6 +-
 .../cache/database/MetadataStorage.java         |   22 +-
 .../processors/cache/database/RowStore.java     |   29 +-
 .../cache/database/tree/BPlusTree.java          |   39 +-
 .../cache/database/tree/io/PageIO.java          |   30 +
 .../tree/io/PagePartitionCountersIO.java        |  175 +
 .../database/tree/io/PagePartitionMetaIO.java   |   20 +
 .../distributed/GridCacheTtlUpdateRequest.java  |    4 +-
 .../distributed/GridDistributedBaseMessage.java |    4 +-
 .../GridDistributedCacheAdapter.java            |   13 +-
 .../GridDistributedTxFinishResponse.java        |   28 +-
 .../GridDistributedTxRemoteAdapter.java         |    2 +
 .../dht/GridCachePartitionedConcurrentMap.java  |   76 +-
 .../dht/GridClientPartitionTopology.java        |   25 +-
 .../dht/GridDhtAffinityAssignmentRequest.java   |   10 +-
 .../dht/GridDhtAffinityAssignmentResponse.java  |   12 +-
 .../dht/GridDhtAssignmentFetchFuture.java       |   22 +-
 .../distributed/dht/GridDhtCacheAdapter.java    |  363 +-
 .../distributed/dht/GridDhtCacheEntry.java      |   16 +-
 .../cache/distributed/dht/GridDhtGetFuture.java |    8 +-
 .../distributed/dht/GridDhtGetSingleFuture.java |   39 +-
 .../distributed/dht/GridDhtLocalPartition.java  |  444 ++-
 .../distributed/dht/GridDhtLockResponse.java    |    2 +-
 .../dht/GridDhtPartitionTopology.java           |    9 +-
 .../dht/GridDhtPartitionTopologyImpl.java       |  278 +-
 .../dht/GridDhtTransactionalCacheAdapter.java   |   37 +-
 .../dht/GridDhtTxFinishResponse.java            |   14 +-
 .../dht/GridDhtTxOnePhaseCommitAckRequest.java  |   16 +-
 .../distributed/dht/GridDhtTxPrepareFuture.java |    4 +-
 .../dht/GridPartitionedGetFuture.java           |    2 +-
 .../dht/GridPartitionedSingleGetFuture.java     |    2 +-
 .../GridDhtAtomicAbstractUpdateRequest.java     |    4 +-
 .../dht/atomic/GridDhtAtomicCache.java          |   68 +-
 .../dht/atomic/GridDhtAtomicCacheEntry.java     |   53 -
 .../GridDhtAtomicDeferredUpdateResponse.java    |    4 +-
 .../dht/atomic/GridDhtAtomicNearResponse.java   |    4 +-
 .../dht/atomic/GridDhtAtomicUpdateResponse.java |    5 +-
 .../GridNearAtomicAbstractUpdateRequest.java    |    4 +-
 .../GridNearAtomicCheckUpdateRequest.java       |    4 +-
 .../atomic/GridNearAtomicUpdateResponse.java    |    4 +-
 .../dht/colocated/GridDhtColocatedCache.java    |   24 +-
 .../colocated/GridDhtColocatedCacheEntry.java   |   52 -
 .../dht/preloader/GridDhtForceKeysFuture.java   |   15 +-
 .../dht/preloader/GridDhtForceKeysRequest.java  |    4 +-
 .../dht/preloader/GridDhtForceKeysResponse.java |    8 +-
 .../GridDhtPartitionDemandMessage.java          |   12 +-
 .../dht/preloader/GridDhtPartitionDemander.java |  262 +-
 .../dht/preloader/GridDhtPartitionSupplier.java |   73 +-
 .../GridDhtPartitionSupplyMessage.java          |  116 +-
 .../GridDhtPartitionsAbstractMessage.java       |   26 +-
 .../GridDhtPartitionsExchangeFuture.java        |  259 +-
 .../preloader/GridDhtPartitionsFullMessage.java |   74 +-
 .../GridDhtPartitionsSingleMessage.java         |   62 +-
 .../GridDhtPartitionsSingleRequest.java         |    7 +-
 .../dht/preloader/GridDhtPreloader.java         |  419 +-
 .../distributed/near/GridNearAtomicCache.java   |    2 +-
 .../distributed/near/GridNearCacheAdapter.java  |   16 +-
 .../distributed/near/GridNearGetRequest.java    |    4 +-
 .../distributed/near/GridNearGetResponse.java   |    4 +-
 .../near/GridNearOptimisticTxPrepareFuture.java |   21 +-
 .../near/GridNearSingleGetRequest.java          |    4 +-
 .../near/GridNearSingleGetResponse.java         |    6 +-
 .../near/GridNearTransactionalCache.java        |    4 +-
 .../near/GridNearTxFinishResponse.java          |   14 +-
 .../processors/cache/local/GridLocalCache.java  |   21 +-
 .../local/atomic/GridLocalAtomicCache.java      |    4 +-
 .../query/GridCacheDistributedQueryManager.java |   10 +-
 .../cache/query/GridCacheQueryManager.java      |    4 +-
 .../cache/query/GridCacheQueryRequest.java      |    3 +-
 .../cache/query/GridCacheQueryResponse.java     |    4 +-
 .../CacheContinuousQueryBatchAck.java           |    4 +-
 .../CacheContinuousQueryEventBuffer.java        |    5 +-
 .../continuous/CacheContinuousQueryHandler.java |   66 +-
 .../CacheContinuousQueryListener.java           |   20 +
 .../continuous/CacheContinuousQueryManager.java |   59 +-
 .../query/continuous/CounterSkipContext.java    |   78 +
 .../cache/transactions/IgniteTxAdapter.java     |    2 +-
 .../cache/transactions/IgniteTxEntry.java       |    4 +-
 .../cache/transactions/IgniteTxHandler.java     |   26 +-
 .../transactions/IgniteTxLocalAdapter.java      |    2 +
 .../cache/transactions/TxLocksRequest.java      |   20 +-
 .../cache/transactions/TxLocksResponse.java     |   28 +-
 .../cluster/GridClusterStateProcessor.java      |    9 +-
 .../processors/query/GridQueryProcessor.java    |    4 +-
 .../internal/processors/query/QueryUtils.java   |   29 +-
 .../visor/cache/VisorCachePartitionsTask.java   |    2 +-
 .../CacheAtomicSingleMessageCountSelfTest.java  |    2 +-
 .../cache/CacheDeferredDeleteQueueTest.java     |    2 +-
 ...cheDhtLocalPartitionAfterRemoveSelfTest.java |    2 +-
 ...CacheExchangeMessageDuplicatedStateTest.java |   54 +-
 .../cache/CacheGroupsMetricsRebalanceTest.java  |  140 +
 .../cache/CacheOffheapMapEntrySelfTest.java     |    9 +-
 .../GridCacheConditionalDeploymentSelfTest.java |   18 +
 .../processors/cache/GridCacheLeakTest.java     |    3 +-
 .../GridCacheOrderedPreloadingSelfTest.java     |   14 +-
 .../cache/GridCacheTtlManagerSelfTest.java      |    3 +-
 .../processors/cache/IgniteCacheGroupsTest.java | 3765 ++++++++++++++++++
 .../cache/IgniteCachePeekModesAbstractTest.java |    2 +-
 .../processors/cache/IgniteCacheStartTest.java  |    5 +-
 .../cache/IgniteOnePhaseCommitInvokeTest.java   |    4 +-
 ...niteTopologyValidatorGridSplitCacheTest.java |    8 +-
 .../IgniteTxStoreExceptionAbstractSelfTest.java |    4 +-
 .../GridCacheBinaryObjectsAbstractSelfTest.java |    2 +-
 .../GridCacheQueueCleanupSelfTest.java          |   13 +-
 .../GridCacheSetAbstractSelfTest.java           |   17 +-
 .../GridCacheSetFailoverAbstractSelfTest.java   |    6 +-
 .../IgnitePartitionedQueueNoBackupsTest.java    |    6 +-
 .../IgnitePartitionedSetNoBackupsSelfTest.java  |    6 +-
 .../CacheDiscoveryDataConcurrentJoinTest.java   |   17 +
 .../distributed/CacheGroupsPreloadTest.java     |  194 +
 .../CacheLateAffinityAssignmentTest.java        |    6 +-
 .../IgniteCachePartitionLossPolicySelfTest.java |   19 +-
 .../IgniteCacheReadFromBackupTest.java          |    5 +-
 ...sabledMultiNodeWithGroupFullApiSelfTest.java |   35 +
 .../atomic/IgniteCacheAtomicProtocolTest.java   |    7 +-
 ...AtomicMultiNodeWithGroupFullApiSelfTest.java |   34 +
 ...nabledMultiNodeWithGroupFullApiSelfTest.java |   35 +
 .../near/GridCacheNearReadersSelfTest.java      |    4 +-
 ...tionedMultiNodeWithGroupFullApiSelfTest.java |   34 +
 .../GridCacheReplicatedPreloadSelfTest.java     |    3 +-
 .../IgniteCacheClientNearCacheExpiryTest.java   |   20 +-
 .../expiry/IgniteCacheTtlCleanupSelfTest.java   |    2 +-
 ...IgniteCacheJdbcBlobStoreNodeRestartTest.java |    3 +
 ...acheLocalAtomicWithGroupFullApiSelfTest.java |   34 +
 .../local/GridCacheLocalFullApiSelfTest.java    |    1 -
 .../GridCacheLocalWithGroupFullApiSelfTest.java |   34 +
 ...nuousQueryConcurrentPartitionUpdateTest.java |  229 +-
 ...ContinuousQueryFailoverAbstractSelfTest.java |    7 +-
 .../TxOptimisticDeadlockDetectionTest.java      |    2 +-
 .../TxPessimisticDeadlockDetectionTest.java     |    2 +-
 .../loadtests/hashmap/GridCacheTestContext.java |    6 +-
 .../communication/GridCacheMessageSelfTest.java |   30 +
 .../ignite/testframework/GridTestUtils.java     |    9 +
 .../testframework/junits/GridAbstractTest.java  |   17 +
 .../junits/common/GridCommonAbstractTest.java   |   64 +
 .../IgniteCacheFullApiSelfTestSuite.java        |   13 +
 .../IgniteCacheMetricsSelfTestSuite.java        |    3 +
 .../testsuites/IgniteCacheTestSuite3.java       |    4 +
 .../testsuites/IgniteCacheTestSuite4.java       |    2 +
 .../processors/query/h2/IgniteH2Indexing.java   |    3 +-
 .../query/h2/database/H2PkHashIndex.java        |    4 +-
 .../query/h2/database/H2RowFactory.java         |    2 +-
 .../processors/query/h2/database/H2Tree.java    |    6 +-
 .../query/h2/database/H2TreeIndex.java          |   26 +-
 .../h2/twostep/GridReduceQueryExecutor.java     |   12 +-
 .../cache/IgniteCacheGroupsSqlTest.java         |  317 ++
 .../cache/IgniteCacheNoClassQuerySelfTest.java  |   23 +-
 .../IgniteCacheWithIndexingTestSuite.java       |    3 +
 .../GridCacheDatabaseSharedManager.java         |  298 +-
 .../cache/database/GridCacheOffheapManager.java |  490 ++-
 .../database/file/FilePageStoreManager.java     |  202 +-
 .../cache/database/pagemem/PageMemoryEx.java    |    8 +-
 .../cache/database/pagemem/PageMemoryImpl.java  |   10 +-
 .../wal/serializer/RecordV1Serializer.java      |   12 +-
 .../IgnitePersistentStoreCacheGroupsTest.java   |  516 +++
 ...IgnitePersistentStoreDataStructuresTest.java |  205 +
 ...tentStoreMultiNodePutGetRestartSelfTest.java |    2 -
 ...dexingAndGroupPutGetPersistenceSelfTest.java |   42 +
 .../database/pagemem/NoOpPageStoreManager.java  |   22 +-
 .../ignite/testsuites/IgnitePdsTestSuite.java   |    5 +
 .../ignite/testsuites/IgnitePdsTestSuite2.java  |    5 +-
 modules/yardstick/pom.xml                       |    6 +
 .../yardstick/IgniteBenchmarkArguments.java     |   39 +-
 .../org/apache/ignite/yardstick/IgniteNode.java |   23 +-
 .../cache/IgniteCacheAbstractBenchmark.java     |  158 +
 .../yardstick/cache/IgniteGetAllBenchmark.java  |    3 +
 .../cache/IgniteGetAllPutAllTxBenchmark.java    |    2 +
 .../cache/IgniteGetAndPutBenchmark.java         |    2 +
 .../cache/IgniteGetAndPutTxBenchmark.java       |    2 +
 .../yardstick/cache/IgniteGetBenchmark.java     |   35 +-
 .../IgniteGetEntriesPutAllTxBenchmark.java      |    2 +
 .../yardstick/cache/IgniteInvokeBenchmark.java  |    2 +
 .../cache/IgniteInvokeTxBenchmark.java          |    2 +
 .../IgniteInvokeWithInjectionBenchmark.java     |    2 +
 .../yardstick/cache/IgnitePutAllBenchmark.java  |    2 +
 .../IgnitePutAllSerializableTxBenchmark.java    |    2 +
 .../yardstick/cache/IgnitePutBenchmark.java     |    2 +
 .../cache/IgnitePutGetBatchBenchmark.java       |    2 +
 .../yardstick/cache/IgnitePutGetBenchmark.java  |    2 +
 .../cache/IgnitePutGetEntryBenchmark.java       |    2 +
 .../cache/IgnitePutGetEntryTxBenchmark.java     |    2 +
 .../cache/IgnitePutGetTxBatchBenchmark.java     |    2 +
 .../cache/IgnitePutGetTxBenchmark.java          |    2 +
 ...IgnitePutIfAbsentIndexedValue1Benchmark.java |    2 +
 .../cache/IgnitePutIndexedValue1Benchmark.java  |    2 +
 .../cache/IgnitePutIndexedValue2Benchmark.java  |    2 +
 .../cache/IgnitePutIndexedValue8Benchmark.java  |    2 +
 .../IgnitePutRandomValueSizeBenchmark.java      |    2 +
 .../cache/IgnitePutRemoveBenchmark.java         |    2 +
 .../yardstick/cache/IgnitePutTxBenchmark.java   |    2 +
 .../cache/IgnitePutTxImplicitBenchmark.java     |    2 +
 .../cache/IgnitePutTxPrimaryOnlyBenchmark.java  |    2 +
 .../IgnitePutTxSkipLocalBackupBenchmark.java    |    2 +
 .../cache/IgnitePutValue8Benchmark.java         |    2 +
 .../IgniteReplaceIndexedValue1Benchmark.java    |    2 +
 .../cache/IgniteScanQueryBenchmark.java         |   88 +
 .../cache/IgniteSqlQueryBenchmark.java          |   16 +-
 .../IgniteSqlQueryDistributedJoinBenchmark.java |   24 +-
 .../cache/IgniteSqlQueryJoinBenchmark.java      |   14 +-
 .../cache/IgniteSqlQueryPutBenchmark.java       |    8 +-
 .../IgniteSqlQueryPutSeparatedBenchmark.java    |    4 +
 .../cache/jdbc/JdbcPutGetBenchmark.java         |    4 +-
 .../IgniteCacheRandomOperationBenchmark.java    |   46 +-
 .../yardstick/cache/load/model/ModelUtil.java   |    6 +-
 260 files changed, 14449 insertions(+), 4079 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index 21f2fba..67b7590 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -202,6 +202,9 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
     /** Cache name. */
     private String name;
 
+    /** Cache group name. */
+    private String grpName;
+
     /** Name of {@link MemoryPolicyConfiguration} for this cache */
     private String memPlcName;
 
@@ -408,6 +411,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
         evictFilter = cc.getEvictionFilter();
         evictPlc = cc.getEvictionPolicy();
         expiryPolicyFactory = cc.getExpiryPolicyFactory();
+        grpName = cc.getGroupName();
         indexedTypes = cc.getIndexedTypes();
         interceptor = cc.getInterceptor();
         invalidate = cc.isInvalidate();
@@ -455,6 +459,23 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
     }
 
     /**
+     * @return Cache group name.
+     */
+    public String getGroupName() {
+        return grpName;
+    }
+
+    /**
+     * @param grpName Cache group name.
+     * @return {@code this} for chaining.
+     */
+    public CacheConfiguration<K, V> setGroupName(String grpName) {
+        this.grpName = grpName;
+
+        return this;
+    }
+
+    /**
      * Cache name or {@code null} if not provided, then this will be considered a default
      * cache which can be accessed via {@link Ignite#cache(String)} method. Otherwise, if name
      * is provided, the cache will be accessed via {@link Ignite#cache(String)} method.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
index 698baf8..ea49dbe 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/GridIoManager.java
@@ -1008,7 +1008,7 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
                 }
 
                 default:
-                    assert plc >= 0 : "Negative policy: " + plc;
+                    assert plc >= 0 : "Negative policy [plc=" + plc + ", msg=" + msg + ']';
 
                     if (isReservedGridIoPolicy(plc))
                         throw new IgniteCheckedException("Failed to process message with policy of reserved range. " +
@@ -1153,10 +1153,14 @@ public class GridIoManager extends GridManagerAdapter<CommunicationSpi<Serializa
             pools.poolForPolicy(plc).execute(c);
         }
         catch (RejectedExecutionException e) {
-            U.error(log, "Failed to process regular message due to execution rejection. Will attempt to process " +
-                "message in the listener thread instead.", e);
+            if (!ctx.isStopping()) {
+                U.error(log, "Failed to process regular message due to execution rejection. Will attempt to process " +
+                        "message in the listener thread instead.", e);
 
-            c.run();
+                c.run();
+            }
+            else if (log.isDebugEnabled())
+                log.debug("Failed to process regular message due to execution rejection: " + msg);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
index 5247ac1..22c2d07 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/DiscoCache.java
@@ -69,7 +69,7 @@ public class DiscoCache {
 
     /** Affinity cache nodes by cache name. */
     @GridToStringInclude
-    private final Map<Integer, List<ClusterNode>> affCacheNodes;
+    private final Map<Integer, List<ClusterNode>> cacheGrpAffNodes;
 
     /** Node map. */
     private final Map<UUID, ClusterNode> nodeMap;
@@ -91,7 +91,7 @@ public class DiscoCache {
      * @param allNodesWithCaches All nodes with at least one cache configured.
      * @param rmtNodesWithCaches Remote nodes with at least one cache configured.
      * @param allCacheNodes Cache nodes by cache name.
-     * @param affCacheNodes Affinity cache nodes by cache name.
+     * @param cacheGrpAffNodes Affinity nodes by cache group ID.
      * @param nodeMap Node map.
      * @param nearEnabledCaches Caches where at least one node has near cache enabled.
      * @param alives Alive nodes.
@@ -105,7 +105,7 @@ public class DiscoCache {
         List<ClusterNode> allNodesWithCaches,
         List<ClusterNode> rmtNodesWithCaches,
         Map<Integer, List<ClusterNode>> allCacheNodes,
-        Map<Integer, List<ClusterNode>> affCacheNodes,
+        Map<Integer, List<ClusterNode>> cacheGrpAffNodes,
         Map<UUID, ClusterNode> nodeMap,
         Set<Integer> nearEnabledCaches,
         Set<UUID> alives) {
@@ -118,7 +118,7 @@ public class DiscoCache {
         this.allNodesWithCaches = allNodesWithCaches;
         this.rmtNodesWithCaches = rmtNodesWithCaches;
         this.allCacheNodes = allCacheNodes;
-        this.affCacheNodes = affCacheNodes;
+        this.cacheGrpAffNodes = cacheGrpAffNodes;
         this.nodeMap = nodeMap;
         this.nearEnabledCaches = nearEnabledCaches;
         this.alives.addAll(alives);
@@ -235,25 +235,11 @@ public class DiscoCache {
     }
 
     /**
-     * Gets all nodes that have cache with given ID and should participate in affinity calculation. With
-     * partitioned cache nodes with near-only cache do not participate in affinity node calculation.
-     *
-     * @param cacheName Cache name.
-     * @return Collection of nodes.
-     */
-    public List<ClusterNode> cacheAffinityNodes(@Nullable String cacheName) {
-        return cacheAffinityNodes(CU.cacheId(cacheName));
-    }
-
-    /**
-     * Gets all nodes that have cache with given ID and should participate in affinity calculation. With
-     * partitioned cache nodes with near-only cache do not participate in affinity node calculation.
-     *
-     * @param cacheId Cache ID.
-     * @return Collection of nodes.
+     * @param grpId Cache group ID.
+     * @return All nodes that participate in affinity calculation.
      */
-    public List<ClusterNode> cacheAffinityNodes(int cacheId) {
-        return emptyIfNull(affCacheNodes.get(cacheId));
+    public List<ClusterNode> cacheGroupAffinityNodes(int grpId) {
+        return emptyIfNull(cacheGrpAffNodes.get(grpId));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
index 7c702c2..e144d9a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/discovery/GridDiscoveryManager.java
@@ -69,7 +69,7 @@ import org.apache.ignite.internal.managers.GridManagerAdapter;
 import org.apache.ignite.internal.managers.communication.GridIoManager;
 import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.jobmetrics.GridJobMetrics;
 import org.apache.ignite.internal.processors.security.SecurityContext;
@@ -251,12 +251,15 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
         new ConcurrentHashMap8<>();
 
     /** Local node initialization event listeners. */
-    private final Collection<IgniteInClosure<ClusterNode>> localNodeInitLsnrs = new ArrayList<>();
+    private final Collection<IgniteInClosure<ClusterNode>> locNodeInitLsnrs = new ArrayList<>();
 
     /** Map of dynamic cache filters. */
     private Map<String, CachePredicate> registeredCaches = new HashMap<>();
 
     /** */
+    private Map<Integer, CacheGroupAffinity> registeredCacheGrps = new HashMap<>();
+
+    /** */
     private final GridSpinBusyLock busyLock = new GridSpinBusyLock();
 
     /** Received custom messages history. */
@@ -321,24 +324,55 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
     }
 
     /**
+     *
+     */
+    public void onLocalNodeJoin() {
+        registeredCacheGrps.clear();
+        registeredCaches.clear();
+    }
+
+    /**
+     * @param grpDesc Cache group descriptor.
+     * @param filter Node filter.
+     * @param cacheMode Cache mode.
+     */
+    public void addCacheGroup(CacheGroupDescriptor grpDesc, IgnitePredicate<ClusterNode> filter, CacheMode cacheMode) {
+        CacheGroupAffinity old = registeredCacheGrps.put(grpDesc.groupId(),
+            new CacheGroupAffinity(grpDesc.cacheOrGroupName(), filter, cacheMode));
+
+        assert old == null : old;
+    }
+
+    /**
+     * @param grpDesc Cache group descriptor.
+     */
+    public void removeCacheGroup(CacheGroupDescriptor grpDesc) {
+        CacheGroupAffinity rmvd = registeredCacheGrps.remove(grpDesc.groupId());
+
+        assert rmvd != null : grpDesc.cacheOrGroupName();
+    }
+
+    /**
      * Adds dynamic cache filter.
      *
+     * @param grpId Cache group ID.
      * @param cacheName Cache name.
-     * @param filter Cache filter.
      * @param nearEnabled Near enabled flag.
-     * @param cacheMode Cache mode.
      */
     public void setCacheFilter(
+        int grpId,
         String cacheName,
-        IgnitePredicate<ClusterNode> filter,
-        boolean nearEnabled,
-        CacheMode cacheMode
+        boolean nearEnabled
     ) {
         if (!registeredCaches.containsKey(cacheName)) {
-            if (cacheMode == CacheMode.REPLICATED)
+            CacheGroupAffinity grp = registeredCacheGrps.get(grpId);
+
+            assert grp != null : "Failed to find cache group [grpId=" + grpId + ", cache=" + cacheName + ']';
+
+            if (grp.cacheMode == CacheMode.REPLICATED)
                 nearEnabled = false;
-            
-            registeredCaches.put(cacheName, new CachePredicate(filter, nearEnabled, cacheMode));
+
+            registeredCaches.put(cacheName, new CachePredicate(grp, nearEnabled));
         }
     }
 
@@ -401,7 +435,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
             }
         }
 
-        return res;
+        return res == null ? Collections.<String, Map<UUID,Boolean>>emptyMap() : res;
     }
 
     /**
@@ -493,7 +527,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
 
             /** {@inheritDoc} */
             @Override public void onLocalNodeInitialized(ClusterNode locNode) {
-                for (IgniteInClosure<ClusterNode> lsnr : localNodeInitLsnrs)
+                for (IgniteInClosure<ClusterNode> lsnr : locNodeInitLsnrs)
                     lsnr.apply(locNode);
             }
 
@@ -639,6 +673,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
                     locJoin = new GridFutureAdapter<>();
 
                     registeredCaches.clear();
+                    registeredCacheGrps.clear();
 
                     for (AffinityTopologyVersion histVer : discoCacheHist.keySet()) {
                         Object rmvd = discoCacheHist.remove(histVer);
@@ -801,7 +836,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
      * @param lsnr Listener to add.
      */
     public void addLocalNodeInitializedEventListener(IgniteInClosure<ClusterNode> lsnr) {
-        localNodeInitLsnrs.add(lsnr);
+        locNodeInitLsnrs.add(lsnr);
     }
 
     /**
@@ -1742,27 +1777,14 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
     }
 
     /**
-     * Gets cache nodes for cache with given name that participate in affinity calculation.
-     *
-     * @param cacheName Cache name.
-     * @param topVer Topology version.
-     * @return Collection of cache affinity nodes.
-     */
-    public Collection<ClusterNode> cacheAffinityNodes(@Nullable String cacheName, AffinityTopologyVersion topVer) {
-        int cacheId = CU.cacheId(cacheName);
-
-        return resolveDiscoCache(cacheId, topVer).cacheAffinityNodes(cacheId);
-    }
-
-    /**
      * Gets cache nodes for cache with given ID that participate in affinity calculation.
      *
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param topVer Topology version.
      * @return Collection of cache affinity nodes.
      */
-    public Collection<ClusterNode> cacheAffinityNodes(int cacheId, AffinityTopologyVersion topVer) {
-        return resolveDiscoCache(cacheId, topVer).cacheAffinityNodes(cacheId);
+    public Collection<ClusterNode> cacheGroupAffinityNodes(int grpId, AffinityTopologyVersion topVer) {
+        return resolveDiscoCache(grpId, topVer).cacheGroupAffinityNodes(grpId);
     }
 
     /**
@@ -1779,6 +1801,19 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
     }
 
     /**
+     * Checks if node is a data node for the given cache group.
+     *
+     * @param node Node to check.
+     * @param grpId Cache group ID.
+     * @return {@code True} if node is a cache data node.
+     */
+    public boolean cacheGroupAffinityNode(ClusterNode node, int grpId) {
+        CacheGroupAffinity aff = registeredCacheGrps.get(grpId);
+
+        return CU.affinityNode(node, aff.cacheFilter);
+    }
+
+    /**
      * @param node Node to check.
      * @param cacheName Cache name.
      * @return {@code True} if node has near cache enabled.
@@ -1825,7 +1860,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
 
             if (!CU.isSystemCache(cacheName) && !CU.isIgfsCache(ctx.config(), cacheName) &&
                 pred != null && pred.cacheNode(node))
-                caches.put(cacheName, pred.cacheMode);
+                caches.put(cacheName, pred.aff.cacheMode);
         }
 
         return caches;
@@ -1845,21 +1880,21 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
     /**
      * Gets discovery cache for given topology version.
      *
-     * @param cacheId Cache ID (participates in exception message).
+     * @param grpId Cache group ID (participates in exception message).
      * @param topVer Topology version.
      * @return Discovery cache.
      */
-    private DiscoCache resolveDiscoCache(int cacheId, AffinityTopologyVersion topVer) {
+    private DiscoCache resolveDiscoCache(int grpId, AffinityTopologyVersion topVer) {
         Snapshot snap = topSnap.get();
 
         DiscoCache cache = AffinityTopologyVersion.NONE.equals(topVer) || topVer.equals(snap.topVer) ?
             snap.discoCache : discoCacheHist.get(topVer);
 
         if (cache == null) {
-            DynamicCacheDescriptor desc = ctx.cache().cacheDescriptor(cacheId);
+            CacheGroupDescriptor desc = ctx.cache().cacheGroupDescriptors().get(grpId);
 
             throw new IgniteException("Failed to resolve nodes topology [" +
-                "cacheName=" + (desc != null ? desc.cacheConfiguration().getName() : "N/A") +
+                "cacheGrp=" + (desc != null ? desc.cacheOrGroupName() : "N/A") +
                 ", topVer=" + topVer +
                 ", history=" + discoCacheHist.keySet() +
                 ", snap=" + snap +
@@ -2073,7 +2108,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
             " [rmtNodes=" + rmtNodes + ", loc=" + loc + ']';
 
         Map<Integer, List<ClusterNode>> allCacheNodes = U.newHashMap(allNodes.size());
-        Map<Integer, List<ClusterNode>> affCacheNodes = U.newHashMap(allNodes.size());
+        Map<Integer, List<ClusterNode>> cacheGrpAffNodes = U.newHashMap(allNodes.size());
 
         Set<ClusterNode> allNodesWithCaches = new TreeSet<>(GridNodeOrderComparator.INSTANCE);
         Set<ClusterNode> rmtNodesWithCaches = new TreeSet<>(GridNodeOrderComparator.INSTANCE);
@@ -2085,6 +2120,20 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
             assert node.order() != 0 : "Invalid node order [locNode=" + loc + ", node=" + node + ']';
             assert !node.isDaemon();
 
+            for (Map.Entry<Integer, CacheGroupAffinity> e : registeredCacheGrps.entrySet()) {
+                CacheGroupAffinity grpAff = e.getValue();
+                Integer grpId = e.getKey();
+
+                if (CU.affinityNode(node, grpAff.cacheFilter)) {
+                    List<ClusterNode> nodes = cacheGrpAffNodes.get(grpId);
+
+                    if (nodes == null)
+                        cacheGrpAffNodes.put(grpId, nodes = new ArrayList<>());
+
+                    nodes.add(node);
+                }
+            }
+
             for (Map.Entry<String, CachePredicate> entry : registeredCaches.entrySet()) {
                 String cacheName = entry.getKey();
                 CachePredicate filter = entry.getValue();
@@ -2100,9 +2149,6 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
 
                     addToMap(allCacheNodes, cacheName, node);
 
-                    if (filter.dataNode(node))
-                        addToMap(affCacheNodes, cacheName, node);
-
                     if (filter.nearNode(node))
                         nearEnabledCaches.add(CU.cacheId(cacheName));
                 }
@@ -2119,7 +2165,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
             U.sealList(allNodesWithCaches),
             U.sealList(rmtNodesWithCaches),
             Collections.unmodifiableMap(allCacheNodes),
-            Collections.unmodifiableMap(affCacheNodes),
+            Collections.unmodifiableMap(cacheGrpAffNodes),
             Collections.unmodifiableMap(nodeMap),
             Collections.unmodifiableSet(nearEnabledCaches),
             alives);
@@ -2732,32 +2778,60 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
     }
 
     /**
+     *
+     */
+    private static class CacheGroupAffinity {
+        /** */
+        private final String name;
+
+        /** Nodes filter. */
+        private final IgnitePredicate<ClusterNode> cacheFilter;
+
+        /** Cache mode. */
+        private final CacheMode cacheMode;
+
+        /**
+         * @param name Name.
+         * @param cacheFilter Node filter.
+         * @param cacheMode Cache mode.
+         */
+        CacheGroupAffinity(
+            String name,
+            IgnitePredicate<ClusterNode> cacheFilter,
+            CacheMode cacheMode) {
+            this.name = name;
+            this.cacheFilter = cacheFilter;
+            this.cacheMode = cacheMode;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "CacheGroupAffinity [name=" + name + ']';
+        }
+    }
+
+    /**
      * Cache predicate.
      */
     private static class CachePredicate {
         /** Cache filter. */
-        private final IgnitePredicate<ClusterNode> cacheFilter;
+        private final CacheGroupAffinity aff;
 
         /** If near cache is enabled on data nodes. */
         private final boolean nearEnabled;
 
-        /** Cache mode. */
-        private final CacheMode cacheMode;
-
         /** Collection of client near nodes. */
         private final ConcurrentHashMap<UUID, Boolean> clientNodes;
 
         /**
-         * @param cacheFilter Cache filter.
+         * @param aff Cache group affinity.
          * @param nearEnabled Near enabled flag.
-         * @param cacheMode Cache mode.
          */
-        private CachePredicate(IgnitePredicate<ClusterNode> cacheFilter, boolean nearEnabled, CacheMode cacheMode) {
-            assert cacheFilter != null;
+        private CachePredicate(CacheGroupAffinity aff, boolean nearEnabled) {
+            assert aff != null;
 
-            this.cacheFilter = cacheFilter;
+            this.aff = aff;
             this.nearEnabled = nearEnabled;
-            this.cacheMode = cacheMode;
 
             clientNodes = new ConcurrentHashMap<>();
         }
@@ -2792,7 +2866,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
          * @return {@code True} if this node is a data node for given cache.
          */
         public boolean dataNode(ClusterNode node) {
-            return CU.affinityNode(node, cacheFilter);
+            return CU.affinityNode(node, aff.cacheFilter);
         }
 
         /**
@@ -2800,7 +2874,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
          * @return {@code True} if cache is accessible on the given node.
          */
         public boolean cacheNode(ClusterNode node) {
-            return !node.isDaemon() && (CU.affinityNode(node, cacheFilter) || clientNodes.containsKey(node.id()));
+            return !node.isDaemon() && (CU.affinityNode(node, aff.cacheFilter) || clientNodes.containsKey(node.id()));
         }
 
         /**
@@ -2808,7 +2882,7 @@ public class GridDiscoveryManager extends GridManagerAdapter<DiscoverySpi> {
          * @return {@code True} if near cache is present on the given nodes.
          */
         public boolean nearNode(ClusterNode node) {
-            if (CU.affinityNode(node, cacheFilter))
+            if (CU.affinityNode(node, aff.cacheFilter))
                 return nearEnabled;
 
             Boolean near = clientNodes.get(node.id());

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java
index f824368..3fa5954 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/PageUtils.java
@@ -138,6 +138,22 @@ public class PageUtils {
     /**
      * @param addr Address.
      * @param off Offset.
+     * @param bytes Bytes array.
+     * @param bytesOff Bytes array offset.
+     * @param len Length.
+     */
+    public static void putBytes(long addr, int off, byte[] bytes, int bytesOff, int len) {
+        assert addr > 0 : addr;
+        assert off >= 0;
+        assert bytes != null;
+        assert bytesOff >= 0 && (bytesOff < bytes.length || bytes.length == 0) : bytesOff;
+
+        GridUnsafe.copyMemory(bytes, GridUnsafe.BYTE_ARR_OFF + bytesOff, null, addr + off, len);
+    }
+
+    /**
+     * @param addr Address.
+     * @param off Offset.
      * @param v Value.
      */
     public static void putByte(long addr, int off, byte v) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/snapshot/SnapshotOperation.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/snapshot/SnapshotOperation.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/snapshot/SnapshotOperation.java
index 39a76dd..bdcc05a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/snapshot/SnapshotOperation.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/snapshot/SnapshotOperation.java
@@ -37,7 +37,10 @@ public class SnapshotOperation implements Serializable {
      */
     private final long snapshotId;
 
-    /** */
+    /** Cache group ids. */
+    private final Set<Integer> cacheGrpIds;
+
+    /** Cache names. */
     private final Set<String> cacheNames;
 
     /** Message. */
@@ -49,6 +52,7 @@ public class SnapshotOperation implements Serializable {
     /**
      * @param type Type.
      * @param snapshotId Snapshot id.
+     * @param cacheGrpIds Cache group ids.
      * @param cacheNames Cache names.
      * @param msg
      * @param extraParam Additional parameter.
@@ -56,12 +60,14 @@ public class SnapshotOperation implements Serializable {
     public SnapshotOperation(
         SnapshotOperationType type,
         long snapshotId,
+        Set<Integer> cacheGrpIds,
         Set<String> cacheNames,
         String msg,
         Object extraParam
     ) {
         this.type = type;
         this.snapshotId = snapshotId;
+        this.cacheGrpIds = cacheGrpIds;
         this.cacheNames = cacheNames;
         this.msg = msg;
         this.extraParam = extraParam;
@@ -84,10 +90,17 @@ public class SnapshotOperation implements Serializable {
     }
 
     /**
-     * Cache names included to this snapshot.
+     * Cache group ids included to this snapshot.
      *
      * @return Cache names.
      */
+    public Set<Integer> cacheGroupIds() {
+        return cacheGrpIds;
+    }
+
+    /**
+     * Cache names included to this snapshot.
+     */
     public Set<String> cacheNames() {
         return cacheNames;
     }
@@ -170,6 +183,7 @@ public class SnapshotOperation implements Serializable {
             "type=" + type +
             ", snapshotId=" + snapshotId +
             ", cacheNames=" + cacheNames +
+            ", cacheGroupIds=" + cacheGrpIds +
             ", msg='" + msg + '\'' +
             ", extraParam=" + extraParam +
             '}';

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
index 0453ecb..11a3804 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/store/IgnitePageStoreManager.java
@@ -17,14 +17,14 @@
 
 package org.apache.ignite.internal.pagemem.store;
 
-import java.util.Set;
+import java.nio.ByteBuffer;
+import java.util.Map;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.pagemem.PageMemory;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedManager;
-
-import java.nio.ByteBuffer;
 import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
 
 /**
@@ -44,39 +44,40 @@ public interface IgnitePageStoreManager extends GridCacheSharedManager, IgniteCh
     /**
      * Callback called when a cache is starting.
      *
+     * @param grpDesc Cache group descriptor.
      * @param ccfg Cache configuration of the cache being started.
      * @throws IgniteCheckedException If failed to handle cache start callback.
      */
-    public void initializeForCache(CacheConfiguration ccfg) throws IgniteCheckedException;
+    public void initializeForCache(CacheGroupDescriptor grpDesc, CacheConfiguration ccfg) throws IgniteCheckedException;
 
     /**
      * Callback called when a cache is stopping. After this callback is invoked, no data associated with
      * the given cache will be stored on disk.
      *
-     * @param cacheCtx Cache context of the cache being stopped.
+     * @param grp Cache group being stopped.
      * @param destroy Flag indicating if the cache is being destroyed and data should be cleaned.
      * @throws IgniteCheckedException If failed to handle cache destroy callback.
      */
-    public void shutdownForCache(GridCacheContext cacheCtx, boolean destroy) throws IgniteCheckedException;
+    public void shutdownForCacheGroup(CacheGroupContext grp, boolean destroy) throws IgniteCheckedException;
 
     /**
      * Callback called when a partition is created on the local node.
      *
-     * @param cacheId Cache ID where the partition is being created.
+     * @param grpId Cache group ID where the partition is being created.
      * @param partId ID of the partition being created.
      * @throws IgniteCheckedException If failed to handle partition create callback.
      */
-    public void onPartitionCreated(int cacheId, int partId) throws IgniteCheckedException;
+    public void onPartitionCreated(int grpId, int partId) throws IgniteCheckedException;
 
     /**
      * Callback called when a partition for the given cache is evicted from the local node.
      * After this callback is invoked, no data associated with the partition will be stored on disk.
      *
-     * @param cacheId Cache ID of the evicted partition.
+     * @param grpId Cache group ID of the evicted partition.
      * @param partId Partition ID.
      * @throws IgniteCheckedException If failed to handle partition destroy callback.
      */
-    public void onPartitionDestroyed(int cacheId, int partId, int tag) throws IgniteCheckedException;
+    public void onPartitionDestroyed(int grpId, int partId, int tag) throws IgniteCheckedException;
 
     /**
      * Reads a page for the given cache ID. Cache ID may be {@code 0} if the page is a meta page.
@@ -89,17 +90,17 @@ public interface IgnitePageStoreManager extends GridCacheSharedManager, IgniteCh
     public void read(int cacheId, long pageId, ByteBuffer pageBuf) throws IgniteCheckedException;
 
     /**
-     * Checks if page exists.
+     * Checks if partition store exists.
      *
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param partId Partition ID.
-     * @return {@code True} if page exists.
+     * @return {@code True} if partition store exists.
      * @throws IgniteCheckedException If failed.
      */
-    public boolean exists(int cacheId, int partId) throws IgniteCheckedException;
+    public boolean exists(int grpId, int partId) throws IgniteCheckedException;
 
     /**
-     * Reads a header of apage store.
+     * Reads a header of a page store.
      *
      * @param cacheId Cache ID.
      * @param partId Partition ID.
@@ -174,19 +175,14 @@ public interface IgnitePageStoreManager extends GridCacheSharedManager, IgniteCh
     public long metaPageId(int cacheId);
 
     /**
-     * @return set of cache names which configurations were saved
-     */
-    public Set<String> savedCacheNames();
-
-    /**
-     * @param cacheName Cache name.
-     * @return saved configuration for cache
+     * @return Saved cache configurations.
+     * @throws IgniteCheckedException If failed.
      */
-    public CacheConfiguration readConfiguration(String cacheName);
+    public Map<String, CacheConfiguration> readCacheConfigurations() throws IgniteCheckedException;
 
     /**
-     * @param cacheId Cache ID.
-     * @return {@code True} if index store for given cache existed before node started.
+     * @param grpId Cache group ID.
+     * @return {@code True} if index store for given cache group existed before node started.
      */
-    public boolean hasIndexStore(int cacheId);
+    public boolean hasIndexStore(int grpId);
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/CheckpointRecord.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/CheckpointRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/CheckpointRecord.java
index 7aaf1c5..cfcd62a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/CheckpointRecord.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/CheckpointRecord.java
@@ -35,7 +35,7 @@ public class CheckpointRecord extends WALRecord {
     private boolean end;
 
     /** */
-    private Map<Integer, CacheState> cacheStates;
+    private Map<Integer, CacheState> cacheGrpStates;
 
     /** Safe replay pointer. */
     private WALPointer cpMark;
@@ -65,28 +65,28 @@ public class CheckpointRecord extends WALRecord {
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param state Cache state.
      */
-    public void addCacheState(int cacheId, CacheState state) {
-        if (cacheStates == null)
-            cacheStates = new HashMap<>();
+    public void addCacheGroupState(int grpId, CacheState state) {
+        if (cacheGrpStates == null)
+            cacheGrpStates = new HashMap<>();
 
-        cacheStates.put(cacheId, state);
+        cacheGrpStates.put(grpId, state);
     }
 
     /**
-     * @param cacheStates Cache states.
+     * @param cacheGrpStates Cache states.
      */
-    public void cacheStates(Map<Integer, CacheState> cacheStates) {
-        this.cacheStates = cacheStates;
+    public void cacheGroupStates(Map<Integer, CacheState> cacheGrpStates) {
+        this.cacheGrpStates = cacheGrpStates;
     }
 
     /**
      * @return Cache states.
      */
-    public Map<Integer, CacheState> cacheStates() {
-        return cacheStates != null ? cacheStates : Collections.<Integer, CacheState>emptyMap();
+    public Map<Integer, CacheState> cacheGroupStates() {
+        return cacheGrpStates != null ? cacheGrpStates : Collections.<Integer, CacheState>emptyMap();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecord.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecord.java
index ef57c46..b28dd52 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecord.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecord.java
@@ -41,6 +41,9 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
     /** */
     private int allocatedIdxCandidate;
 
+    /** */
+    private long cntrsPageId;
+
     /**
      * @param cacheId Cache ID.
      * @param pageId Page ID.
@@ -52,7 +55,7 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
         long updateCntr,
         long globalRmvId,
         int partSize,
-        byte state,
+        long cntrsPageId, byte state,
         int allocatedIdxCandidate
     ) {
         super(cacheId, pageId);
@@ -62,6 +65,7 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
         this.partSize = partSize;
         this.state = state;
         this.allocatedIdxCandidate = allocatedIdxCandidate;
+        this.cntrsPageId = cntrsPageId;
     }
 
     /**
@@ -86,6 +90,13 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
     }
 
     /**
+     * @return Partition size.
+     */
+    public long countersPageId() {
+        return cntrsPageId;
+    }
+
+    /**
      * @return Partition state
      */
     public byte state() {
@@ -99,6 +110,7 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
         io.setUpdateCounter(pageAddr, updateCntr);
         io.setGlobalRemoveId(pageAddr, globalRmvId);
         io.setSize(pageAddr, partSize);
+        io.setCountersPageId(pageAddr, cntrsPageId);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
index 4830f06..b478462 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
@@ -60,11 +60,11 @@ public class GridAffinityAssignmentCache {
     /** Cleanup history size. */
     private final int MAX_HIST_SIZE = getInteger(IGNITE_AFFINITY_HISTORY_SIZE, 500);
 
-    /** Cache name. */
-    private final String cacheName;
+    /** Group name if specified or cache name. */
+    private final String cacheOrGrpName;
 
-    /** */
-    private final int cacheId;
+    /** Group ID. */
+    private final int grpId;
 
     /** Number of backups. */
     private final int backups;
@@ -115,7 +115,8 @@ public class GridAffinityAssignmentCache {
      * Constructs affinity cached calculations.
      *
      * @param ctx Kernal context.
-     * @param cacheName Cache name.
+     * @param cacheOrGrpName Cache or cache group name.
+     * @param grpId Group ID.
      * @param aff Affinity function.
      * @param nodeFilter Node filter.
      * @param backups Number of backups.
@@ -123,7 +124,8 @@ public class GridAffinityAssignmentCache {
      */
     @SuppressWarnings("unchecked")
     public GridAffinityAssignmentCache(GridKernalContext ctx,
-        String cacheName,
+        String cacheOrGrpName,
+        int grpId,
         AffinityFunction aff,
         IgnitePredicate<ClusterNode> nodeFilter,
         int backups,
@@ -132,16 +134,16 @@ public class GridAffinityAssignmentCache {
         assert ctx != null;
         assert aff != null;
         assert nodeFilter != null;
+        assert grpId != 0;
 
         this.ctx = ctx;
         this.aff = aff;
         this.nodeFilter = nodeFilter;
-        this.cacheName = cacheName;
+        this.cacheOrGrpName = cacheOrGrpName;
+        this.grpId = grpId;
         this.backups = backups;
         this.locCache = locCache;
 
-        cacheId = CU.cacheId(cacheName);
-
         log = ctx.log(GridAffinityAssignmentCache.class);
 
         partsCnt = aff.partitions();
@@ -161,17 +163,17 @@ public class GridAffinityAssignmentCache {
     }
 
     /**
-     * @return Cache name.
+     * @return Group name if it is specified, otherwise cache name.
      */
-    public String cacheName() {
-        return cacheName;
+    public String cacheOrGroupName() {
+        return cacheOrGrpName;
     }
 
     /**
-     * @return Cache ID.
+     * @return Cache group ID.
      */
-    public int cacheId() {
-        return cacheId;
+    public int groupId() {
+        return grpId;
     }
 
     /**
@@ -269,7 +271,7 @@ public class GridAffinityAssignmentCache {
         List<ClusterNode> sorted;
 
         if (!locCache) {
-            sorted = new ArrayList<>(discoCache.cacheAffinityNodes(cacheId()));
+            sorted = new ArrayList<>(discoCache.cacheGroupAffinityNodes(groupId()));
 
             Collections.sort(sorted, GridNodeOrderComparator.INSTANCE);
         }
@@ -432,7 +434,7 @@ public class GridAffinityAssignmentCache {
      */
     public void dumpDebugInfo() {
         if (!readyFuts.isEmpty()) {
-            U.warn(log, "Pending affinity ready futures [cache=" + cacheName + ", lastVer=" + lastVersion() + "]:");
+            U.warn(log, "Pending affinity ready futures [grp=" + cacheOrGrpName + ", lastVer=" + lastVersion() + "]:");
 
             for (AffinityReadyFuture fut : readyFuts.values())
                 U.warn(log, ">>> " + fut);
@@ -461,7 +463,7 @@ public class GridAffinityAssignmentCache {
             if (cache == null) {
                 throw new IllegalStateException("Getting affinity for topology version earlier than affinity is " +
                     "calculated [locNode=" + ctx.discovery().localNode() +
-                    ", cache=" + cacheName +
+                    ", grp=" + cacheOrGrpName +
                     ", topVer=" + topVer +
                     ", head=" + head.get().topologyVersion() +
                     ", history=" + affCache.keySet() +


[03/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
index 4bd2372..625fb7d 100755
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java
@@ -65,6 +65,8 @@ import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteKernal;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.affinity.GridAffinityFunctionContextImpl;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
+import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheExplicitLockSpan;
@@ -1731,4 +1733,66 @@ public abstract class GridCommonAbstractTest extends GridAbstractTest {
 
         return U.field(tm, "completedVersHashMap");
     }
+
+    /**
+     *
+     */
+    protected final void checkCacheDiscoveryDataConsistent() {
+        Map<Integer, CacheGroupDescriptor> cacheGrps = null;
+        Map<String, DynamicCacheDescriptor> caches = null;
+
+        for (Ignite node : G.allGrids()) {
+            Map<Integer, CacheGroupDescriptor> cacheGrps0 =
+                ((IgniteKernal)node).context().cache().cacheGroupDescriptors();
+            Map<String, DynamicCacheDescriptor> caches0 =
+                ((IgniteKernal)node).context().cache().cacheDescriptors();
+
+            assertNotNull(cacheGrps0);
+            assertNotNull(caches0);
+
+            if (cacheGrps == null) {
+                cacheGrps = cacheGrps0;
+                caches = caches0;
+            }
+            else {
+                assertEquals(cacheGrps.size(), cacheGrps0.size());
+
+                for (Map.Entry<Integer, CacheGroupDescriptor> e : cacheGrps.entrySet()) {
+                    CacheGroupDescriptor desc = e.getValue();
+                    CacheGroupDescriptor desc0 = cacheGrps0.get(e.getKey());
+
+                    assertNotNull(desc0);
+                    checkGroupDescriptorsData(desc, desc0);
+                }
+
+                for (Map.Entry<String, DynamicCacheDescriptor> e : caches.entrySet()) {
+                    DynamicCacheDescriptor desc = e.getValue();
+                    DynamicCacheDescriptor desc0 = caches.get(e.getKey());
+
+                    assertNotNull(desc0);
+                    assertEquals(desc.deploymentId(), desc0.deploymentId());
+                    assertEquals(desc.receivedFrom(), desc0.receivedFrom());
+                    assertEquals(desc.startTopologyVersion(), desc0.startTopologyVersion());
+                    assertEquals(desc.cacheConfiguration().getName(), desc0.cacheConfiguration().getName());
+                    assertEquals(desc.cacheConfiguration().getGroupName(), desc0.cacheConfiguration().getGroupName());
+                    checkGroupDescriptorsData(desc.groupDescriptor(), desc0.groupDescriptor());
+                }
+            }
+        }
+    }
+
+    /**
+     * @param desc First descriptor.
+     * @param desc0 Second descriptor.
+     */
+    private void checkGroupDescriptorsData(CacheGroupDescriptor desc, CacheGroupDescriptor desc0) {
+        assertEquals(desc.groupName(), desc0.groupName());
+        assertEquals(desc.sharedGroup(), desc0.sharedGroup());
+        assertEquals(desc.deploymentId(), desc0.deploymentId());
+        assertEquals(desc.receivedFrom(), desc0.receivedFrom());
+        assertEquals(desc.startTopologyVersion(), desc0.startTopologyVersion());
+        assertEquals(desc.config().getName(), desc0.config().getName());
+        assertEquals(desc.config().getGroupName(), desc0.config().getGroupName());
+        assertEquals(desc.caches(), desc0.caches());
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java
index 710b2a1..11a4a10 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFullApiSelfTestSuite.java
@@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePart
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledMultiNodeP2PDisabledFullApiSelfTest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledOnheapFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridCachePartitionedNearDisabledOnheapMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.CachePartitionedMultiNodeLongTxTimeoutFullApiTest;
@@ -38,7 +39,9 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAto
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicLateAffDisabledMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicMultiNodeP2PDisabledFullApiSelfTest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicMultiNodeWithGroupFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicNearEnabledMultiNodeFullApiSelfTest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicNearOnlyMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicNearOnlyMultiNodeP2PDisabledFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicOnheapFullApiSelfTest;
@@ -58,6 +61,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePar
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedMultiNodeCounterSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedMultiNodeP2PDisabledFullApiSelfTest;
+import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedMultiNodeWithGroupFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedNearOnlyNoPrimaryFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedOnheapFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCachePartitionedOnheapMultiNodeFullApiSelfTest;
@@ -71,8 +75,10 @@ import org.apache.ignite.internal.processors.cache.distributed.replicated.GridCa
 import org.apache.ignite.internal.processors.cache.distributed.replicated.GridCacheReplicatedMultiNodeP2PDisabledFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.replicated.GridCacheReplicatedNearOnlyMultiNodeFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.local.GridCacheLocalAtomicFullApiSelfTest;
+import org.apache.ignite.internal.processors.cache.local.GridCacheLocalAtomicWithGroupFullApiSelfTest;
 import org.apache.ignite.internal.processors.cache.local.GridCacheLocalFullApiMultithreadedSelfTest;
 import org.apache.ignite.internal.processors.cache.local.GridCacheLocalFullApiSelfTest;
+import org.apache.ignite.internal.processors.cache.local.GridCacheLocalWithGroupFullApiSelfTest;
 
 /**
  * Test suite for cache API.
@@ -161,6 +167,13 @@ public class IgniteCacheFullApiSelfTestSuite extends TestSuite {
         // Other.
         suite.addTestSuite(GridCacheClearSelfTest.class);
 
+        suite.addTestSuite(GridCacheLocalWithGroupFullApiSelfTest.class);
+        suite.addTestSuite(GridCacheLocalAtomicWithGroupFullApiSelfTest.class);
+        suite.addTestSuite(GridCacheAtomicMultiNodeWithGroupFullApiSelfTest.class);
+        suite.addTestSuite(GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest.class);
+        suite.addTestSuite(GridCachePartitionedMultiNodeWithGroupFullApiSelfTest.class);
+        suite.addTestSuite(GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest.class);
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMetricsSelfTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMetricsSelfTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMetricsSelfTestSuite.java
index ebcf1df..d3471ca 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMetricsSelfTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMetricsSelfTestSuite.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.testsuites;
 
 import junit.framework.TestSuite;
+import org.apache.ignite.internal.processors.cache.CacheGroupsMetricsRebalanceTest;
 import org.apache.ignite.internal.processors.cache.CacheMetricsForClusterGroupSelfTest;
 import org.apache.ignite.internal.processors.cache.OffheapCacheMetricsForClusterGroupSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridCacheAtomicPartitionedMetricsSelfTest;
@@ -57,6 +58,8 @@ public class IgniteCacheMetricsSelfTestSuite extends TestSuite {
         suite.addTestSuite(GridCacheAtomicPartitionedTckMetricsSelfTestImpl.class);
         suite.addTestSuite(GridCacheAtomicLocalTckMetricsSelfTestImpl.class);
 
+        suite.addTestSuite(CacheGroupsMetricsRebalanceTest.class);
+
         // Cluster wide metrics.
         suite.addTestSuite(CacheMetricsForClusterGroupSelfTest.class);
         suite.addTestSuite(OffheapCacheMetricsForClusterGroupSelfTest.class);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java
index 222ac30..feb2cdf 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite3.java
@@ -32,9 +32,11 @@ import org.apache.ignite.internal.processors.cache.GridCacheValueConsistencyTran
 import org.apache.ignite.internal.processors.cache.GridCacheValueConsistencyTransactionalSelfTest;
 import org.apache.ignite.internal.processors.cache.GridCacheVersionSelfTest;
 import org.apache.ignite.internal.processors.cache.GridCacheVersionTopologyChangeTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheGroupsTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheInterceptorSelfTestSuite;
 import org.apache.ignite.internal.processors.cache.IgniteCacheScanPredicateDeploymentSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.CacheAsyncOperationsTest;
+import org.apache.ignite.internal.processors.cache.distributed.CacheGroupsPreloadTest;
 import org.apache.ignite.internal.processors.cache.distributed.GridCacheMixedModeSelfTest;
 import org.apache.ignite.internal.processors.cache.distributed.IgniteTxGetAfterStopTest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridCacheDaemonNodePartitionedSelfTest;
@@ -195,6 +197,8 @@ public class IgniteCacheTestSuite3 extends TestSuite {
 
         suite.addTestSuite(CacheAsyncOperationsTest.class);
 
+        suite.addTestSuite(IgniteCacheGroupsTest.class);
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite4.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite4.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite4.java
index 23f8445..1b35acb 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite4.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite4.java
@@ -88,6 +88,7 @@ import org.apache.ignite.internal.processors.cache.distributed.CacheAffinityEarl
 import org.apache.ignite.internal.processors.cache.distributed.CacheAtomicPrimarySyncBackPressureTest;
 import org.apache.ignite.internal.processors.cache.distributed.CacheDiscoveryDataConcurrentJoinTest;
 import org.apache.ignite.internal.processors.cache.distributed.CacheGetFutureHangsSelfTest;
+import org.apache.ignite.internal.processors.cache.distributed.CacheGroupsPreloadTest;
 import org.apache.ignite.internal.processors.cache.distributed.CacheNoValueClassOnServerNodeTest;
 import org.apache.ignite.internal.processors.cache.distributed.CacheStartOnJoinTest;
 import org.apache.ignite.internal.processors.cache.distributed.IgniteCacheCreatePutMultiNodeSelfTest;
@@ -305,6 +306,7 @@ public class IgniteCacheTestSuite4 extends TestSuite {
         suite.addTestSuite(GridCacheDhtTxPreloadSelfTest.class);
         suite.addTestSuite(GridCacheNearTxPreloadSelfTest.class);
         suite.addTestSuite(GridReplicatedTxPreloadTest.class);
+        suite.addTestSuite(CacheGroupsPreloadTest.class);
 
         suite.addTestSuite(IgniteDynamicCacheFilterTest.class);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 63e56ca..41176e1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -1734,7 +1734,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         IgniteCacheOffheapManager offheapMgr = cctx.isNear() ? cctx.near().dht().context().offheap() : cctx.offheap();
 
         for (int p = 0; p < cctx.affinity().partitions(); p++) {
-            try (GridCloseableIterator<KeyCacheObject> keyIter = offheapMgr.keysIterator(p)) {
+            try (GridCloseableIterator<KeyCacheObject> keyIter = offheapMgr.cacheKeysIterator(cctx.cacheId(), p)) {
                 while (keyIter.hasNext()) {
                     cctx.shared().database().checkpointReadLock();
 
@@ -2283,5 +2283,4 @@ public class IgniteH2Indexing implements GridQueryIndexing {
     private interface ClIter<X> extends AutoCloseable, Iterator<X> {
         // No-op.
     }
-
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
index 7caf354..0440615 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2PkHashIndex.java
@@ -108,7 +108,7 @@ public class H2PkHashIndex extends GridH2IndexBase {
             List<GridCursor<? extends CacheDataRow>> cursors = new ArrayList<>();
 
             for (IgniteCacheOffheapManager.CacheDataStore store : cctx.offheap().cacheDataStores())
-                cursors.add(store.cursor(lowerObj, upperObj));
+                cursors.add(store.cursor(cctx.cacheId(), lowerObj, upperObj));
 
             return new H2Cursor(new CompositeGridCursor<>(cursors.iterator()), p);
         }
@@ -126,7 +126,7 @@ public class H2PkHashIndex extends GridH2IndexBase {
     @Override public GridH2Row findOne(GridH2Row row) {
         try {
             for (IgniteCacheOffheapManager.CacheDataStore store : cctx.offheap().cacheDataStores()) {
-                CacheDataRow found = store.find(row.key);
+                CacheDataRow found = store.find(cctx, row.key);
 
                 if (found != null)
                     tbl.rowDescriptor().createRow(row.key(), row.partition(), row.value(), row.version(), 0);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
index 2024c36..86b2749 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2RowFactory.java
@@ -59,7 +59,7 @@ public class H2RowFactory {
 
         final CacheDataRowAdapter rowBuilder = new CacheDataRowAdapter(link);
 
-        rowBuilder.initFromLink(cctx, CacheDataRowAdapter.RowData.FULL);
+        rowBuilder.initFromLink(cctx.group(), CacheDataRowAdapter.RowData.FULL);
 
         GridH2Row row;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
index f673717..f2b8bad 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2Tree.java
@@ -63,7 +63,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
     /**
      * @param name Tree name.
      * @param reuseList Reuse list.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param pageMem Page memory.
      * @param wal Write ahead log manager.
      * @param rowStore Row data store.
@@ -74,7 +74,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
     protected H2Tree(
         String name,
         ReuseList reuseList,
-        int cacheId,
+        int grpId,
         PageMemory pageMem,
         IgniteWriteAheadLogManager wal,
         AtomicLong globalRmvId,
@@ -85,7 +85,7 @@ public abstract class H2Tree extends BPlusTree<SearchRow, GridH2Row> {
         List<InlineIndexHelper> inlineIdxs,
         int inlineSize
     ) throws IgniteCheckedException {
-        super(name, cacheId, pageMem, wal, globalRmvId, metaPageId, reuseList);
+        super(name, grpId, pageMem, wal, globalRmvId, metaPageId, reuseList);
 
         if (!initNew) {
             // Page is ready - read inline size from it.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index c1c1d9c..b84daf1 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -24,7 +24,6 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.database.IgniteCacheDatabaseSharedManager;
 import org.apache.ignite.internal.processors.cache.database.RootPage;
 import org.apache.ignite.internal.processors.cache.database.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
@@ -100,8 +99,6 @@ public class H2TreeIndex extends GridH2IndexBase {
         name = BPlusTree.treeName(name, "H2Tree");
 
         if (cctx.affinityNode()) {
-            IgniteCacheDatabaseSharedManager dbMgr = cctx.shared().database();
-
             inlineIdxs = getAvailableInlineColumns(cols);
 
             segments = new H2Tree[segmentsCnt];
@@ -110,8 +107,9 @@ public class H2TreeIndex extends GridH2IndexBase {
                 RootPage page = getMetaPage(name, i);
 
                 segments[i] = new H2Tree(
-                    name,cctx.offheap().reuseListForIndex(name),
-                    cctx.cacheId(),
+                    name,
+                    cctx.offheap().reuseListForIndex(name),
+                    cctx.groupId(),
                     cctx.memoryPolicy().pageMemory(),
                     cctx.shared().wal(),
                     cctx.offheap().globalRemoveId(),
@@ -326,10 +324,12 @@ public class H2TreeIndex extends GridH2IndexBase {
     @Override public void destroy() {
         try {
             if (cctx.affinityNode()) {
-                for (H2Tree tree : segments) {
+                for (int i = 0; i < segments.length; i++) {
+                    H2Tree tree = segments[i];
+
                     tree.destroy();
 
-                    cctx.offheap().dropRootPageForIndex(tree.getName());
+                    dropMetaPage(tree.getName(), i);
                 }
             }
         }
@@ -413,10 +413,20 @@ public class H2TreeIndex extends GridH2IndexBase {
 
     /**
      * @param name Name.
+     * @param segIdx Segment index.
      * @return RootPage for meta page.
      * @throws IgniteCheckedException If failed.
      */
     private RootPage getMetaPage(String name, int segIdx) throws IgniteCheckedException {
-        return cctx.offheap().rootPageForIndex(name + "%" + segIdx);
+        return cctx.offheap().rootPageForIndex(cctx.cacheId(), name + "%" + segIdx);
+    }
+
+    /**
+     * @param name Name.
+     * @param segIdx Segment index.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void dropMetaPage(String name, int segIdx) throws IgniteCheckedException {
+        cctx.offheap().dropRootPageForIndex(cctx.cacheId(), name + "%" + segIdx);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
index 2d12635..c1cbada 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java
@@ -1026,12 +1026,12 @@ public class GridReduceQueryExecutor {
     }
 
     /**
-     * @param cacheName Cache name.
+     * @param grpId Cache group ID.
      * @param topVer Topology version.
      * @return Collection of data nodes.
      */
-    private Collection<ClusterNode> dataNodes(String cacheName, AffinityTopologyVersion topVer) {
-        Collection<ClusterNode> res = ctx.discovery().cacheAffinityNodes(cacheName, topVer);
+    private Collection<ClusterNode> dataNodes(int grpId, AffinityTopologyVersion topVer) {
+        Collection<ClusterNode> res = ctx.discovery().cacheGroupAffinityNodes(grpId, topVer);
 
         return res != null ? res : Collections.<ClusterNode>emptySet();
     }
@@ -1047,7 +1047,7 @@ public class GridReduceQueryExecutor {
 
         String cacheName = cctx.name();
 
-        Set<ClusterNode> dataNodes = new HashSet<>(dataNodes(cacheName, NONE));
+        Set<ClusterNode> dataNodes = new HashSet<>(dataNodes(cctx.groupId(), NONE));
 
         if (dataNodes.isEmpty())
             throw new CacheException("Failed to find data nodes for cache: " + cacheName);
@@ -1110,7 +1110,7 @@ public class GridReduceQueryExecutor {
 
                     continue;
                 }
-                else if (!F.isEmpty(dataNodes(cctx.name(), NONE)))
+                else if (!F.isEmpty(dataNodes(cctx.groupId(), NONE)))
                     return null; // Retry.
 
                 throw new CacheException("Failed to find data nodes [cache=" + cctx.name() + ", part=" + p + "]");
@@ -1139,7 +1139,7 @@ public class GridReduceQueryExecutor {
                         continue; // Skip unmapped partitions.
 
                     if (F.isEmpty(owners)) {
-                        if (!F.isEmpty(dataNodes(extraCctx.name(), NONE)))
+                        if (!F.isEmpty(dataNodes(extraCctx.groupId(), NONE)))
                             return null; // Retry.
 
                         throw new CacheException("Failed to find data nodes [cache=" + extraCctx.name() +

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsSqlTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsSqlTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsSqlTest.java
new file mode 100644
index 0000000..b538449
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheGroupsSqlTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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.List;
+import java.util.concurrent.Callable;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryIndex;
+import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
+import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.binary.AffinityKey;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.transactions.Transaction;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheMode.REPLICATED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgniteCacheGroupsSqlTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String GROUP1 = "grp1";
+
+    /** */
+    private static final String GROUP2 = "grp2";
+
+    /** */
+    private boolean client;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
+
+        cfg.setClientMode(client);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        startGridsMultiThreaded(3);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        super.afterTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testSqlQuery() throws Exception {
+        Ignite node = ignite(0);
+
+        IgniteCache c1 = node.createCache(personCacheConfiguration(GROUP1, "c1"));
+        IgniteCache c2 = node.createCache(personCacheConfiguration(GROUP1, "c2"));
+
+        SqlFieldsQuery qry = new SqlFieldsQuery("select name from Person where name=?");
+        qry.setArgs("p1");
+
+        assertEquals(0, c1.query(qry).getAll().size());
+        assertEquals(0, c2.query(qry).getAll().size());
+
+        c1.put(1, new Person("p1"));
+
+        assertEquals(1, c1.query(qry).getAll().size());
+        assertEquals(0, c2.query(qry).getAll().size());
+
+        c2.put(2, new Person("p1"));
+
+        assertEquals(1, c1.query(qry).getAll().size());
+        assertEquals(1, c2.query(qry).getAll().size());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery1() throws Exception {
+        joinQuery(GROUP1, GROUP2, REPLICATED, PARTITIONED, TRANSACTIONAL, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery2() throws Exception {
+        GridTestUtils.assertThrows(log, new Callable<Void>() {
+            @Override public Void call() throws Exception {
+                joinQuery(GROUP1, GROUP1, REPLICATED, PARTITIONED, TRANSACTIONAL, TRANSACTIONAL);
+                return null;
+            }
+        }, IgniteCheckedException.class, "Cache mode mismatch for caches related to the same group");
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery3() throws Exception {
+        joinQuery(GROUP1, GROUP1, PARTITIONED, PARTITIONED, TRANSACTIONAL, ATOMIC);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery4() throws Exception {
+        joinQuery(GROUP1, GROUP1, REPLICATED, REPLICATED, ATOMIC, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery5() throws Exception {
+        joinQuery(GROUP1, null, REPLICATED, PARTITIONED, TRANSACTIONAL, TRANSACTIONAL);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testJoinQuery6() throws Exception {
+        joinQuery(GROUP1, null, PARTITIONED, PARTITIONED, TRANSACTIONAL, ATOMIC);
+    }
+
+    /**
+     * @param grp1 First cache group.
+     * @param grp2 Second cache group.
+     * @param cm1 First cache mode.
+     * @param cm2 Second cache mode.
+     * @param cam1 First cache atomicity mode.
+     * @param cam2 Second cache atomicity mode.
+     * @throws Exception If failed.
+     */
+    private void joinQuery(String grp1, String grp2, CacheMode cm1,
+        CacheMode cm2, CacheAtomicityMode cam1, CacheAtomicityMode cam2) throws Exception {
+        int keys = 1000;
+        int accsPerPerson = 4;
+
+        Ignite srv0 = ignite(0);
+
+        IgniteCache pers = srv0.createCache(personCacheConfiguration(grp1, "pers")
+            .setAffinity(new RendezvousAffinityFunction().setPartitions(10))
+            .setCacheMode(cm1)
+            .setAtomicityMode(cam1));
+
+        IgniteCache acc = srv0.createCache(accountCacheConfiguration(grp2, "acc")
+            .setAffinity(new RendezvousAffinityFunction().setPartitions(10))
+            .setCacheMode(cm2)
+            .setAtomicityMode(cam2));
+
+        try(Transaction tx = cam1 == TRANSACTIONAL || cam2 == TRANSACTIONAL ? srv0.transactions().txStart() : null) {
+            for (int i = 0; i < keys; i++) {
+
+                int pKey = i - (i % accsPerPerson);
+
+                if (i % accsPerPerson == 0)
+                    pers.put(pKey, new Person("pers-" + pKey));
+
+
+                acc.put(new AffinityKey(i, pKey), new Account(pKey, "acc-" + i));
+            }
+
+            if (tx != null)
+                tx.commit();
+        }
+
+        Ignite node = ignite(2);
+
+        SqlFieldsQuery qry = new SqlFieldsQuery(
+            "select p._key as p_key, p.name, a._key as a_key, a.personId, a.attr \n" +
+            "from \"pers\".Person p inner join \"acc\".Account a \n" +
+            "on (p._key = a.personId)");
+
+        IgniteCache<Object, Object> cache = node.cache("acc");
+
+        List<List<?>> res = cache.query(qry).getAll();
+
+        assertEquals(keys, res.size());
+
+        for (List<?> row : res)
+            assertEquals(row.get(0), row.get(3));
+    }
+
+    /**
+     * @param grpName Group name.
+     * @param cacheName Cache name.
+     * @return Person cache configuration.
+     */
+    private CacheConfiguration personCacheConfiguration(String grpName, String cacheName) {
+        QueryEntity entity = new QueryEntity();
+
+        entity.setKeyType(Integer.class.getName());
+        entity.setValueType(Person.class.getName());
+        entity.addQueryField("name", String.class.getName(), null);
+
+        return cacheConfiguration(grpName, cacheName, entity);
+    }
+
+    /**
+     * @param grpName Group name.
+     * @param cacheName Cache name.
+     * @return Account cache configuration.
+     */
+    private CacheConfiguration accountCacheConfiguration(String grpName, String cacheName) {
+        QueryEntity entity = new QueryEntity();
+
+        entity.setKeyType(AffinityKey.class.getName());
+        entity.setValueType(Account.class.getName());
+        entity.addQueryField("personId", Integer.class.getName(), null);
+        entity.addQueryField("attr", String.class.getName(), null);
+        entity.setIndexes(F.asList(new QueryIndex("personId")));
+
+        return cacheConfiguration(grpName, cacheName, entity);
+    }
+
+    /**
+     * @param grpName Group name.
+     * @param cacheName Cache name.
+     * @param queryEntity Query entity.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration(String grpName, String cacheName, QueryEntity queryEntity) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+        ccfg.setGroupName(grpName);
+        ccfg.setName(cacheName);
+
+        ccfg.setQueryEntities(F.asList(queryEntity));
+
+        return ccfg;
+    }
+
+    /**
+     *
+     */
+    private static class Person implements Serializable {
+        /** */
+        String name;
+
+        /**
+         * @param name Name.
+         */
+        public Person(String name) {
+            this.name = name;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+    }
+
+    /**
+     *
+     */
+    private static class Account implements Serializable {
+        /** */
+        Integer personId;
+
+        /** */
+        String attr;
+
+        /**
+         * @param personId Person ID.
+         * @param attr Attribute (some data).
+         */
+        public Account(Integer personId, String attr) {
+            this.personId = personId;
+            this.attr = attr;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Account.class, this);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheNoClassQuerySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheNoClassQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheNoClassQuerySelfTest.java
index e0148b3..770770e 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheNoClassQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheNoClassQuerySelfTest.java
@@ -20,13 +20,10 @@ package org.apache.ignite.internal.processors.cache;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashMap;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.QueryEntity;
 import org.apache.ignite.cache.QueryIndex;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.configuration.NearCacheConfiguration;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
@@ -44,23 +41,6 @@ public class IgniteCacheNoClassQuerySelfTest extends GridCommonAbstractTest {
     /** */
     private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
 
-    /** */
-    protected Ignite ignite;
-
-    /**
-     * @return Atomicity mode.
-     */
-    protected CacheAtomicityMode atomicityMode() {
-        return TRANSACTIONAL;
-    }
-
-    /**
-     * @return Distribution.
-     */
-    protected NearCacheConfiguration nearCacheConfiguration() {
-        return new NearCacheConfiguration();
-    }
-
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
@@ -118,5 +98,8 @@ public class IgniteCacheNoClassQuerySelfTest extends GridCommonAbstractTest {
         catch (Exception e) {
             assertTrue(e.getMessage().contains("default marshaller"));
         }
+        finally {
+            stopAllGrids();
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
index 0f4a418..794ec4d 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheWithIndexingTestSuite.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheOffheapIndexEntryEvi
 import org.apache.ignite.internal.processors.cache.GridCacheOffheapIndexGetSelfTest;
 import org.apache.ignite.internal.processors.cache.GridIndexingWithNoopSwapSelfTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheConfigurationPrimitiveTypesSelfTest;
+import org.apache.ignite.internal.processors.cache.IgniteCacheGroupsSqlTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheStarvationOnRebalanceTest;
 import org.apache.ignite.internal.processors.cache.IgniteClientReconnectQueriesTest;
 import org.apache.ignite.internal.processors.cache.ttl.CacheTtlAtomicLocalSelfTest;
@@ -72,6 +73,8 @@ public class IgniteCacheWithIndexingTestSuite extends TestSuite {
 
         suite.addTestSuite(ClientReconnectAfterClusterRestartTest.class);
 
+        suite.addTestSuite(IgniteCacheGroupsSqlTest.class);
+
         return suite;
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
index 0f75b0d..1b36451 100755
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
@@ -94,6 +94,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.PageDeltaRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionDestroyRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionMetaStateRecord;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.ClusterState;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
@@ -214,7 +215,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     private volatile GridFutureAdapter<Void> enableChangeApplied;
 
     /** */
-    public ReentrantReadWriteLock checkpointLock = new ReentrantReadWriteLock();
+    private ReentrantReadWriteLock checkpointLock = new ReentrantReadWriteLock();
 
     /** */
     private long checkpointFreq;
@@ -409,25 +410,25 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         if (!reconnect && !cctx.kernalContext().clientNode() && cctx.kernalContext().state().active()) {
             Collection<String> cacheNames = new HashSet<>();
 
-            for (CacheConfiguration ccfg : cctx.kernalContext().config().getCacheConfiguration())
+            // TODO IGNITE-5075 group descriptors.
+            for (CacheConfiguration ccfg : cctx.kernalContext().config().getCacheConfiguration()) {
                 if (CU.isSystemCache(ccfg.getName())) {
-                    storeMgr.initializeForCache(ccfg);
+                    storeMgr.initializeForCache(cctx.cache().cacheDescriptors().get(ccfg.getName()).groupDescriptor(), ccfg);
 
                     cacheNames.add(ccfg.getName());
                 }
+            }
 
             for (CacheConfiguration ccfg : cctx.kernalContext().config().getCacheConfiguration())
                 if (!CU.isSystemCache(ccfg.getName())) {
-                    storeMgr.initializeForCache(ccfg);
+                    storeMgr.initializeForCache(cctx.cache().cacheDescriptors().get(ccfg.getName()).groupDescriptor(), ccfg);
 
                     cacheNames.add(ccfg.getName());
                 }
 
-            for (String name : cctx.pageStore().savedCacheNames()) {
-                CacheConfiguration ccfg = cctx.pageStore().readConfiguration(name);
-
-                if (ccfg != null && !cacheNames.contains(name))
-                    storeMgr.initializeForCache(ccfg);
+            for (CacheConfiguration ccfg : cctx.pageStore().readCacheConfigurations().values()) {
+                if (!cacheNames.contains(ccfg.getName()))
+                    storeMgr.initializeForCache(cctx.cache().cacheDescriptors().get(ccfg.getName()).groupDescriptor(), ccfg);
             }
 
             readCheckpointAndRestoreMemory();
@@ -548,7 +549,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /** {@inheritDoc} */
-    protected long[] calculateFragmentSizes(int concLvl, long cacheSize) {
+    private long[] calculateFragmentSizes(int concLvl, long cacheSize) {
         if (concLvl < 2)
             concLvl = Runtime.getRuntime().availableProcessors();
 
@@ -599,7 +600,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     FullPageId fullId,
                     PageMemoryEx pageMem
                 ) throws IgniteCheckedException {
-                  snapshotMgr.onChangeTrackerPage(page,fullId,pageMem);
+                  snapshotMgr.onChangeTrackerPage(page, fullId, pageMem);
                 }
             },
             this
@@ -688,7 +689,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         if (cctx.kernalContext().query().moduleEnabled()) {
             for (GridCacheContext cacheCtx : (Collection<GridCacheContext>)cctx.cacheContexts()) {
                 if (cacheCtx.startTopologyVersion().equals(fut.topologyVersion()) &&
-                    !cctx.pageStore().hasIndexStore(cacheCtx.cacheId()) && cacheCtx.affinityNode()) {
+                    !cctx.pageStore().hasIndexStore(cacheCtx.groupId()) && cacheCtx.affinityNode()) {
                     final int cacheId = cacheCtx.cacheId();
 
                     final IgniteInternalFuture<?> rebuildFut = cctx.kernalContext().query()
@@ -717,7 +718,8 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /** {@inheritDoc} */
-    @Override public void onCachesStopped(Collection<IgniteBiTuple<GridCacheContext, Boolean>> stoppedCtxs) {
+    @Override public void onCacheGroupsStopped(
+        Collection<IgniteBiTuple<CacheGroupContext, Boolean>> stoppedGrps) {
         try {
             waitForCheckpoint("caches stop");
         }
@@ -727,30 +729,30 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
         Map<PageMemoryEx, Collection<Integer>> destroyed = new HashMap<>();
 
-        for (IgniteBiTuple<GridCacheContext, Boolean> tup : stoppedCtxs) {
+        for (IgniteBiTuple<CacheGroupContext, Boolean> tup : stoppedGrps) {
             PageMemoryEx pageMem = (PageMemoryEx)tup.get1().memoryPolicy().pageMemory();
 
-            Collection<Integer> cacheIds = destroyed.get(pageMem);
+            Collection<Integer> grpIds = destroyed.get(pageMem);
 
-            if (cacheIds == null) {
-                cacheIds = new HashSet<>();
+            if (grpIds == null) {
+                grpIds = new HashSet<>();
 
-                destroyed.put(pageMem, cacheIds);
+                destroyed.put(pageMem, grpIds);
             }
 
-            cacheIds.add(tup.get1().cacheId());
+            grpIds.add(tup.get1().groupId());
 
-            pageMem.onCacheDestroyed(tup.get1().cacheId());
+            pageMem.onCacheGroupDestroyed(tup.get1().groupId());
         }
 
         Collection<IgniteInternalFuture<Void>> clearFuts = new ArrayList<>(destroyed.size());
 
         for (Map.Entry<PageMemoryEx, Collection<Integer>> entry : destroyed.entrySet()) {
-            final Collection<Integer> cacheIds = entry.getValue();
+            final Collection<Integer> grpIds = entry.getValue();
 
             clearFuts.add(entry.getKey().clearAsync(new P3<Integer, Long, Integer>() {
-                @Override public boolean apply(Integer cacheId, Long pageId, Integer tag) {
-                    return cacheIds.contains(cacheId);
+                @Override public boolean apply(Integer grpId, Long pageId, Integer tag) {
+                    return grpIds.contains(grpId);
                 }
             }, false));
         }
@@ -764,15 +766,17 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         }
 
         if (cctx.pageStore() != null) {
-            for (IgniteBiTuple<GridCacheContext, Boolean> tup : stoppedCtxs) {
-                GridCacheContext cacheCtx = tup.get1();
+            for (IgniteBiTuple<CacheGroupContext, Boolean> tup : stoppedGrps) {
+                CacheGroupContext grp = tup.get1();
 
-                try {
-                    cctx.pageStore().shutdownForCache(cacheCtx, tup.get2());
-                }
-                catch (IgniteCheckedException e) {
-                    U.error(log, "Failed to gracefully clean page store resources for destroyed cache " +
-                        "[cache=" + cacheCtx.name() + "]", e);
+                if (grp.affinityNode()) {
+                    try {
+                        cctx.pageStore().shutdownForCacheGroup(grp, tup.get2());
+                    }
+                    catch (IgniteCheckedException e) {
+                        U.error(log, "Failed to gracefully clean page store resources for destroyed cache " +
+                            "[cache=" + grp.cacheOrGroupName() + "]", e);
+                    }
                 }
             }
         }
@@ -849,15 +853,17 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         checkpointLock.readLock().unlock();
 
         if (checkpointer != null) {
-            Collection<GridCacheContext> cacheCtxs = context().cacheContexts();
+            Collection<MemoryPolicy> memPlcs = context().database().memoryPolicies();
 
-            for (GridCacheContext cacheCtx : cacheCtxs) {
-                PageMemoryEx mem = (PageMemoryEx) cacheCtx.memoryPolicy().pageMemory();
+            if (memPlcs != null) {
+                for (MemoryPolicy memPlc : memPlcs) {
+                    PageMemoryEx mem = (PageMemoryEx)memPlc.pageMemory();
 
-                if (mem != null && !mem.safeToUpdate()) {
-                    checkpointer.wakeupForCheckpoint(0, "too many dirty pages");
+                    if (mem != null && !mem.safeToUpdate()) {
+                        checkpointer.wakeupForCheckpoint(0, "too many dirty pages");
 
-                    break;
+                        break;
+                    }
                 }
             }
         }
@@ -869,7 +875,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /**
      * @throws IgniteCheckedException If failed to restore database status from WAL.
      */
-    public void restoreState() throws IgniteCheckedException {
+    private void restoreState() throws IgniteCheckedException {
         try {
             CheckpointStatus status = readCheckpointStatus();
 
@@ -899,27 +905,27 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
         reservedForExchange = new HashMap<>();
 
-        for (GridCacheContext cacheCtx : (Collection<GridCacheContext>)cctx.cacheContexts()) {
-            if (cacheCtx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            for (GridDhtLocalPartition part : cacheCtx.topology().currentLocalPartitions()) {
-                if (part.state() != GridDhtPartitionState.OWNING || part.dataStore().size() <= walRebalanceThreshold)
+            for (GridDhtLocalPartition part : grp.topology().currentLocalPartitions()) {
+                if (part.state() != GridDhtPartitionState.OWNING || part.dataStore().fullSize() <= walRebalanceThreshold)
                     continue;
 
-                CheckpointEntry cpEntry = searchCheckpointEntry(cacheCtx, part.id(), null);
+                CheckpointEntry cpEntry = searchCheckpointEntry(grp.groupId(), part.id(), null);
 
                 try {
                     if (cpEntry != null && cctx.wal().reserve(cpEntry.cpMark)) {
-                        Map<Integer, T2<Long, WALPointer>> cacheMap = reservedForExchange.get(cacheCtx.cacheId());
+                        Map<Integer, T2<Long, WALPointer>> cacheMap = reservedForExchange.get(grp.groupId());
 
                         if (cacheMap == null) {
                             cacheMap = new HashMap<>();
 
-                            reservedForExchange.put(cacheCtx.cacheId(), cacheMap);
+                            reservedForExchange.put(grp.groupId(), cacheMap);
                         }
 
-                        cacheMap.put(part.id(), new T2<>(cpEntry.partitionCounter(cacheCtx.cacheId(), part.id()), cpEntry.cpMark));
+                        cacheMap.put(part.id(), new T2<>(cpEntry.partitionCounter(grp.groupId(), part.id()), cpEntry.cpMark));
                     }
                 }
                 catch (IgniteCheckedException ex) {
@@ -962,8 +968,8 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /** {@inheritDoc} */
-    @Override public boolean reserveHistoryForPreloading(int cacheId, int partId, long cntr) {
-        CheckpointEntry cpEntry = searchCheckpointEntry(cctx.cacheContext(cacheId), partId, cntr);
+    @Override public boolean reserveHistoryForPreloading(int grpId, int partId, long cntr) {
+        CheckpointEntry cpEntry = searchCheckpointEntry(grpId, partId, cntr);
 
         if (cpEntry == null)
             return false;
@@ -985,7 +991,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         }
 
         if (reserved)
-            reservedForPreloading.put(new T2<>(cacheId, partId), new T2<>(cntr, ptr));
+            reservedForPreloading.put(new T2<>(grpId, partId), new T2<>(cntr, ptr));
 
         return reserved;
     }
@@ -1050,30 +1056,17 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /**
-     * Schedules partition destroy during next checkpoint. This method must be called inside checkpoint read lock.
-     *
-     * @param cacheCtx Cache context.
-     * @param partId Partition ID.
-     */
-    public void schedulePartitionDestroy(GridCacheContext<?, ?> cacheCtx, int partId) {
-        Checkpointer cp = checkpointer;
-
-        if (cp != null)
-            cp.schedulePartitionDestroy(cacheCtx, partId);
-    }
-
-    /**
      * Cancels partition destroy if it has not begun yet. Otherwise, will wait for cleanup to finish.
      *
-     * @param cacheCtx Cache context.
+     * @param grpId Cache group ID.
      * @param partId Partition ID.
      */
-    public void cancelOrWaitPartitionDestroy(GridCacheContext<?, ?> cacheCtx, int partId)
+    void cancelOrWaitPartitionDestroy(int grpId, int partId)
         throws IgniteCheckedException {
         Checkpointer cp = checkpointer;
 
         if (cp != null)
-            cp.cancelOrWaitPartitionDestroy(cacheCtx, partId);
+            cp.cancelOrWaitPartitionDestroy(grpId, partId);
     }
 
 
@@ -1081,13 +1074,13 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /**
      * Tries to search for a WAL pointer for the given partition counter start.
      *
-     * @param cacheCtx Cache context.
+     * @param grpId Cache group ID.
      * @param part Partition ID.
      * @param partCntrSince Partition counter or {@code null} to search for minimal counter.
      * @return Checkpoint entry or {@code null} if failed to search.
      */
-    @Nullable public WALPointer searchPartitionCounter(GridCacheContext cacheCtx, int part, @Nullable Long partCntrSince) {
-        CheckpointEntry entry = searchCheckpointEntry(cacheCtx, part, partCntrSince);
+    @Nullable public WALPointer searchPartitionCounter(int grpId, int part, @Nullable Long partCntrSince) {
+        CheckpointEntry entry = searchCheckpointEntry(grpId, part, partCntrSince);
 
         if (entry == null)
             return null;
@@ -1098,12 +1091,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /**
      * Tries to search for a WAL pointer for the given partition counter start.
      *
-     * @param cacheCtx Cache context.
+     * @param grpId Cache group ID.
      * @param part Partition ID.
      * @param partCntrSince Partition counter or {@code null} to search for minimal counter.
      * @return Checkpoint entry or {@code null} if failed to search.
      */
-    @Nullable private CheckpointEntry searchCheckpointEntry(GridCacheContext cacheCtx, int part, @Nullable Long partCntrSince) {
+    @Nullable private CheckpointEntry searchCheckpointEntry(int grpId, int part, @Nullable Long partCntrSince) {
         boolean hasGap = false;
         CheckpointEntry first = null;
 
@@ -1111,7 +1104,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             try {
                 CheckpointEntry entry = checkpointHist.entry(cpTs);
 
-                Long foundCntr = entry.partitionCounter(cacheCtx.cacheId(), part);
+                Long foundCntr = entry.partitionCounter(grpId, part);
 
                 if (foundCntr != null) {
                     if (partCntrSince == null) {
@@ -1323,7 +1316,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                             int cacheId = pageRec.fullPageId().cacheId();
                             long pageId = pageRec.fullPageId().pageId();
 
-                            PageMemoryEx pageMem = getPageMemoryForCacheId(cacheId);
+                            PageMemoryEx pageMem = getPageMemoryForCacheGroup(cacheId);
 
                             long page = pageMem.acquirePage(cacheId, pageId, true);
 
@@ -1353,7 +1346,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                             final int cId = destroyRec.cacheId();
                             final int pId = destroyRec.partitionId();
 
-                            PageMemoryEx pageMem = getPageMemoryForCacheId(cId);
+                            PageMemoryEx pageMem = getPageMemoryForCacheGroup(cId);
 
                             pageMem.clearAsync(new P3<Integer, Long, Integer>() {
                                 @Override public boolean apply(Integer cacheId, Long pageId, Integer tag) {
@@ -1371,7 +1364,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                             int cacheId = r.cacheId();
                             long pageId = r.pageId();
 
-                            PageMemoryEx pageMem = getPageMemoryForCacheId(cacheId);
+                            PageMemoryEx pageMem = getPageMemoryForCacheGroup(cacheId);
 
                             // Here we do not require tag check because we may be applying memory changes after
                             // several repetitive restarts and the same pages may have changed several times.
@@ -1416,20 +1409,21 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /**
      * Obtains PageMemory reference from cache descriptor instead of cache context.
      *
-     * @param cacheId Cache id.
+     * @param grpId Cache group id.
      * @return PageMemoryEx instance.
      * @throws IgniteCheckedException if no MemoryPolicy is configured for a name obtained from cache descriptor.
      */
-    private PageMemoryEx getPageMemoryForCacheId(int cacheId) throws IgniteCheckedException {
+    private PageMemoryEx getPageMemoryForCacheGroup(int grpId) throws IgniteCheckedException {
+        // TODO IGNITE-5075: cache descriptor can be removed.
         GridCacheSharedContext sharedCtx = context();
 
         String memPlcName = sharedCtx
             .cache()
-            .cacheDescriptor(cacheId)
-            .cacheConfiguration()
+            .cacheGroupDescriptors().get(grpId)
+            .config()
             .getMemoryPolicyName();
 
-        return (PageMemoryEx) sharedCtx.database().memoryPolicy(memPlcName).pageMemory();
+        return (PageMemoryEx)sharedCtx.database().memoryPolicy(memPlcName).pageMemory();
     }
 
     /**
@@ -1502,35 +1496,31 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     private void restorePartitionState(
         Map<T2<Integer, Integer>, T2<Integer, Long>> partStates
     ) throws IgniteCheckedException {
-        Collection<GridCacheContext> cacheContexts = cctx.cacheContexts();
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            int grpId = grp.groupId();
 
-        for (GridCacheContext context : cacheContexts) {
-            int cacheId = context.cacheId();
+            PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
 
-            GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+            for (int i = 0; i < grp.affinity().partitions(); i++) {
+                if (storeMgr.exists(grpId, i)) {
+                    storeMgr.ensure(grpId, i);
 
-            PageMemoryEx pageMem = (PageMemoryEx)cacheCtx.memoryPolicy().pageMemory();
-
-            for (int i = 0; i < context.affinity().partitions(); i++) {
-                if (storeMgr.exists(cacheId, i)) {
-                    storeMgr.ensure(cacheId, i);
-
-                    if (storeMgr.pages(cacheId, i) <= 1)
+                    if (storeMgr.pages(grpId, i) <= 1)
                         continue;
 
-                    long partMetaId = pageMem.partitionMetaPageId(cacheId, i);
-                    long partMetaPage = pageMem.acquirePage(cacheId, partMetaId);
+                    long partMetaId = pageMem.partitionMetaPageId(grpId, i);
+                    long partMetaPage = pageMem.acquirePage(grpId, partMetaId);
                     try {
-                        long pageAddr = pageMem.writeLock(cacheId, partMetaId, partMetaPage);
+                        long pageAddr = pageMem.writeLock(grpId, partMetaId, partMetaPage);
 
                         boolean changed = false;
 
                         try {
                             PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.forPage(pageAddr);
 
-                            T2<Integer, Long> fromWal = partStates.get(new T2<>(cacheId, i));
+                            T2<Integer, Long> fromWal = partStates.get(new T2<>(grpId, i));
 
-                            GridDhtLocalPartition part = context.topology()
+                            GridDhtLocalPartition part = grp.topology()
                                 .localPartition(i, AffinityTopologyVersion.NONE, true);
 
                             assert part != null;
@@ -1543,7 +1533,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                                 changed = updateState(part, stateId);
 
                                 if (stateId == GridDhtPartitionState.OWNING.ordinal()) {
-                                    cacheCtx.offheap().onPartitionInitialCounterUpdated(i, fromWal.get2());
+                                    grp.offheap().onPartitionInitialCounterUpdated(i, fromWal.get2());
 
                                     if (part.initialUpdateCounter() < fromWal.get2()) {
                                         part.initialUpdateCounter(fromWal.get2());
@@ -1556,11 +1546,11 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                                 changed = updateState(part, (int)io.getPartitionState(pageAddr));
                         }
                         finally {
-                            pageMem.writeUnlock(cacheId, partMetaId, partMetaPage, null, changed);
+                            pageMem.writeUnlock(grpId, partMetaId, partMetaPage, null, changed);
                         }
                     }
                     finally {
-                        pageMem.releasePage(cacheId, partMetaId, partMetaPage);
+                        pageMem.releasePage(grpId, partMetaId, partMetaPage);
                     }
                 }
             }
@@ -1598,6 +1588,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             case CREATE:
             case UPDATE:
                 cacheCtx.offheap().update(
+                    cacheCtx,
                     dataEntry.key(),
                     dataEntry.value(),
                     dataEntry.writeVersion(),
@@ -1611,7 +1602,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 break;
 
             case DELETE:
-                cacheCtx.offheap().remove(dataEntry.key(), dataEntry.partitionId(), locPart);
+                cacheCtx.offheap().remove(cacheCtx, dataEntry.key(), dataEntry.partitionId(), locPart);
 
                 if (dataEntry.partitionCounter() != 0)
                     cacheCtx.offheap().onPartitionInitialCounterUpdated(dataEntry.partitionId(), dataEntry.partitionCounter());
@@ -1744,7 +1735,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 ch.force(true);
 
             return type == CheckpointEntryType.START ?
-                new CheckpointEntry(cpTs, ptr, cpId, rec.cacheStates()) : null;
+                new CheckpointEntry(cpTs, ptr, cpId, rec.cacheGroupStates()) : null;
         }
         catch (IOException e) {
             throw new IgniteCheckedException(e);
@@ -1771,28 +1762,29 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         final Set<PageMemoryEx> pageMemSet = new HashSet<>();
 
         for (PartitionDestroyRequest req : reqs) {
-            Collection<Integer> partIds = filterMap.get(req.cacheId);
+            Collection<Integer> partIds = filterMap.get(req.grpId);
 
             if (partIds == null) {
                 partIds = new HashSet<>();
 
-                filterMap.put(req.cacheId, partIds);
+                filterMap.put(req.grpId, partIds);
             }
 
             partIds.add(req.partId);
 
-            pageMemSet.add((PageMemoryEx)cctx.cacheContext(req.cacheId).memoryPolicy().pageMemory());
+            // TODO IGNITE-5075.
+            pageMemSet.add((PageMemoryEx)cctx.cache().cacheGroup(req.grpId).memoryPolicy().pageMemory());
         }
 
         for (PageMemoryEx pageMem : pageMemSet) {
             IgniteInternalFuture<Void> clearFut = pageMem.clearAsync(new P3<Integer, Long, Integer>() {
-                @Override public boolean apply(Integer cacheId, Long pageId, Integer tag) {
-                    assert cacheId != null;
+                @Override public boolean apply(Integer grpId, Long pageId, Integer tag) {
+                    assert grpId != null;
                     assert pageId != null;
 
                     int partId = PageIdUtils.partId(pageId);
 
-                    Collection<Integer> parts = filterMap.get(cacheId);
+                    Collection<Integer> parts = filterMap.get(grpId);
 
                     return parts != null && parts.contains(partId);
                 }
@@ -1805,7 +1797,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                             assert !req.allowFastEviction;
 
                             // Tag should never grow in this case.
-                            cctx.pageStore().onPartitionDestroyed(req.cacheId, req.partId, 1);
+                            cctx.pageStore().onPartitionDestroyed(req.grpId, req.partId, 1);
                         }
                         catch (IgniteCheckedException e) {
                             req.onDone(e);
@@ -1932,36 +1924,36 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         }
 
         /**
-         * @param cacheCtx Cache context.
+         * @param grp Cache group.
          * @param partId Partition ID.
          */
-        private void schedulePartitionDestroy(GridCacheContext<?, ?> cacheCtx, int partId) {
+        private void schedulePartitionDestroy(CacheGroupContext grp, int partId) {
             synchronized (this) {
-                scheduledCp.destroyQueue.addDestroyRequest(cacheCtx, partId);
+                scheduledCp.destroyQueue.addDestroyRequest(grp, partId);
             }
 
             wakeupForCheckpoint(partDestroyCheckpointDelay, "partition destroy");
         }
 
         /**
-         * @param cacheCtx Cache context.
+         * @param grpId Cache group ID.
          * @param partId Partition ID.
          */
-        private void cancelOrWaitPartitionDestroy(GridCacheContext<?, ?> cacheCtx, int partId)
+        private void cancelOrWaitPartitionDestroy(int grpId, int partId)
             throws IgniteCheckedException {
             CheckpointProgress cur = curCpProgress;
 
             PartitionDestroyRequest req;
 
             if (cur != null) {
-                req = cur.destroyQueue.cancelDestroy(cacheCtx.cacheId(), partId);
+                req = cur.destroyQueue.cancelDestroy(grpId, partId);
 
                 if (req != null)
                     req.waitCompleted();
             }
 
             synchronized (this) {
-                req = scheduledCp.destroyQueue.cancelDestroy(cacheCtx.cacheId(), partId);
+                req = scheduledCp.destroyQueue.cancelDestroy(grpId, partId);
             }
 
             if (req != null)
@@ -2060,7 +2052,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
                     if (req != null) {
                         // Log destroy record before actual partition clear.
-                        lastPtr = cctx.wal().log(new PartitionDestroyRecord(req.cacheId, req.partId));
+                        lastPtr = cctx.wal().log(new PartitionDestroyRecord(req.grpId, req.partId));
 
                         if (reqs == null)
                             reqs = new ArrayList<>();
@@ -2178,18 +2170,16 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 for (DbCheckpointListener lsnr : lsnrs)
                     lsnr.onCheckpointBegin(ctx0);
 
-                Collection<GridCacheContext> cacheCtxs = ((GridCacheSharedContext<Object, Object>)cctx).cacheContexts();
+                for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                    if (grp.isLocal())
+                        continue;
 
-                for (GridCacheContext cacheCtx : cacheCtxs) {
                     CacheState state = new CacheState();
 
-                    if (cacheCtx.isLocal())
-                        continue;
-
-                    for (GridDhtLocalPartition part : cacheCtx.topology().currentLocalPartitions())
-                        state.addPartitionState(part.id(), part.dataStore().size(), part.lastAppliedUpdate());
+                    for (GridDhtLocalPartition part : grp.topology().currentLocalPartitions())
+                        state.addPartitionState(part.id(), part.dataStore().fullSize(), part.lastAppliedUpdate());
 
-                    cpRec.addCacheState(cacheCtx.cacheId(), state);
+                    cpRec.addCacheGroupState(grp.groupId(), state);
                 }
 
                 if (curr.nextSnapshot)
@@ -2359,14 +2349,14 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
                     snapshotMgr.beforePageWrite(fullId);
 
-                    int cacheId = fullId.cacheId();
+                    int grpId = fullId.cacheId();
 
-                    GridCacheContext cacheCtx = context().cacheContext(cacheId);
+                    CacheGroupContext grp = context().cache().cacheGroup(grpId);
 
-                    if (cacheCtx == null)
+                    if (grp == null)
                         continue;
 
-                    PageMemoryEx pageMem = (PageMemoryEx) cacheCtx.memoryPolicy().pageMemory();
+                    PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
 
                     Integer tag = pageMem.getForCheckpoint(fullId, tmpWriteBuf);
 
@@ -2385,7 +2375,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
                         PageIO.setCrc(writeAddr, 0);
 
-                        PageStore store = storeMgr.writeInternal(cacheId, fullId.pageId(), tmpWriteBuf, tag);
+                        PageStore store = storeMgr.writeInternal(grpId, fullId.pageId(), tmpWriteBuf, tag);
 
                         updStores.add(store);
                     }
@@ -2685,15 +2675,15 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 try {
                     entry.initIfNeeded(cctx);
 
-                    if (entry.cacheStates == null)
+                    if (entry.cacheGrpStates == null)
                         continue;
 
-                    CacheState cacheState = entry.cacheStates.get(cacheId);
+                    CacheState grpState = entry.cacheGrpStates.get(cacheId);
 
-                    if (cacheState == null)
+                    if (grpState == null)
                         continue;
 
-                    CacheState.PartitionState partState = cacheState.partitions().get(partId);
+                    CacheState.PartitionState partState = grpState.partitions().get(partId);
 
                     if (partState != null) {
                         if (cctx.wal().reserve(entry.checkpointMark()))
@@ -2734,7 +2724,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         private UUID cpId;
 
         /** Cache states. Initialized lazily. */
-        private Map<Integer, CacheState> cacheStates;
+        private Map<Integer, CacheState> cacheGrpStates;
 
         /** Initialization exception. */
         private IgniteCheckedException initEx;
@@ -2760,13 +2750,13 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
          * @param cpTs Checkpoint timestamp.
          * @param cpMark Checkpoint mark pointer.
          * @param cpId Checkpoint ID.
-         * @param cacheStates Cache states.
+         * @param cacheGrpStates Cache groups states.
          */
-        private CheckpointEntry(long cpTs, WALPointer cpMark, UUID cpId, Map<Integer, CacheState> cacheStates) {
+        private CheckpointEntry(long cpTs, WALPointer cpMark, UUID cpId, Map<Integer, CacheState> cacheGrpStates) {
             this.cpTs = cpTs;
             this.cpMark = cpMark;
             this.cpId = cpId;
-            this.cacheStates = cacheStates;
+            this.cacheGrpStates = cacheGrpStates;
 
             initGuard = 1;
             initLatch = new CountDownLatch(0);
@@ -2808,17 +2798,17 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         }
 
         /**
-         * @param cacheId Cache ID.
+         * @param grpId Cache group ID.
          * @param part Partition ID.
          * @return Partition counter or {@code null} if not found.
          */
-        private Long partitionCounter(int cacheId, int part) {
+        private Long partitionCounter(int grpId, int part) {
             assert initGuard != 0;
 
-            if (initEx != null || cacheStates == null)
+            if (initEx != null || cacheGrpStates == null)
                 return null;
 
-            CacheState state = cacheStates.get(cacheId);
+            CacheState state = cacheGrpStates.get(grpId);
 
             if (state != null) {
                 CacheState.PartitionState partState = state.partitions().get(part);
@@ -2841,7 +2831,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                         CheckpointRecord rec = (CheckpointRecord)tup.get2();
 
                         cpId = rec.checkpointId();
-                        cacheStates = rec.cacheStates();
+                        cacheGrpStates = rec.cacheGroupStates();
                     }
                     else
                         initEx = new IgniteCheckedException("Failed to find checkpoint record at " +
@@ -2874,16 +2864,16 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             new ConcurrentHashMap<>();
 
         /**
-         * @param cacheCtx Cache context.
+         * @param grp Cache group.
          * @param partId Partition ID to destroy.
          */
-        private void addDestroyRequest(GridCacheContext<?, ?> cacheCtx, int partId) {
-            PartitionDestroyRequest req = new PartitionDestroyRequest(cacheCtx, partId);
+        private void addDestroyRequest(CacheGroupContext grp, int partId) {
+            PartitionDestroyRequest req = new PartitionDestroyRequest(grp, partId);
 
-            PartitionDestroyRequest old = pendingReqs.putIfAbsent(new T2<>(cacheCtx.cacheId(), partId), req);
+            PartitionDestroyRequest old = pendingReqs.putIfAbsent(new T2<>(grp.groupId(), partId), req);
 
             assert old == null : "Must wait for old destroy request to finish before adding a new one " +
-                "[cacheId=" + cacheCtx.cacheId() + ", cacheName=" + cacheCtx.name() + ", partId=" + partId + ']';
+                "[grpId=" + grp.groupId() + ", name=" + grp.cacheOrGroupName() + ", partId=" + partId + ']';
         }
 
         /**
@@ -2913,10 +2903,10 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
      */
     private static class PartitionDestroyRequest {
         /** */
-        private int cacheId;
+        private int grpId;
 
         /** */
-        private String cacheName;
+        private String name;
 
         /** */
         private int partId;
@@ -2931,13 +2921,13 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         private GridFutureAdapter<Void> destroyFut;
 
         /**
-         * @param cacheCtx Cache context.
+         * @param grp Cache group.
          * @param partId Partition ID.
          */
-        private PartitionDestroyRequest(GridCacheContext<?, ?> cacheCtx, int partId) {
-            cacheId = cacheCtx.cacheId();
-            cacheName = cacheCtx.name();
-            allowFastEviction = cacheCtx.allowFastEviction();
+        private PartitionDestroyRequest(CacheGroupContext grp, int partId) {
+            grpId = grp.groupId();
+            name = grp.cacheOrGroupName();
+            allowFastEviction = grp.allowFastEviction();
 
             this.partId = partId;
         }
@@ -3005,7 +2995,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
         /** {@inheritDoc} */
         @Override public String toString() {
-            return "PartitionDestroyRequest [cacheId=" + cacheId + ", cacheName=" + cacheName +
+            return "PartitionDestroyRequest [grpId=" + grpId + ", name=" + name +
                 ", partId=" + partId + ']';
         }
     }


[08/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysResponse.java
index c4c57a7..977e9ba 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtForceKeysResponse.java
@@ -29,7 +29,7 @@ import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -43,7 +43,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Force keys response. Contains absent keys.
  */
-public class GridDhtForceKeysResponse extends GridCacheMessage implements GridCacheDeployable {
+public class GridDhtForceKeysResponse extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -168,7 +168,7 @@ public class GridDhtForceKeysResponse extends GridCacheMessage implements GridCa
 
         if (infos != null) {
             for (GridCacheEntryInfo info : infos)
-                info.marshal(cctx);
+                info.marshal(cctx.cacheObjectContext());
         }
 
         if (err != null && errBytes == null)
@@ -186,7 +186,7 @@ public class GridDhtForceKeysResponse extends GridCacheMessage implements GridCa
 
         if (infos != null) {
             for (GridCacheEntryInfo info : infos)
-                info.unmarshal(cctx, ldr);
+                info.unmarshal(cctx.cacheObjectContext(), ldr);
         }
 
         if (errBytes != null && err == null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemandMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemandMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemandMessage.java
index ef6a3b9..4a693bf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemandMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemandMessage.java
@@ -27,7 +27,7 @@ import org.apache.ignite.internal.GridDirectCollection;
 import org.apache.ignite.internal.GridDirectMap;
 import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -39,7 +39,7 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Partition demand request.
  */
-public class GridDhtPartitionDemandMessage extends GridCacheMessage {
+public class GridDhtPartitionDemandMessage extends GridCacheGroupIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -77,10 +77,10 @@ public class GridDhtPartitionDemandMessage extends GridCacheMessage {
     /**
      * @param updateSeq Update sequence for this node.
      * @param topVer Topology version.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      */
-    GridDhtPartitionDemandMessage(long updateSeq, @NotNull AffinityTopologyVersion topVer, int cacheId) {
-        this.cacheId = cacheId;
+    GridDhtPartitionDemandMessage(long updateSeq, @NotNull AffinityTopologyVersion topVer, int grpId) {
+        this.grpId = grpId;
         this.updateSeq = updateSeq;
         this.topVer = topVer;
     }
@@ -91,7 +91,7 @@ public class GridDhtPartitionDemandMessage extends GridCacheMessage {
      */
     GridDhtPartitionDemandMessage(GridDhtPartitionDemandMessage cp, Collection<Integer> parts,
         Map<Integer, Long> partsCntrs) {
-        cacheId = cp.cacheId;
+        grpId = cp.grpId;
         updateSeq = cp.updateSeq;
         topic = cp.topic;
         timeout = cp.timeout;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
index 3c04617..cda24e5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java
@@ -27,7 +27,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
@@ -38,13 +37,17 @@ import org.apache.ignite.events.DiscoveryEvent;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.CacheMetricsImpl;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
 import org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
@@ -78,7 +81,10 @@ import static org.apache.ignite.internal.processors.dr.GridDrType.DR_PRELOAD;
 @SuppressWarnings("NonConstantFieldWithUpperCaseName")
 public class GridDhtPartitionDemander {
     /** */
-    private final GridCacheContext<?, ?> cctx;
+    private final GridCacheSharedContext<?, ?> ctx;
+
+    /** */
+    private final CacheGroupContext grp;
 
     /** */
     private final IgniteLogger log;
@@ -104,30 +110,20 @@ public class GridDhtPartitionDemander {
     private final Map<Integer, Object> rebalanceTopics;
 
     /**
-     * Started event sent.
-     * Make sense for replicated cache only.
+     * @param grp Ccahe group.
      */
-    private final AtomicBoolean startedEvtSent = new AtomicBoolean();
+    public GridDhtPartitionDemander(CacheGroupContext grp) {
+        assert grp != null;
 
-    /**
-     * Stopped event sent.
-     * Make sense for replicated cache only.
-     */
-    private final AtomicBoolean stoppedEvtSent = new AtomicBoolean();
+        this.grp = grp;
 
-    /**
-     * @param cctx Cctx.
-     */
-    public GridDhtPartitionDemander(GridCacheContext<?, ?> cctx) {
-        assert cctx != null;
-
-        this.cctx = cctx;
+        ctx = grp.shared();
 
-        log = cctx.logger(getClass());
+        log = ctx.logger(getClass());
 
-        boolean enabled = cctx.rebalanceEnabled() && !cctx.kernalContext().clientNode();
+        boolean enabled = grp.rebalanceEnabled() && !ctx.kernalContext().clientNode();
 
-        rebalanceFut = new RebalanceFuture();//Dummy.
+        rebalanceFut = new RebalanceFuture(); //Dummy.
 
         if (!enabled) {
             // Calling onDone() immediately since preloading is disabled.
@@ -137,7 +133,7 @@ public class GridDhtPartitionDemander {
 
         Map<Integer, Object> tops = new HashMap<>();
 
-        for (int idx = 0; idx < cctx.gridConfig().getRebalanceThreadPoolSize(); idx++)
+        for (int idx = 0; idx < grp.shared().kernalContext().config().getRebalanceThreadPoolSize(); idx++)
             tops.put(idx, GridCachePartitionExchangeManager.rebalanceTopic(idx));
 
         rebalanceTopics = tops;
@@ -196,7 +192,7 @@ public class GridDhtPartitionDemander {
         GridTimeoutObject obj = lastTimeoutObj.getAndSet(null);
 
         if (obj != null)
-            cctx.time().removeTimeoutObject(obj);
+            ctx.time().removeTimeoutObject(obj);
 
         final GridDhtPartitionsExchangeFuture exchFut = lastExchangeFut;
 
@@ -208,7 +204,7 @@ public class GridDhtPartitionDemander {
 
             exchFut.listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() {
                 @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> t) {
-                    IgniteInternalFuture<Boolean> fut0 = cctx.shared().exchange().forceRebalance(exchFut);
+                    IgniteInternalFuture<Boolean> fut0 = ctx.exchange().forceRebalance(exchFut);
 
                     fut0.listen(new IgniteInClosure<IgniteInternalFuture<Boolean>>() {
                         @Override public void apply(IgniteInternalFuture<Boolean> future) {
@@ -237,7 +233,7 @@ public class GridDhtPartitionDemander {
      */
     private boolean topologyChanged(RebalanceFuture fut) {
         return
-            !cctx.affinity().affinityTopologyVersion().equals(fut.topologyVersion()) || // Topology already changed.
+            !grp.affinity().lastVersion().equals(fut.topologyVersion()) || // Topology already changed.
                 fut != rebalanceFut; // Same topology, but dummy exchange forced because of missing partitions.
     }
 
@@ -268,12 +264,12 @@ public class GridDhtPartitionDemander {
 
         assert force == (forcedRebFut != null);
 
-        long delay = cctx.config().getRebalanceDelay();
+        long delay = grp.config().getRebalanceDelay();
 
         if (delay == 0 || force) {
             final RebalanceFuture oldFut = rebalanceFut;
 
-            final RebalanceFuture fut = new RebalanceFuture(assigns, cctx, log, startedEvtSent, stoppedEvtSent, cnt);
+            final RebalanceFuture fut = new RebalanceFuture(grp, assigns, log, cnt);
 
             if (!oldFut.isInitial())
                 oldFut.cancel();
@@ -302,16 +298,18 @@ public class GridDhtPartitionDemander {
 
             fut.sendRebalanceStartedEvent();
 
-            final boolean statsEnabled = cctx.config().isStatisticsEnabled();
+            for (GridCacheContext cctx : grp.caches()) {
+                if (cctx.config().isStatisticsEnabled()) {
+                    final CacheMetricsImpl metrics = cctx.cache().metrics0();
 
-            if (statsEnabled) {
-                cctx.cache().metrics0().clearRebalanceCounters();
+                    metrics.clearRebalanceCounters();
 
-                rebalanceFut.listen(new IgniteInClosure<IgniteInternalFuture<Boolean>>() {
-                    @Override public void apply(IgniteInternalFuture<Boolean> fut) {
-                        cctx.cache().metrics0().clearRebalanceCounters();
-                    }
-                });
+                    rebalanceFut.listen(new IgniteInClosure<IgniteInternalFuture<Boolean>>() {
+                        @Override public void apply(IgniteInternalFuture<Boolean> fut) {
+                            metrics.clearRebalanceCounters();
+                        }
+                    });
+                }
             }
 
             if (assigns.cancelled()) { // Pending exchange.
@@ -331,7 +329,7 @@ public class GridDhtPartitionDemander {
 
                 fut.onDone(true);
 
-                ((GridFutureAdapter)cctx.preloader().syncFuture()).onDone();
+                ((GridFutureAdapter)grp.preloader().syncFuture()).onDone();
 
                 fut.sendRebalanceFinishedEvent();
 
@@ -362,7 +360,7 @@ public class GridDhtPartitionDemander {
             GridTimeoutObject obj = lastTimeoutObj.get();
 
             if (obj != null)
-                cctx.time().removeTimeoutObject(obj);
+                ctx.time().removeTimeoutObject(obj);
 
             final GridDhtPartitionsExchangeFuture exchFut = lastExchangeFut;
 
@@ -372,7 +370,7 @@ public class GridDhtPartitionDemander {
                 @Override public void onTimeout() {
                     exchFut.listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() {
                         @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> f) {
-                            cctx.shared().exchange().forceRebalance(exchFut);
+                            ctx.exchange().forceRebalance(exchFut);
                         }
                     });
                 }
@@ -380,7 +378,7 @@ public class GridDhtPartitionDemander {
 
             lastTimeoutObj.set(obj);
 
-            cctx.time().addTimeoutObject(obj);
+            ctx.time().addTimeoutObject(obj);
         }
 
         return null;
@@ -389,7 +387,6 @@ public class GridDhtPartitionDemander {
     /**
      * @param fut Rebalance future.
      * @param assigns Assignments.
-     * @throws IgniteCheckedException If failed.
      */
     private void requestPartitions(final RebalanceFuture fut, GridDhtPreloaderAssignments assigns) {
         assert fut != null;
@@ -411,17 +408,19 @@ public class GridDhtPartitionDemander {
 
                 Collection<Integer> parts= e.getValue().partitions();
 
-                assert parts != null : "Partitions are null [cache=" + cctx.name() + ", fromNode=" + nodeId + "]";
+                assert parts != null : "Partitions are null [grp=" + grp.cacheOrGroupName() + ", fromNode=" + nodeId + "]";
 
                 fut.remaining.put(nodeId, new T2<>(U.currentTimeMillis(), parts));
             }
         }
 
+        final CacheConfiguration cfg = grp.config();
+
+        int lsnrCnt = ctx.gridConfig().getRebalanceThreadPoolSize();
+
         for (Map.Entry<ClusterNode, GridDhtPartitionDemandMessage> e : assigns.entrySet()) {
             final ClusterNode node = e.getKey();
 
-            final CacheConfiguration cfg = cctx.config();
-
             final Collection<Integer> parts = fut.remaining.get(node.id()).get2();
 
             GridDhtPartitionDemandMessage d = e.getValue();
@@ -430,8 +429,6 @@ public class GridDhtPartitionDemander {
                 ", fromNode=" + node.id() + ", partitionsCount=" + parts.size() +
                 ", topology=" + fut.topologyVersion() + ", updateSeq=" + fut.updateSeq + "]");
 
-            int lsnrCnt = cctx.gridConfig().getRebalanceThreadPoolSize();
-
             final List<Set<Integer>> sParts = new ArrayList<>(lsnrCnt);
 
             for (int cnt = 0; cnt < lsnrCnt; cnt++)
@@ -451,16 +448,15 @@ public class GridDhtPartitionDemander {
 
                     initD.topic(rebalanceTopics.get(cnt));
                     initD.updateSequence(fut.updateSeq);
-                    initD.timeout(cctx.config().getRebalanceTimeout());
+                    initD.timeout(cfg.getRebalanceTimeout());
 
                     final int finalCnt = cnt;
 
-                    cctx.kernalContext().closure().runLocalSafe(new Runnable() {
+                    ctx.kernalContext().closure().runLocalSafe(new Runnable() {
                         @Override public void run() {
                             try {
                                 if (!fut.isDone()) {
-                                    cctx.io().sendOrderedMessage(node,
-                                        rebalanceTopics.get(finalCnt), initD, cctx.ioPolicy(), initD.timeout());
+                                    ctx.io().sendOrderedMessage(node, rebalanceTopics.get(finalCnt), initD, grp.ioPolicy(), initD.timeout());
 
                                     // Cleanup required in case partitions demanded in parallel with cancellation.
                                     synchronized (fut) {
@@ -507,11 +503,11 @@ public class GridDhtPartitionDemander {
 
         for (Integer part : parts) {
             try {
-                if (cctx.shared().database().persistenceEnabled()) {
+                if (ctx.database().persistenceEnabled()) {
                     if (partCntrs == null)
                         partCntrs = new HashMap<>(parts.size(), 1.0f);
 
-                    GridDhtLocalPartition p = cctx.topology().localPartition(part, old.topologyVersion(), false);
+                    GridDhtLocalPartition p = grp.topology().localPartition(part, old.topologyVersion(), false);
 
                     partCntrs.put(part, p.initialUpdateCounter());
                 }
@@ -587,7 +583,7 @@ public class GridDhtPartitionDemander {
 
         final RebalanceFuture fut = rebalanceFut;
 
-        ClusterNode node = cctx.node(id);
+        ClusterNode node = ctx.node(id);
 
         if (node == null)
             return;
@@ -611,23 +607,42 @@ public class GridDhtPartitionDemander {
             return;
         }
 
-        final GridDhtPartitionTopology top = cctx.dht().topology();
+        final GridDhtPartitionTopology top = grp.topology();
+
+        if (grp.sharedGroup()) {
+            for (GridCacheContext cctx : grp.caches()) {
+                if (cctx.config().isStatisticsEnabled()) {
+                    long keysCnt = supply.keysForCache(cctx.cacheId());
 
-        final boolean statsEnabled = cctx.config().isStatisticsEnabled();
+                    if (keysCnt != -1)
+                        cctx.cache().metrics0().onRebalancingKeysCountEstimateReceived(keysCnt);
 
-        if (statsEnabled) {
-            if (supply.estimatedKeysCount() != -1)
-                cctx.cache().metrics0().onRebalancingKeysCountEstimateReceived(supply.estimatedKeysCount());
+                    // Can not be calculated per cache.
+                    cctx.cache().metrics0().onRebalanceBatchReceived(supply.messageSize());
+                }
+            }
+        }
+        else {
+            GridCacheContext cctx = grp.singleCacheContext();
 
-            cctx.cache().metrics0().onRebalanceBatchReceived(supply.messageSize());
+            if (cctx.config().isStatisticsEnabled()) {
+                if (supply.estimatedKeysCount() != -1)
+                    cctx.cache().metrics0().onRebalancingKeysCountEstimateReceived(supply.estimatedKeysCount());
+
+                cctx.cache().metrics0().onRebalanceBatchReceived(supply.messageSize());
+            }
         }
 
         try {
+            AffinityAssignment aff = grp.affinity().cachedAffinity(topVer);
+
+            GridCacheContext cctx = grp.sharedGroup() ? null : grp.singleCacheContext();
+
             // Preload.
             for (Map.Entry<Integer, CacheEntryInfoCollection> e : supply.infos().entrySet()) {
                 int p = e.getKey();
 
-                if (cctx.affinity().partitionLocalNode(p, topVer)) {
+                if (aff.get(p).contains(ctx.localNode())) {
                     GridDhtLocalPartition part = top.localPartition(p, topVer, true);
 
                     assert part != null;
@@ -638,7 +653,7 @@ public class GridDhtPartitionDemander {
                         boolean reserved = part.reserve();
 
                         assert reserved : "Failed to reserve partition [igniteInstanceName=" +
-                            cctx.igniteInstanceName() + ", cacheName=" + cctx.name() + ", part=" + part + ']';
+                            ctx.igniteInstanceName() + ", grp=" + grp.cacheOrGroupName() + ", part=" + part + ']';
 
                         part.lock();
 
@@ -662,7 +677,10 @@ public class GridDhtPartitionDemander {
                                     break;
                                 }
 
-                                if (statsEnabled)
+                                if (grp.sharedGroup() && (cctx == null || cctx.cacheId() != entry.cacheId()))
+                                    cctx = ctx.cacheContext(entry.cacheId());
+
+                                if(cctx != null && cctx.config().isStatisticsEnabled())
                                     cctx.cache().metrics0().onRebalanceKeyReceived();
                             }
 
@@ -700,7 +718,7 @@ public class GridDhtPartitionDemander {
 
             // Only request partitions based on latest topology version.
             for (Integer miss : supply.missed()) {
-                if (cctx.affinity().partitionLocalNode(miss, topVer))
+                if (aff.get(miss).contains(ctx.localNode()))
                     fut.partitionMissed(id, miss);
             }
 
@@ -708,16 +726,18 @@ public class GridDhtPartitionDemander {
                 fut.partitionDone(id, miss);
 
             GridDhtPartitionDemandMessage d = new GridDhtPartitionDemandMessage(
-                supply.updateSequence(), supply.topologyVersion(), cctx.cacheId());
+                supply.updateSequence(),
+                supply.topologyVersion(),
+                grp.groupId());
 
-            d.timeout(cctx.config().getRebalanceTimeout());
+            d.timeout(grp.config().getRebalanceTimeout());
 
             d.topic(rebalanceTopics.get(idx));
 
             if (!topologyChanged(fut) && !fut.isDone()) {
                 // Send demand message.
-                cctx.io().sendOrderedMessage(node, rebalanceTopics.get(idx),
-                    d, cctx.ioPolicy(), cctx.config().getRebalanceTimeout());
+                ctx.io().sendOrderedMessage(node, rebalanceTopics.get(idx),
+                    d, grp.ioPolicy(), grp.config().getRebalanceTimeout());
             }
         }
         catch (IgniteCheckedException e) {
@@ -746,11 +766,15 @@ public class GridDhtPartitionDemander {
         GridCacheEntryInfo entry,
         AffinityTopologyVersion topVer
     ) throws IgniteCheckedException {
+        ctx.database().checkpointReadLock();
+
         try {
             GridCacheEntryEx cached = null;
 
             try {
-                cached = cctx.dht().entryEx(entry.key());
+                GridCacheContext cctx = grp.sharedGroup() ? ctx.cacheContext(entry.cacheId()) : grp.singleCacheContext();
+
+                cached = cctx.dhtCache().entryEx(entry.key());
 
                 if (log.isDebugEnabled())
                     log.debug("Rebalancing key [key=" + entry.key() + ", part=" + p + ", node=" + pick.id() + ']');
@@ -808,7 +832,10 @@ public class GridDhtPartitionDemander {
         }
         catch (IgniteCheckedException e) {
             throw new IgniteCheckedException("Failed to cache rebalanced entry (will stop rebalancing) [local=" +
-                cctx.nodeId() + ", node=" + pick.id() + ", key=" + entry.key() + ", part=" + p + ']', e);
+                ctx.localNode() + ", node=" + pick.id() + ", key=" + entry.key() + ", part=" + p + ']', e);
+        }
+        finally {
+            ctx.database().checkpointReadUnlock();
         }
 
         return true;
@@ -824,16 +851,10 @@ public class GridDhtPartitionDemander {
      */
     public static class RebalanceFuture extends GridFutureAdapter<Boolean> {
         /** */
-        private static final long serialVersionUID = 1L;
-
-        /** Should EVT_CACHE_REBALANCE_STARTED event be sent or not. */
-        private final AtomicBoolean startedEvtSent;
-
-        /** Should EVT_CACHE_REBALANCE_STOPPED event be sent or not. */
-        private final AtomicBoolean stoppedEvtSent;
+        private final GridCacheSharedContext<?, ?> ctx;
 
         /** */
-        private final GridCacheContext<?, ?> cctx;
+        private final CacheGroupContext grp;
 
         /** */
         private final IgniteLogger log;
@@ -855,42 +876,38 @@ public class GridDhtPartitionDemander {
         private final long updateSeq;
 
         /**
+         * @param grp Cache group.
          * @param assigns Assigns.
-         * @param cctx Context.
          * @param log Logger.
-         * @param startedEvtSent Start event sent flag.
-         * @param stoppedEvtSent Stop event sent flag.
          * @param updateSeq Update sequence.
          */
-        RebalanceFuture(GridDhtPreloaderAssignments assigns,
-            GridCacheContext<?, ?> cctx,
+        RebalanceFuture(
+            CacheGroupContext grp,
+            GridDhtPreloaderAssignments assigns,
             IgniteLogger log,
-            AtomicBoolean startedEvtSent,
-            AtomicBoolean stoppedEvtSent,
             long updateSeq) {
             assert assigns != null;
 
             exchFut = assigns.exchangeFuture();
             topVer = assigns.topologyVersion();
 
-            this.cctx = cctx;
+            this.grp = grp;
             this.log = log;
-            this.startedEvtSent = startedEvtSent;
-            this.stoppedEvtSent = stoppedEvtSent;
             this.updateSeq = updateSeq;
+
+            ctx= grp.shared();
         }
 
         /**
          * Dummy future. Will be done by real one.
          */
-        public RebalanceFuture() {
-            exchFut = null;
-            topVer = null;
-            cctx = null;
-            log = null;
-            startedEvtSent = null;
-            stoppedEvtSent = null;
-            updateSeq = -1;
+        RebalanceFuture() {
+            this.exchFut = null;
+            this.topVer = null;
+            this.ctx = null;
+            this.grp = null;
+            this.log = null;
+            this.updateSeq = -1;
         }
 
         /**
@@ -927,7 +944,7 @@ public class GridDhtPartitionDemander {
 
                 U.log(log, "Cancelled rebalancing from all nodes [topology=" + topologyVersion() + ']');
 
-                if (!cctx.kernalContext().isStopping()) {
+                if (!ctx.kernalContext().isStopping()) {
                     for (UUID nodeId : remaining.keySet())
                         cleanupRemoteContexts(nodeId);
                 }
@@ -948,7 +965,7 @@ public class GridDhtPartitionDemander {
                 if (isDone())
                     return;
 
-                U.log(log, ("Cancelled rebalancing [cache=" + cctx.name() +
+                U.log(log, ("Cancelled rebalancing [cache=" + grp.cacheOrGroupName() +
                     ", fromNode=" + nodeId + ", topology=" + topologyVersion() +
                     ", time=" + (U.currentTimeMillis() - remaining.get(nodeId).get1()) + " ms]"));
 
@@ -983,22 +1000,24 @@ public class GridDhtPartitionDemander {
          * @param nodeId Node id.
          */
         private void cleanupRemoteContexts(UUID nodeId) {
-            ClusterNode node = cctx.discovery().node(nodeId);
+            ClusterNode node = ctx.discovery().node(nodeId);
 
             if (node == null)
                 return;
 
             GridDhtPartitionDemandMessage d = new GridDhtPartitionDemandMessage(
-                -1/* remove supply context signal */, this.topologyVersion(), cctx.cacheId());
+                -1/* remove supply context signal */,
+                this.topologyVersion(),
+                grp.groupId());
 
-            d.timeout(cctx.config().getRebalanceTimeout());
+            d.timeout(grp.config().getRebalanceTimeout());
 
             try {
-                for (int idx = 0; idx < cctx.gridConfig().getRebalanceThreadPoolSize(); idx++) {
+                for (int idx = 0; idx < ctx.gridConfig().getRebalanceThreadPoolSize(); idx++) {
                     d.topic(GridCachePartitionExchangeManager.rebalanceTopic(idx));
 
-                    cctx.io().sendOrderedMessage(node, GridCachePartitionExchangeManager.rebalanceTopic(idx),
-                        d, cctx.ioPolicy(), cctx.config().getRebalanceTimeout());
+                    ctx.io().sendOrderedMessage(node, GridCachePartitionExchangeManager.rebalanceTopic(idx),
+                        d, grp.ioPolicy(), grp.config().getRebalanceTimeout());
                 }
             }
             catch (IgniteCheckedException ignored) {
@@ -1016,20 +1035,19 @@ public class GridDhtPartitionDemander {
                 if (isDone())
                     return;
 
-                if (cctx.events().isRecordable(EVT_CACHE_REBALANCE_PART_LOADED))
-                    preloadEvent(p, EVT_CACHE_REBALANCE_PART_LOADED,
-                        exchFut.discoveryEvent());
+                if (grp.eventRecordable(EVT_CACHE_REBALANCE_PART_LOADED))
+                    rebalanceEvent(p, EVT_CACHE_REBALANCE_PART_LOADED, exchFut.discoveryEvent());
 
                 T2<Long, Collection<Integer>> t = remaining.get(nodeId);
 
-                assert t != null : "Remaining not found [cache=" + cctx.name() + ", fromNode=" + nodeId +
+                assert t != null : "Remaining not found [grp=" + grp.name() + ", fromNode=" + nodeId +
                     ", part=" + p + "]";
 
                 Collection<Integer> parts = t.get2();
 
                 boolean rmvd = parts.remove(p);
 
-                assert rmvd : "Partition already done [cache=" + cctx.name() + ", fromNode=" + nodeId +
+                assert rmvd : "Partition already done [grp=" + grp.name() + ", fromNode=" + nodeId +
                     ", part=" + p + ", left=" + parts + "]";
 
                 if (parts.isEmpty()) {
@@ -1049,18 +1067,18 @@ public class GridDhtPartitionDemander {
          * @param type Type.
          * @param discoEvt Discovery event.
          */
-        private void preloadEvent(int part, int type, DiscoveryEvent discoEvt) {
+        private void rebalanceEvent(int part, int type, DiscoveryEvent discoEvt) {
             assert discoEvt != null;
 
-            cctx.events().addPreloadEvent(part, type, discoEvt.eventNode(), discoEvt.type(), discoEvt.timestamp());
+            grp.addRebalanceEvent(part, type, discoEvt.eventNode(), discoEvt.type(), discoEvt.timestamp());
         }
 
         /**
          * @param type Type.
          * @param discoEvt Discovery event.
          */
-        private void preloadEvent(int type, DiscoveryEvent discoEvt) {
-            preloadEvent(-1, type, discoEvt);
+        private void rebalanceEvent(int type, DiscoveryEvent discoEvt) {
+            rebalanceEvent(-1, type, discoEvt);
         }
 
         /**
@@ -1080,7 +1098,7 @@ public class GridDhtPartitionDemander {
                 if (log.isDebugEnabled())
                     log.debug("Completed rebalance future: " + this);
 
-                cctx.shared().exchange().scheduleResendPartitions();
+                ctx.exchange().scheduleResendPartitions();
 
                 Collection<Integer> m = new HashSet<>();
 
@@ -1094,13 +1112,13 @@ public class GridDhtPartitionDemander {
 
                     onDone(false); //Finished but has missed partitions, will force dummy exchange
 
-                    cctx.shared().exchange().forceDummyExchange(true, exchFut);
+                    ctx.exchange().forceDummyExchange(true, exchFut);
 
                     return;
                 }
 
-                if (!cancelled && !cctx.preloader().syncFuture().isDone())
-                    ((GridFutureAdapter)cctx.preloader().syncFuture()).onDone();
+                if (!cancelled && !grp.preloader().syncFuture().isDone())
+                    ((GridFutureAdapter)grp.preloader().syncFuture()).onDone();
 
                 onDone(!cancelled);
             }
@@ -1110,24 +1128,16 @@ public class GridDhtPartitionDemander {
          *
          */
         private void sendRebalanceStartedEvent() {
-            if (cctx.events().isRecordable(EVT_CACHE_REBALANCE_STARTED) &&
-                (!cctx.isReplicated() || !startedEvtSent.get())) {
-                preloadEvent(EVT_CACHE_REBALANCE_STARTED, exchFut.discoveryEvent());
-
-                startedEvtSent.set(true);
-            }
+            if (grp.eventRecordable(EVT_CACHE_REBALANCE_STARTED))
+                rebalanceEvent(EVT_CACHE_REBALANCE_STARTED, exchFut.discoveryEvent());
         }
 
         /**
          *
          */
         private void sendRebalanceFinishedEvent() {
-            if (cctx.events().isRecordable(EVT_CACHE_REBALANCE_STOPPED) &&
-                (!cctx.isReplicated() || !stoppedEvtSent.get())) {
-                preloadEvent(EVT_CACHE_REBALANCE_STOPPED, exchFut.discoveryEvent());
-
-                stoppedEvtSent.set(true);
-            }
+            if (grp.eventRecordable(EVT_CACHE_REBALANCE_STOPPED))
+                rebalanceEvent(EVT_CACHE_REBALANCE_STOPPED, exchFut.discoveryEvent());
         }
 
         /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
index 0ff03f7..467b906 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplier.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.processors.cache.distributed.dht.preloader;
 
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
@@ -26,7 +27,7 @@ import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
 import org.apache.ignite.internal.processors.cache.IgniteRebalanceIterator;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
@@ -47,7 +48,7 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
  */
 class GridDhtPartitionSupplier {
     /** */
-    private final GridCacheContext<?, ?> cctx;
+    private final CacheGroupContext grp;
 
     /** */
     private final IgniteLogger log;
@@ -65,18 +66,18 @@ class GridDhtPartitionSupplier {
     private final Map<T3<UUID, Integer, AffinityTopologyVersion>, SupplyContext> scMap = new HashMap<>();
 
     /**
-     * @param cctx Cache context.
+     * @param grp Cache group.
      */
-    GridDhtPartitionSupplier(GridCacheContext<?, ?> cctx) {
-        assert cctx != null;
+    GridDhtPartitionSupplier(CacheGroupContext grp) {
+        assert grp != null;
 
-        this.cctx = cctx;
+        this.grp = grp;
 
-        log = cctx.logger(getClass());
+        log = grp.shared().logger(getClass());
 
-        top = cctx.dht().topology();
+        top = grp.topology();
 
-        depEnabled = cctx.gridDeploy().enabled();
+        depEnabled = grp.shared().gridDeploy().enabled();
     }
 
     /**
@@ -171,7 +172,7 @@ class GridDhtPartitionSupplier {
         assert d != null;
         assert id != null;
 
-        AffinityTopologyVersion cutTop = cctx.affinity().affinityTopologyVersion();
+        AffinityTopologyVersion cutTop = grp.affinity().lastVersion();
         AffinityTopologyVersion demTop = d.topologyVersion();
 
         T3<UUID, Integer, AffinityTopologyVersion> scId = new T3<>(id, idx, demTop);
@@ -197,9 +198,12 @@ class GridDhtPartitionSupplier {
                 ", from=" + id + ", idx=" + idx + "]");
 
         GridDhtPartitionSupplyMessage s = new GridDhtPartitionSupplyMessage(
-            d.updateSequence(), cctx.cacheId(), d.topologyVersion(), cctx.deploymentEnabled());
+            d.updateSequence(),
+            grp.groupId(),
+            d.topologyVersion(),
+            grp.deploymentEnabled());
 
-        ClusterNode node = cctx.discovery().node(id);
+        ClusterNode node = grp.shared().discovery().node(id);
 
         if (node == null)
             return; // Context will be cleaned at topology change.
@@ -225,7 +229,7 @@ class GridDhtPartitionSupplier {
 
             boolean newReq = true;
 
-            long maxBatchesCnt = cctx.config().getRebalanceBatchesPrefetchCount();
+            long maxBatchesCnt = grp.config().getRebalanceBatchesPrefetchCount();
 
             if (sctx != null) {
                 phase = sctx.phase;
@@ -234,7 +238,7 @@ class GridDhtPartitionSupplier {
             }
             else {
                 if (log.isDebugEnabled())
-                    log.debug("Starting supplying rebalancing [cache=" + cctx.name() +
+                    log.debug("Starting supplying rebalancing [cache=" + grp.cacheOrGroupName() +
                         ", fromNode=" + node.id() + ", partitionsCount=" + d.partitions().size() +
                         ", topology=" + d.topologyVersion() + ", updateSeq=" + d.updateSequence() +
                         ", idx=" + idx + "]");
@@ -243,18 +247,19 @@ class GridDhtPartitionSupplier {
             Iterator<Integer> partIt = sctx != null ? sctx.partIt : d.partitions().iterator();
 
             if (sctx == null) {
-                long keysCnt = 0;
-
                 for (Integer part : d.partitions()) {
                     GridDhtLocalPartition loc = top.localPartition(part, d.topologyVersion(), false);
 
                     if (loc == null || loc.state() != OWNING)
                         continue;
 
-                    keysCnt += cctx.offheap().entriesCount(part);
+                    if (grp.sharedGroup()) {
+                        for (int cacheId : grp.cacheIds())
+                            s.addKeysForCache(cacheId, grp.offheap().cacheEntriesCount(cacheId, part));
+                    }
+                    else
+                        s.addEstimatedKeysCount(grp.offheap().totalPartitionEntriesCount(part));
                 }
-
-                s.estimatedKeysCount(keysCnt);
             }
 
             while ((sctx != null && newReq) || partIt.hasNext()) {
@@ -295,22 +300,24 @@ class GridDhtPartitionSupplier {
                         IgniteRebalanceIterator iter;
 
                         if (sctx == null || sctx.entryIt == null) {
-                            iter = cctx.offheap().rebalanceIterator(part, d.topologyVersion(),
+                            iter = grp.offheap().rebalanceIterator(part, d.topologyVersion(),
                                 d.isHistorical(part) ? d.partitionCounter(part) : null);
 
                             if (!iter.historical()) {
-                                assert !cctx.shared().database().persistenceEnabled() || !d.isHistorical(part);
+                                assert !grp.shared().database().persistenceEnabled() || !d.isHistorical(part);
 
                                 s.clean(part);
                             }
                             else
-                                assert cctx.shared().database().persistenceEnabled() && d.isHistorical(part);
+                                assert grp.shared().database().persistenceEnabled() && d.isHistorical(part);
                         }
                         else
                             iter = (IgniteRebalanceIterator)sctx.entryIt;
 
                         while (iter.hasNext()) {
-                            if (!cctx.affinity().partitionBelongs(node, part, d.topologyVersion())) {
+                            List<ClusterNode> nodes = grp.affinity().cachedAffinity(d.topologyVersion()).get(part);
+
+                            if (!nodes.contains(node)) {
                                 // Demander no longer needs this partition,
                                 // so we send '-1' partition and move on.
                                 s.missed(part);
@@ -334,7 +341,7 @@ class GridDhtPartitionSupplier {
                                 break;
                             }
 
-                            if (s.messageSize() >= cctx.config().getRebalanceBatchSize()) {
+                            if (s.messageSize() >= grp.config().getRebalanceBatchSize()) {
                                 if (++bCnt >= maxBatchesCnt) {
                                     saveSupplyContext(scId,
                                         phase,
@@ -356,9 +363,9 @@ class GridDhtPartitionSupplier {
                                         return;
 
                                     s = new GridDhtPartitionSupplyMessage(d.updateSequence(),
-                                        cctx.cacheId(),
+                                        grp.groupId(),
                                         d.topologyVersion(),
-                                        cctx.deploymentEnabled());
+                                        grp.deploymentEnabled());
                                 }
                             }
 
@@ -370,9 +377,10 @@ class GridDhtPartitionSupplier {
                             info.expireTime(row.expireTime());
                             info.version(row.version());
                             info.value(row.value());
+                            info.cacheId(row.cacheId());
 
                             if (preloadPred == null || preloadPred.apply(info))
-                                s.addEntry0(part, info, cctx);
+                                s.addEntry0(part, info, grp.shared(), grp.cacheObjectContext());
                             else {
                                 if (log.isDebugEnabled())
                                     log.debug("Rebalance predicate evaluated to false (will not send " +
@@ -421,7 +429,7 @@ class GridDhtPartitionSupplier {
             reply(node, d, s, scId);
 
             if (log.isDebugEnabled())
-                log.debug("Finished supplying rebalancing [cache=" + cctx.name() +
+                log.debug("Finished supplying rebalancing [cache=" + grp.cacheOrGroupName() +
                     ", fromNode=" + node.id() +
                     ", topology=" + d.topologyVersion() + ", updateSeq=" + d.updateSequence() +
                     ", idx=" + idx + "]");
@@ -448,16 +456,15 @@ class GridDhtPartitionSupplier {
         GridDhtPartitionSupplyMessage s,
         T3<UUID, Integer, AffinityTopologyVersion> scId)
         throws IgniteCheckedException {
-
         try {
             if (log.isDebugEnabled())
                 log.debug("Replying to partition demand [node=" + n.id() + ", demand=" + d + ", supply=" + s + ']');
 
-            cctx.io().sendOrderedMessage(n, d.topic(), s, cctx.ioPolicy(), d.timeout());
+            grp.shared().io().sendOrderedMessage(n, d.topic(), s, grp.ioPolicy(), d.timeout());
 
             // Throttle preloading.
-            if (cctx.config().getRebalanceThrottle() > 0)
-                U.sleep(cctx.config().getRebalanceThrottle());
+            if (grp.config().getRebalanceThrottle() > 0)
+                U.sleep(grp.config().getRebalanceThrottle());
 
             return true;
         }
@@ -490,7 +497,7 @@ class GridDhtPartitionSupplier {
         AffinityTopologyVersion topVer,
         long updateSeq) {
         synchronized (scMap) {
-            if (cctx.affinity().affinityTopologyVersion().equals(topVer)) {
+            if (grp.affinity().lastVersion().equals(topVer)) {
                 assert scMap.get(t) == null;
 
                 scMap.put(t,

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplyMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplyMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplyMessage.java
index 1cb32e3..ef14a90 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplyMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionSupplyMessage.java
@@ -28,13 +28,13 @@ import java.util.Map;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.GridDirectCollection;
 import org.apache.ignite.internal.GridDirectMap;
-import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -45,7 +45,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Partition supply message.
  */
-public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements GridCacheDeployable {
+public class GridDhtPartitionSupplyMessage extends GridCacheGroupIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -79,17 +79,21 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
     /** Estimated keys count. */
     private long estimatedKeysCnt = -1;
 
+    /** Estimated keys count per cache in case the message is for shared group. */
+    @GridDirectMap(keyType = int.class, valueType = long.class)
+    private Map<Integer, Long> keysPerCache;
+
     /**
      * @param updateSeq Update sequence for this node.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param topVer Topology version.
      * @param addDepInfo Deployment info flag.
      */
     GridDhtPartitionSupplyMessage(long updateSeq,
-        int cacheId,
+        int grpId,
         AffinityTopologyVersion topVer,
         boolean addDepInfo) {
-        this.cacheId = cacheId;
+        this.grpId = grpId;
         this.updateSeq = updateSeq;
         this.topVer = topVer;
         this.addDepInfo = addDepInfo;
@@ -206,18 +210,19 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
     /**
      * @param p Partition.
      * @param info Entry to add.
-     * @param ctx Cache context.
+     * @param ctx Cache shared context.
+     * @param cacheObjCtx Cache object context.
      * @throws IgniteCheckedException If failed.
      */
-    void addEntry0(int p, GridCacheEntryInfo info, GridCacheContext ctx) throws IgniteCheckedException {
+    void addEntry0(int p, GridCacheEntryInfo info, GridCacheSharedContext ctx, CacheObjectContext cacheObjCtx) throws IgniteCheckedException {
         assert info != null;
         assert info.key() != null : info;
         assert info.value() != null : info;
 
         // Need to call this method to initialize info properly.
-        marshalInfo(info, ctx);
+        marshalInfo(info, ctx, cacheObjCtx);
 
-        msgSize += info.marshalledSize(ctx);
+        msgSize += info.marshalledSize(cacheObjCtx);
 
         CacheEntryInfoCollection infoCol = infos().get(p);
 
@@ -237,13 +242,13 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
     @Override public void finishUnmarshal(GridCacheSharedContext ctx, ClassLoader ldr) throws IgniteCheckedException {
         super.finishUnmarshal(ctx, ldr);
 
-        GridCacheContext cacheCtx = ctx.cacheContext(cacheId);
+        CacheGroupContext grp = ctx.cache().cacheGroup(grpId);
 
         for (CacheEntryInfoCollection col : infos().values()) {
             List<GridCacheEntryInfo> entries = col.infos();
 
             for (int i = 0; i < entries.size(); i++)
-                entries.get(i).unmarshal(cacheCtx, ldr);
+                entries.get(i).unmarshal(grp.cacheObjectContext(), ldr);
         }
     }
 
@@ -281,46 +286,53 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 writer.incrementState();
 
             case 4:
-                if (!writer.writeMap("infos", infos, MessageCollectionItemType.INT, MessageCollectionItemType.MSG))
+                if (!writer.writeLong("estimatedKeysCnt", estimatedKeysCnt))
                     return false;
 
                 writer.incrementState();
 
             case 5:
-                if (!writer.writeCollection("last", last, MessageCollectionItemType.INT))
+                if (!writer.writeMap("infos", infos, MessageCollectionItemType.INT, MessageCollectionItemType.MSG))
                     return false;
 
                 writer.incrementState();
 
             case 6:
-                if (!writer.writeCollection("missed", missed, MessageCollectionItemType.INT))
+                if (!writer.writeMap("keysPerCache", keysPerCache, MessageCollectionItemType.INT, MessageCollectionItemType.LONG))
                     return false;
 
                 writer.incrementState();
 
             case 7:
-                if (!writer.writeMessage("topVer", topVer))
+                if (!writer.writeCollection("last", last, MessageCollectionItemType.INT))
                     return false;
 
                 writer.incrementState();
 
             case 8:
-                if (!writer.writeLong("updateSeq", updateSeq))
+                if (!writer.writeCollection("missed", missed, MessageCollectionItemType.INT))
                     return false;
 
                 writer.incrementState();
 
             case 9:
-                if (!writer.writeLong("estimatedKeysCnt", estimatedKeysCnt))
+                if (!writer.writeInt("msgSize", msgSize))
                     return false;
 
                 writer.incrementState();
 
             case 10:
-                if (!writer.writeInt("msgSize", msgSize))
+                if (!writer.writeMessage("topVer", topVer))
                     return false;
 
                 writer.incrementState();
+
+            case 11:
+                if (!writer.writeLong("updateSeq", updateSeq))
+                    return false;
+
+                writer.incrementState();
+
         }
 
         return true;
@@ -346,7 +358,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 4:
-                infos = reader.readMap("infos", MessageCollectionItemType.INT, MessageCollectionItemType.MSG, false);
+                estimatedKeysCnt = reader.readLong("estimatedKeysCnt");
 
                 if (!reader.isLastRead())
                     return false;
@@ -354,7 +366,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 5:
-                last = reader.readCollection("last", MessageCollectionItemType.INT);
+                infos = reader.readMap("infos", MessageCollectionItemType.INT, MessageCollectionItemType.MSG, false);
 
                 if (!reader.isLastRead())
                     return false;
@@ -362,7 +374,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 6:
-                missed = reader.readCollection("missed", MessageCollectionItemType.INT);
+                keysPerCache = reader.readMap("keysPerCache", MessageCollectionItemType.INT, MessageCollectionItemType.LONG, false);
 
                 if (!reader.isLastRead())
                     return false;
@@ -370,7 +382,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 7:
-                topVer = reader.readMessage("topVer");
+                last = reader.readCollection("last", MessageCollectionItemType.INT);
 
                 if (!reader.isLastRead())
                     return false;
@@ -378,7 +390,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 8:
-                updateSeq = reader.readLong("updateSeq");
+                missed = reader.readCollection("missed", MessageCollectionItemType.INT);
 
                 if (!reader.isLastRead())
                     return false;
@@ -386,7 +398,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 9:
-                estimatedKeysCnt = reader.readLong("estimatedKeysCnt");
+                msgSize = reader.readInt("msgSize");
 
                 if (!reader.isLastRead())
                     return false;
@@ -394,12 +406,21 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
                 reader.incrementState();
 
             case 10:
-                msgSize = reader.readInt("msgSize");
+                topVer = reader.readMessage("topVer");
 
                 if (!reader.isLastRead())
                     return false;
 
                 reader.incrementState();
+
+            case 11:
+                updateSeq = reader.readLong("updateSeq");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
         }
 
         return reader.afterMessageRead(GridDhtPartitionSupplyMessage.class);
@@ -412,7 +433,7 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 11;
+        return 12;
     }
 
     /**
@@ -423,10 +444,43 @@ public class GridDhtPartitionSupplyMessage extends GridCacheMessage implements G
     }
 
     /**
-     * @param estimatedKeysCnt New estimated keys count.
+     * @param cnt Keys count to add.
      */
-    public void estimatedKeysCount(long estimatedKeysCnt) {
-        this.estimatedKeysCnt = estimatedKeysCnt;
+    public void addEstimatedKeysCount(long cnt) {
+        this.estimatedKeysCnt += cnt;
+    }
+
+    /**
+     * @return Estimated keys count for a given cache ID.
+     */
+    public long keysForCache(int cacheId) {
+        if (this.keysPerCache == null)
+            return -1;
+
+        Long cnt = this.keysPerCache.get(cacheId);
+
+        return cnt != null ? cnt : 0;
+    }
+
+    /**
+     * @param cacheId Cache ID.
+     * @param cnt Keys count.
+     */
+    public void addKeysForCache(int cacheId, long cnt) {
+        assert cacheId != 0 && cnt >= 0;
+
+        if (keysPerCache == null)
+            keysPerCache = new HashMap<>();
+
+        Long cnt0 = keysPerCache.get(cacheId);
+
+        if (cnt0 == null) {
+            keysPerCache.put(cacheId, cnt);
+
+            msgSize += 12;
+        }
+        else
+            keysPerCache.put(cacheId, cnt0 + cnt);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java
index 74bbcb0..441952d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsAbstractMessage.java
@@ -65,6 +65,11 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
     }
 
     /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
     @Override public int partition() {
         return GridIoMessage.STRIPE_DISABLED_PART;
     }
@@ -87,10 +92,10 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @return Parition update counters.
      */
-    public abstract Map<Integer, T2<Long, Long>> partitionUpdateCounters(int cacheId);
+    public abstract Map<Integer, T2<Long, Long>> partitionUpdateCounters(int grpId);
 
     /**
      * @return Last used version among all nodes.
@@ -114,6 +119,11 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
     }
 
     /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 5;
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
         writer.setBuffer(buf);
 
@@ -128,19 +138,19 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
         }
 
         switch (writer.state()) {
-            case 3:
+            case 2:
                 if (!writer.writeMessage("exchId", exchId))
                     return false;
 
                 writer.incrementState();
 
-            case 4:
+            case 3:
                 if (!writer.writeByte("flags", flags))
                     return false;
 
                 writer.incrementState();
 
-            case 5:
+            case 4:
                 if (!writer.writeMessage("lastVer", lastVer))
                     return false;
 
@@ -162,7 +172,7 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
             return false;
 
         switch (reader.state()) {
-            case 3:
+            case 2:
                 exchId = reader.readMessage("exchId");
 
                 if (!reader.isLastRead())
@@ -170,7 +180,7 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
 
                 reader.incrementState();
 
-            case 4:
+            case 3:
                 flags = reader.readByte("flags");
 
                 if (!reader.isLastRead())
@@ -178,7 +188,7 @@ public abstract class GridDhtPartitionsAbstractMessage extends GridCacheMessage
 
                 reader.incrementState();
 
-            case 5:
+            case 4:
                 lastVer = reader.readMessage("lastVer");
 
                 if (!reader.isLastRead())


[16/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
new file mode 100644
index 0000000..b85c41d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
@@ -0,0 +1,964 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cache.affinity.AffinityFunction;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.configuration.TopologyValidator;
+import org.apache.ignite.events.CacheRebalancingEvent;
+import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache;
+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.distributed.dht.GridDhtAffinityAssignmentRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtAffinityAssignmentResponse;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopology;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionTopologyImpl;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader;
+import org.apache.ignite.internal.processors.cache.query.continuous.CounterSkipContext;
+import org.apache.ignite.internal.processors.query.QueryUtils;
+import org.apache.ignite.internal.util.typedef.CI1;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.LT;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiInClosure;
+import org.apache.ignite.lang.IgniteFuture;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.cache.CacheMode.LOCAL;
+import static org.apache.ignite.cache.CacheRebalanceMode.NONE;
+import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_PART_UNLOADED;
+import static org.apache.ignite.internal.managers.communication.GridIoPolicy.AFFINITY_POOL;
+
+/**
+ *
+ */
+public class CacheGroupContext {
+    /**
+     * Unique group ID. Currently for shared group it is generated as group name hash,
+     * for non-shared as cache name hash (see {@link ClusterCachesInfo#checkCacheConflict}).
+     */
+    private final int grpId;
+
+    /** Node ID cache group was received from. */
+    private final UUID rcvdFrom;
+
+    /** Flag indicating that this cache group is in a recovery mode due to partitions loss. */
+    private boolean needsRecovery;
+
+    /** */
+    private final AffinityTopologyVersion locStartVer;
+
+    /** */
+    private final CacheConfiguration<?, ?> ccfg;
+
+    /** */
+    private final GridCacheSharedContext ctx;
+
+    /** */
+    private final boolean affNode;
+
+    /** */
+    private final CacheType cacheType;
+
+    /** */
+    private final byte ioPlc;
+
+    /** */
+    private final boolean depEnabled;
+
+    /** */
+    private final boolean storeCacheId;
+
+    /** */
+    private volatile List<GridCacheContext> caches;
+
+    /** */
+    private volatile List<GridCacheContext> contQryCaches;
+
+    /** */
+    private final IgniteLogger log;
+
+    /** */
+    private GridAffinityAssignmentCache aff;
+
+    /** */
+    private GridDhtPartitionTopologyImpl top;
+
+    /** */
+    private IgniteCacheOffheapManager offheapMgr;
+
+    /** */
+    private GridCachePreloader preldr;
+
+    /** */
+    private final MemoryPolicy memPlc;
+
+    /** */
+    private final CacheObjectContext cacheObjCtx;
+
+    /** */
+    private final FreeList freeList;
+
+    /** */
+    private final ReuseList reuseList;
+
+    /** */
+    private boolean drEnabled;
+
+    /** */
+    private boolean qryEnabled;
+
+    /**
+     * @param grpId Group ID.
+     * @param ctx Context.
+     * @param rcvdFrom Node ID cache group was received from.
+     * @param cacheType Cache type.
+     * @param ccfg Cache configuration.
+     * @param affNode Affinity node flag.
+     * @param memPlc Memory policy.
+     * @param cacheObjCtx Cache object context.
+     * @param freeList Free list.
+     * @param reuseList Reuse list.
+     * @param locStartVer Topology version when group was started on local node.
+     */
+    CacheGroupContext(
+        GridCacheSharedContext ctx,
+        int grpId,
+        UUID rcvdFrom,
+        CacheType cacheType,
+        CacheConfiguration ccfg,
+        boolean affNode,
+        MemoryPolicy memPlc,
+        CacheObjectContext cacheObjCtx,
+        FreeList freeList,
+        ReuseList reuseList,
+        AffinityTopologyVersion locStartVer) {
+        assert ccfg != null;
+        assert memPlc != null || !affNode;
+        assert grpId != 0 : "Invalid group ID [cache=" + ccfg.getName() + ", grpName=" + ccfg.getGroupName() + ']';
+
+        this.grpId = grpId;
+        this.rcvdFrom = rcvdFrom;
+        this.ctx = ctx;
+        this.ccfg = ccfg;
+        this.affNode = affNode;
+        this.memPlc = memPlc;
+        this.cacheObjCtx = cacheObjCtx;
+        this.freeList = freeList;
+        this.reuseList = reuseList;
+        this.locStartVer = locStartVer;
+        this.cacheType = cacheType;
+
+        ioPlc = cacheType.ioPolicy();
+
+        depEnabled = ctx.kernalContext().deploy().enabled() && !ctx.kernalContext().cacheObjects().isBinaryEnabled(ccfg);
+
+        storeCacheId = affNode && memPlc.config().getPageEvictionMode() != DataPageEvictionMode.DISABLED;
+
+        log = ctx.kernalContext().log(getClass());
+
+        caches = new ArrayList<>();
+    }
+
+    /**
+     * @return {@code True} if this is cache group for one of system caches.
+     */
+    public boolean systemCache() {
+        return !sharedGroup() && CU.isSystemCache(ccfg.getName());
+    }
+
+    /**
+     * @return Node ID initiated cache group start.
+     */
+    public UUID receivedFrom() {
+        return rcvdFrom;
+    }
+
+    /**
+     * @return {@code True} if cacheId should be stored in data pages.
+     */
+    public boolean storeCacheIdInDataPage() {
+        return storeCacheId;
+    }
+
+    /**
+     * @return {@code True} if deployment is enabled.
+     */
+    public boolean deploymentEnabled() {
+        return depEnabled;
+    }
+
+    /**
+     * @return Preloader.
+     */
+    public GridCachePreloader preloader() {
+        return preldr;
+    }
+
+    /**
+     * @return IO policy for the given cache group.
+     */
+    public byte ioPolicy() {
+        return ioPlc;
+    }
+
+    /**
+     * @param cctx Cache context.
+     * @throws IgniteCheckedException If failed.
+     */
+    void onCacheStarted(GridCacheContext cctx) throws IgniteCheckedException {
+        addCacheContext(cctx);
+
+        offheapMgr.onCacheStarted(cctx);
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @return {@code True} if group contains cache with given name.
+     */
+    public boolean hasCache(String cacheName) {
+        List<GridCacheContext> caches = this.caches;
+
+        for (int i = 0; i < caches.size(); i++) {
+            if (caches.get(i).name().equals(cacheName))
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param cctx Cache context.
+     */
+    private void addCacheContext(GridCacheContext cctx) {
+        assert cacheType.userCache() == cctx.userCache() : cctx.name();
+        assert grpId == cctx.groupId() : cctx.name();
+
+        ArrayList<GridCacheContext> caches = new ArrayList<>(this.caches);
+
+        assert sharedGroup() || caches.isEmpty();
+
+        boolean add = caches.add(cctx);
+
+        assert add : cctx.name();
+
+        if (!qryEnabled && QueryUtils.isEnabled(cctx.config()))
+            qryEnabled = true;
+
+        if (!drEnabled && cctx.isDrEnabled())
+            drEnabled = true;
+
+        this.caches = caches;
+   }
+
+    /**
+     * @param cctx Cache context.
+     */
+    private void removeCacheContext(GridCacheContext cctx) {
+        ArrayList<GridCacheContext> caches = new ArrayList<>(this.caches);
+
+        // It is possible cache was not added in case of errors on cache start.
+        for (Iterator<GridCacheContext> it = caches.iterator(); it.hasNext();) {
+            GridCacheContext next = it.next();
+
+            if (next == cctx) {
+                assert sharedGroup() || caches.size() == 1 : caches.size();
+
+                it.remove();
+
+                break;
+            }
+        }
+
+        if (QueryUtils.isEnabled(cctx.config())) {
+            boolean qryEnabled = false;
+
+            for (int i = 0; i < caches.size(); i++) {
+                if (QueryUtils.isEnabled(caches.get(i).config())) {
+                    qryEnabled = true;
+
+                    break;
+                }
+            }
+
+            this.qryEnabled = qryEnabled;
+        }
+
+        if (cctx.isDrEnabled()) {
+            boolean drEnabled = false;
+
+            for (int i = 0; i < caches.size(); i++) {
+                if (caches.get(i).isDrEnabled()) {
+                    drEnabled = true;
+
+                    break;
+                }
+            }
+
+            this.drEnabled = drEnabled;
+        }
+
+        this.caches = caches;
+    }
+
+    /**
+     * @return Cache context if group contains single cache.
+     */
+    public GridCacheContext singleCacheContext() {
+        List<GridCacheContext> caches = this.caches;
+
+        assert !sharedGroup() && caches.size() == 1;
+
+        return caches.get(0);
+    }
+
+    /**
+     *
+     */
+    public void unwindUndeploys() {
+        List<GridCacheContext> caches = this.caches;
+
+        for (int i = 0; i < caches.size(); i++) {
+            GridCacheContext cctx = caches.get(i);
+
+            cctx.deploy().unwind(cctx);
+        }
+    }
+
+    /**
+     * @param type Event type to check.
+     * @return {@code True} if given event type should be recorded.
+     */
+    public boolean eventRecordable(int type) {
+        return cacheType.userCache() && ctx.gridEvents().isRecordable(type);
+    }
+
+    /**
+     * Adds rebalancing event.
+     *
+     * @param part Partition.
+     * @param type Event type.
+     * @param discoNode Discovery node.
+     * @param discoType Discovery event type.
+     * @param discoTs Discovery event timestamp.
+     */
+    public void addRebalanceEvent(int part, int type, ClusterNode discoNode, int discoType, long discoTs) {
+        assert discoNode != null;
+        assert type > 0;
+        assert discoType > 0;
+        assert discoTs > 0;
+
+        if (!eventRecordable(type))
+            LT.warn(log, "Added event without checking if event is recordable: " + U.gridEventName(type));
+
+        List<GridCacheContext> caches = this.caches;
+
+        for (int i = 0; i < caches.size(); i++) {
+            GridCacheContext cctx = caches.get(i);
+
+            if (cctx.recordEvent(type)) {
+                cctx.gridEvents().record(new CacheRebalancingEvent(cctx.name(),
+                    cctx.localNode(),
+                    "Cache rebalancing event.",
+                    type,
+                    part,
+                    discoNode,
+                    discoType,
+                    discoTs));
+            }
+        }
+    }
+    /**
+     * Adds partition unload event.
+     *
+     * @param part Partition.
+     */
+    public void addUnloadEvent(int part) {
+        if (!eventRecordable(EVT_CACHE_REBALANCE_PART_UNLOADED))
+            LT.warn(log, "Added event without checking if event is recordable: " +
+                U.gridEventName(EVT_CACHE_REBALANCE_PART_UNLOADED));
+
+        List<GridCacheContext> caches = this.caches;
+
+        for (int i = 0; i < caches.size(); i++) {
+            GridCacheContext cctx = caches.get(i);
+
+            cctx.gridEvents().record(new CacheRebalancingEvent(cctx.name(),
+                cctx.localNode(),
+                "Cache unloading event.",
+                EVT_CACHE_REBALANCE_PART_UNLOADED,
+                part,
+                null,
+                0,
+                0));
+        }
+    }
+
+    /**
+     * @param part Partition.
+     * @param key Key.
+     * @param evtNodeId Event node ID.
+     * @param type Event type.
+     * @param newVal New value.
+     * @param hasNewVal Has new value flag.
+     * @param oldVal Old values.
+     * @param hasOldVal Has old value flag.
+     * @param keepBinary Keep binary flag.
+     */
+    public void addCacheEvent(
+        int part,
+        KeyCacheObject key,
+        UUID evtNodeId,
+        int type,
+        @Nullable CacheObject newVal,
+        boolean hasNewVal,
+        @Nullable CacheObject oldVal,
+        boolean hasOldVal,
+        boolean keepBinary
+    ) {
+        List<GridCacheContext> caches = this.caches;
+
+        for (int i = 0; i < caches.size(); i++) {
+            GridCacheContext cctx = caches.get(i);
+
+            cctx.events().addEvent(part,
+                key,
+                evtNodeId,
+                (IgniteUuid)null,
+                null,
+                type,
+                newVal,
+                hasNewVal,
+                oldVal,
+                hasOldVal,
+                null,
+                null,
+                null,
+                keepBinary);
+        }
+    }
+
+    /**
+     * @return {@code True} if contains cache with query indexing enabled.
+     */
+    public boolean queriesEnabled() {
+        return qryEnabled;
+    }
+
+    /**
+     * @return {@code True} if fast eviction is allowed.
+     */
+    public boolean allowFastEviction() {
+        return ctx.database().persistenceEnabled() && !queriesEnabled();
+    }
+
+    /**
+     * @return {@code True} in case replication is enabled.
+     */
+    public boolean isDrEnabled() {
+        return drEnabled;
+    }
+
+    /**
+     * @return Free List.
+     */
+    public FreeList freeList() {
+        return freeList;
+    }
+
+    /**
+     * @return Reuse List.
+     */
+    public ReuseList reuseList() {
+        return reuseList;
+    }
+
+    /**
+     * @return Cache object context.
+     */
+    public CacheObjectContext cacheObjectContext() {
+        return cacheObjCtx;
+    }
+
+    /**
+     * @return Cache shared context.
+     */
+    public GridCacheSharedContext shared() {
+        return ctx;
+    }
+
+    /**
+     * @return Memory policy.
+     */
+    public MemoryPolicy memoryPolicy() {
+        return memPlc;
+    }
+
+    /**
+     * @return {@code True} if local node is affinity node.
+     */
+    public boolean affinityNode() {
+        return affNode;
+    }
+
+    /**
+     * @return Topology.
+     */
+    public GridDhtPartitionTopology topology() {
+        if (top == null)
+            throw new IllegalStateException("Topology is not initialized: " + name());
+
+        return top;
+    }
+
+    /**
+     * @return Offheap manager.
+     */
+    public IgniteCacheOffheapManager offheap() {
+        return offheapMgr;
+    }
+
+    /**
+     * @return Current cache state. Must only be modified during exchange.
+     */
+    public boolean needsRecovery() {
+        return needsRecovery;
+    }
+
+    /**
+     * @param needsRecovery Needs recovery flag.
+     */
+    public void needsRecovery(boolean needsRecovery) {
+        this.needsRecovery = needsRecovery;
+    }
+
+    /**
+     * @return Topology version when group was started on local node.
+     */
+    public AffinityTopologyVersion localStartVersion() {
+        return locStartVer;
+    }
+
+    /**
+     * @return {@code True} if cache is local.
+     */
+    public boolean isLocal() {
+        return ccfg.getCacheMode() == LOCAL;
+    }
+
+    /**
+     * @return Cache configuration.
+     */
+    public CacheConfiguration config() {
+        return ccfg;
+    }
+
+    /**
+     * @return Cache node filter.
+     */
+    public IgnitePredicate<ClusterNode> nodeFilter() {
+        return ccfg.getNodeFilter();
+    }
+
+    /**
+     * @return Configured user objects which should be initialized/stopped on group start/stop.
+     */
+    Collection<?> configuredUserObjects() {
+        return Arrays.asList(ccfg.getAffinity(), ccfg.getNodeFilter(), ccfg.getTopologyValidator());
+    }
+
+    /**
+     * @return Configured topology validator.
+     */
+    @Nullable public TopologyValidator topologyValidator() {
+        return ccfg.getTopologyValidator();
+    }
+
+    /**
+     * @return Configured affinity function.
+     */
+    public AffinityFunction affinityFunction() {
+        return ccfg.getAffinity();
+    }
+
+    /**
+     * @return Affinity.
+     */
+    public GridAffinityAssignmentCache affinity() {
+        return aff;
+    }
+
+    /**
+     * @return Group name or {@code null} if group name was not specified for cache.
+     */
+    @Nullable public String name() {
+        return ccfg.getGroupName();
+    }
+
+    /**
+     * @return Group name if it is specified, otherwise cache name.
+     */
+    public String cacheOrGroupName() {
+        return ccfg.getGroupName() != null ? ccfg.getGroupName() : ccfg.getName();
+    }
+
+    /**
+     * @return Group ID.
+     */
+    public int groupId() {
+        return grpId;
+    }
+
+    /**
+     * @return {@code True} if group can contain multiple caches.
+     */
+    public boolean sharedGroup() {
+        return ccfg.getGroupName() != null;
+    }
+
+    /**
+     *
+     */
+    public void onKernalStop() {
+        aff.cancelFutures(new IgniteCheckedException("Failed to wait for topology update, node is stopping."));
+
+        preldr.onKernalStop();
+
+        offheapMgr.onKernalStop();
+    }
+
+    /**
+     * @param cctx Cache context.
+     * @param destroy Destroy flag.
+     */
+    void stopCache(GridCacheContext cctx, boolean destroy) {
+        if (top != null)
+            top.onCacheStopped(cctx.cacheId());
+
+        offheapMgr.stopCache(cctx.cacheId(), destroy);
+
+        removeCacheContext(cctx);
+    }
+
+    /**
+     *
+     */
+    void stopGroup() {
+        IgniteCheckedException err =
+            new IgniteCheckedException("Failed to wait for topology update, cache (or node) is stopping.");
+
+        aff.cancelFutures(err);
+
+        offheapMgr.stop();
+
+        ctx.io().removeCacheGroupHandlers(grpId);
+    }
+
+    /**
+     * @return IDs of caches in this group.
+     */
+    public Set<Integer> cacheIds() {
+        List<GridCacheContext> caches = this.caches;
+
+        Set<Integer> ids = U.newHashSet(caches.size());
+
+        for (int i = 0; i < caches.size(); i++)
+            ids.add(caches.get(i).cacheId());
+
+        return ids;
+    }
+
+    /**
+     * @return Caches in this group.
+     */
+    public List<GridCacheContext> caches() {
+        return this.caches;
+    }
+
+    /**
+     * @return {@code True} if group contains caches.
+     */
+    boolean hasCaches() {
+        List<GridCacheContext> caches = this.caches;
+
+        return !caches.isEmpty();
+    }
+
+    /**
+     * @param part Partition ID.
+     */
+    public void onPartitionEvicted(int part) {
+        List<GridCacheContext> caches = this.caches;
+
+        for (int i = 0; i < caches.size(); i++) {
+            GridCacheContext cctx = caches.get(i);
+
+            if (cctx.isDrEnabled())
+                cctx.dr().partitionEvicted(part);
+
+            cctx.continuousQueries().onPartitionEvicted(part);
+
+            cctx.dataStructures().onPartitionEvicted(part);
+        }
+    }
+
+    /**
+     * @param cctx Cache context.
+     */
+    public void addCacheWithContinuousQuery(GridCacheContext cctx) {
+        assert sharedGroup() : cacheOrGroupName();
+        assert cctx.group() == this : cctx.name();
+        assert !cctx.isLocal() : cctx.name();
+
+        synchronized (this) {
+            List<GridCacheContext> contQryCaches = this.contQryCaches;
+
+            if (contQryCaches == null)
+                contQryCaches = new ArrayList<>();
+
+            contQryCaches.add(cctx);
+
+            this.contQryCaches = contQryCaches;
+        }
+    }
+
+    /**
+     * @param cctx Cache context.
+     */
+    public void removeCacheWithContinuousQuery(GridCacheContext cctx) {
+        assert sharedGroup() : cacheOrGroupName();
+        assert cctx.group() == this : cctx.name();
+        assert !cctx.isLocal() : cctx.name();
+
+        synchronized (this) {
+            List<GridCacheContext> contQryCaches = this.contQryCaches;
+
+            if (contQryCaches == null)
+                return;
+
+            contQryCaches.remove(cctx);
+
+            if (contQryCaches.isEmpty())
+                contQryCaches = null;
+
+            this.contQryCaches = contQryCaches;
+        }
+    }
+
+    /**
+     * @param cacheId ID of cache initiated counter update.
+     * @param part Partition number.
+     * @param cntr Counter.
+     * @param topVer Topology version for current operation.
+     */
+    public void onPartitionCounterUpdate(int cacheId,
+        int part,
+        long cntr,
+        AffinityTopologyVersion topVer,
+        boolean primary) {
+        assert sharedGroup();
+
+        if (isLocal())
+            return;
+
+        List<GridCacheContext> contQryCaches = this.contQryCaches;
+
+        if (contQryCaches == null)
+            return;
+
+        CounterSkipContext skipCtx = null;
+
+        for (int i = 0; i < contQryCaches.size(); i++) {
+            GridCacheContext cctx = contQryCaches.get(i);
+
+            if (cacheId != cctx.cacheId())
+                skipCtx = cctx.continuousQueries().skipUpdateCounter(skipCtx, part, cntr, topVer, primary);
+        }
+
+        final List<Runnable> procC = skipCtx != null ? skipCtx.processClosures() : null;
+
+        if (procC != null) {
+            ctx.kernalContext().closure().runLocalSafe(new Runnable() {
+                @Override public void run() {
+                    for (Runnable c : procC)
+                        c.run();
+                }
+            });
+        }
+    }
+
+    /**
+     * @throws IgniteCheckedException If failed.
+     */
+    public void start() throws IgniteCheckedException {
+        aff = new GridAffinityAssignmentCache(ctx.kernalContext(),
+            cacheOrGroupName(),
+            grpId,
+            ccfg.getAffinity(),
+            ccfg.getNodeFilter(),
+            ccfg.getBackups(),
+            ccfg.getCacheMode() == LOCAL);
+
+        if (ccfg.getCacheMode() != LOCAL) {
+            top = new GridDhtPartitionTopologyImpl(ctx, this);
+
+            if (!ctx.kernalContext().clientNode()) {
+                ctx.io().addCacheGroupHandler(groupId(), GridDhtAffinityAssignmentRequest.class,
+                    new IgniteBiInClosure<UUID, GridDhtAffinityAssignmentRequest>() {
+                        @Override public void apply(UUID nodeId, GridDhtAffinityAssignmentRequest msg) {
+                            processAffinityAssignmentRequest(nodeId, msg);
+                        }
+                    });
+            }
+
+            preldr = new GridDhtPreloader(this);
+
+            preldr.start();
+        }
+        else
+            preldr = new GridCachePreloaderAdapter(this);
+
+        if (ctx.kernalContext().config().getPersistentStoreConfiguration() != null) {
+            ClassLoader clsLdr = U.gridClassLoader();
+
+            try {
+                offheapMgr = (IgniteCacheOffheapManager) clsLdr
+                    .loadClass("org.apache.ignite.internal.processors.cache.database.GridCacheOffheapManager")
+                    .getConstructor()
+                    .newInstance();
+            }
+            catch (Exception e) {
+                throw new IgniteCheckedException("Failed to initialize offheap manager", e);
+            }
+        }
+        else
+            offheapMgr = new IgniteCacheOffheapManagerImpl();
+
+        offheapMgr.start(ctx, this);
+
+        ctx.affinity().onCacheGroupCreated(this);
+    }
+
+    /**
+     * @param nodeId Node ID.
+     * @param req Request.
+     */
+    private void processAffinityAssignmentRequest(final UUID nodeId,
+        final GridDhtAffinityAssignmentRequest req) {
+        if (log.isDebugEnabled())
+            log.debug("Processing affinity assignment request [node=" + nodeId + ", req=" + req + ']');
+
+        IgniteInternalFuture<AffinityTopologyVersion> fut = aff.readyFuture(req.topologyVersion());
+
+        if (fut != null) {
+            fut.listen(new CI1<IgniteInternalFuture<AffinityTopologyVersion>>() {
+                @Override public void apply(IgniteInternalFuture<AffinityTopologyVersion> fut) {
+                    processAffinityAssignmentRequest0(nodeId, req);
+                }
+            });
+        }
+        else
+            processAffinityAssignmentRequest0(nodeId, req);
+    }
+
+    /**
+     * @param nodeId Node ID.
+     * @param req Request.
+     */
+    private void processAffinityAssignmentRequest0(UUID nodeId, final GridDhtAffinityAssignmentRequest req) {
+        AffinityTopologyVersion topVer = req.topologyVersion();
+
+        if (log.isDebugEnabled())
+            log.debug("Affinity is ready for topology version, will send response [topVer=" + topVer +
+                ", node=" + nodeId + ']');
+
+        AffinityAssignment assignment = aff.cachedAffinity(topVer);
+
+        GridDhtAffinityAssignmentResponse res = new GridDhtAffinityAssignmentResponse(
+            req.futureId(),
+            grpId,
+            topVer,
+            assignment.assignment());
+
+        if (aff.centralizedAffinityFunction()) {
+            assert assignment.idealAssignment() != null;
+
+            res.idealAffinityAssignment(assignment.idealAssignment());
+        }
+
+        try {
+            ctx.io().send(nodeId, res, AFFINITY_POOL);
+        }
+        catch (IgniteCheckedException e) {
+            U.error(log, "Failed to send affinity assignment response to remote node [node=" + nodeId + ']', e);
+        }
+    }
+
+    /**
+     * @param reconnectFut Reconnect future.
+     */
+    public void onDisconnected(IgniteFuture reconnectFut) {
+        IgniteCheckedException err = new IgniteClientDisconnectedCheckedException(reconnectFut,
+            "Failed to wait for topology update, client disconnected.");
+
+        if (aff != null)
+            aff.cancelFutures(err);
+    }
+
+    /**
+     * @return {@code True} if rebalance is enabled.
+     */
+    public boolean rebalanceEnabled() {
+        return ccfg.getRebalanceMode() != NONE;
+    }
+
+    /**
+     *
+     */
+    public void onReconnected() {
+        aff.onReconnected();
+
+        if (top != null)
+            top.onReconnected();
+
+        preldr.onReconnected();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "CacheGroupContext [grp=" + cacheOrGroupName() + ']';
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupData.java
new file mode 100644
index 0000000..a290caf
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupData.java
@@ -0,0 +1,147 @@
+/*
+ * 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.Map;
+import java.util.UUID;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class CacheGroupData implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    private final int grpId;
+
+    /** */
+    private final String grpName;
+
+    /** */
+    private final AffinityTopologyVersion startTopVer;
+
+    /** */
+    private final UUID rcvdFrom;
+
+    /** */
+    private final IgniteUuid deploymentId;
+
+    /** */
+    private final CacheConfiguration<?, ?> cacheCfg;
+
+    /** */
+    @GridToStringInclude
+    private final Map<String, Integer> caches;
+
+    /** */
+    private long flags;
+
+    /**
+     * @param cacheCfg Cache configuration.
+     * @param grpName Group name.
+     * @param grpId  Group ID.
+     * @param rcvdFrom Node ID cache group received from.
+     * @param startTopVer Start version for dynamically started group.
+     * @param deploymentId Deployment ID.
+     * @param caches Cache group caches.
+     */
+    CacheGroupData(
+        CacheConfiguration cacheCfg,
+        @Nullable String grpName,
+        int grpId,
+        UUID rcvdFrom,
+        @Nullable AffinityTopologyVersion startTopVer,
+        IgniteUuid deploymentId,
+        Map<String, Integer> caches,
+        long flags) {
+        assert cacheCfg != null;
+        assert grpId != 0;
+        assert deploymentId != null;
+
+        this.cacheCfg = cacheCfg;
+        this.grpName = grpName;
+        this.grpId = grpId;
+        this.rcvdFrom = rcvdFrom;
+        this.startTopVer = startTopVer;
+        this.deploymentId = deploymentId;
+        this.caches = caches;
+        this.flags = flags;
+    }
+
+    /**
+     * @return Start version for dynamically started group.
+     */
+    @Nullable public AffinityTopologyVersion startTopologyVersion() {
+        return startTopVer;
+    }
+
+    /**
+     * @return Node ID group was received from.
+     */
+    public UUID receivedFrom() {
+        return rcvdFrom;
+    }
+
+    /**
+     * @return Group name.
+     */
+    @Nullable public String groupName() {
+        return grpName;
+    }
+
+    /**
+     * @return Group ID.
+     */
+    public int groupId() {
+        return grpId;
+    }
+
+    /**
+     * @return Deployment ID.
+     */
+    public IgniteUuid deploymentId() {
+        return deploymentId;
+    }
+
+    /**
+     * @return Configuration.
+     */
+    public CacheConfiguration<?, ?> config() {
+        return cacheCfg;
+    }
+
+    /**
+     * @return Group caches.
+     */
+    Map<String, Integer> caches() {
+        return caches;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(CacheGroupData.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java
new file mode 100644
index 0000000..c4976e5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupDescriptor.java
@@ -0,0 +1,210 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.lang.IgniteUuid;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class CacheGroupDescriptor {
+    /** */
+    private final int grpId;
+
+    /** */
+    private final String grpName;
+
+    /** */
+    private final AffinityTopologyVersion startTopVer;
+
+    /** */
+    private final UUID rcvdFrom;
+
+    /** */
+    private final IgniteUuid deploymentId;
+
+    /** */
+    @GridToStringExclude
+    private final CacheConfiguration<?, ?> cacheCfg;
+
+    /** */
+    @GridToStringInclude
+    private Map<String, Integer> caches;
+
+    /** */
+    private AffinityTopologyVersion rcvdFromVer;
+
+    /**
+     * @param cacheCfg Cache configuration.
+     * @param grpName Group name.
+     * @param grpId  Group ID.
+     * @param rcvdFrom Node ID cache group received from.
+     * @param startTopVer Start version for dynamically started group.
+     * @param deploymentId Deployment ID.
+     * @param caches Cache group caches.
+     */
+    CacheGroupDescriptor(
+        CacheConfiguration cacheCfg,
+        @Nullable String grpName,
+        int grpId,
+        UUID rcvdFrom,
+        @Nullable AffinityTopologyVersion startTopVer,
+        IgniteUuid deploymentId,
+        Map<String, Integer> caches) {
+        assert cacheCfg != null;
+        assert grpId != 0;
+
+        this.grpName = grpName;
+        this.grpId = grpId;
+        this.rcvdFrom = rcvdFrom;
+        this.startTopVer = startTopVer;
+        this.deploymentId = deploymentId;
+        this.cacheCfg = cacheCfg;
+        this.caches = caches;
+    }
+
+    /**
+     * @return Node ID group was received from.
+     */
+    public UUID receivedFrom() {
+        return rcvdFrom;
+    }
+
+    /**
+     * @return Deployment ID.
+     */
+    public IgniteUuid deploymentId() {
+        return deploymentId;
+    }
+
+    /**
+     * @param cacheName Cache name
+     * @param cacheId Cache ID.
+     */
+    void onCacheAdded(String cacheName, int cacheId) {
+        assert cacheName != null;
+        assert cacheId != 0 : cacheName;
+
+        Map<String, Integer> caches = new HashMap<>(this.caches);
+
+        caches.put(cacheName, cacheId);
+
+        this.caches = caches;
+    }
+
+    /**
+     * @param cacheName Cache name
+     * @param cacheId Cache ID.
+     */
+    void onCacheStopped(String cacheName, int cacheId) {
+        assert cacheName != null;
+        assert cacheId != 0;
+
+        Map<String, Integer> caches = new HashMap<>(this.caches);
+
+        Integer rmvd = caches.remove(cacheName);
+
+        assert rmvd != null && rmvd == cacheId : cacheName;
+
+        this.caches = caches;
+    }
+
+    /**
+     * @return {@code True} if group contains cache.
+     */
+    boolean hasCaches() {
+        return caches != null && !caches.isEmpty();
+    }
+
+    /**
+     * @return {@code True} if group can contain multiple caches.
+     */
+    public boolean sharedGroup() {
+        return grpName != null;
+    }
+
+    /**
+     * @return Group name if it is specified, otherwise cache name.
+     */
+    public String cacheOrGroupName() {
+        return grpName != null ? grpName : cacheCfg.getName();
+    }
+
+    /**
+     * @return Group name or {@code null} if group name was not specified for cache.
+     */
+    @Nullable public String groupName() {
+        return grpName;
+    }
+
+    /**
+     * @return Group ID.
+     */
+    public int groupId() {
+        return grpId;
+    }
+
+    /**
+     * @return Configuration.
+     */
+    public CacheConfiguration<?, ?> config() {
+        return cacheCfg;
+    }
+
+    /**
+     * @return Group caches.
+     */
+    public Map<String, Integer> caches() {
+        return caches;
+    }
+
+    /**
+     * @return Topology version when node provided cache configuration was started.
+     */
+    @Nullable AffinityTopologyVersion receivedFromStartVersion() {
+        return rcvdFromVer;
+    }
+
+    /**
+     * @param rcvdFromVer Topology version when node provided cache configuration was started.
+     */
+    void receivedFromStartVersion(AffinityTopologyVersion rcvdFromVer) {
+        this.rcvdFromVer = rcvdFromVer;
+    }
+
+    /**
+     * @return Start version for dynamically started group.
+     */
+    @Nullable public AffinityTopologyVersion startTopologyVersion() {
+        return startTopVer;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(CacheGroupDescriptor.class, this, "cacheName", cacheCfg.getName());
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheJoinNodeDiscoveryData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheJoinNodeDiscoveryData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheJoinNodeDiscoveryData.java
index afc01c9..58c9d82 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheJoinNodeDiscoveryData.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheJoinNodeDiscoveryData.java
@@ -111,7 +111,7 @@ class CacheJoinNodeDiscoveryData implements Serializable {
         private final boolean sql;
 
         /** Flags added for future usage. */
-        private final byte flags;
+        private final long flags;
 
         /**
          * @param ccfg Cache configuration.
@@ -119,7 +119,7 @@ class CacheJoinNodeDiscoveryData implements Serializable {
          * @param sql SQL flag - {@code true} if cache was created with {@code CREATE TABLE}.
          * @param flags Flags (for future usage).
          */
-        CacheInfo(CacheConfiguration ccfg, CacheType cacheType, boolean sql, byte flags) {
+        CacheInfo(CacheConfiguration ccfg, CacheType cacheType, boolean sql, long flags) {
             this.ccfg = ccfg;
             this.cacheType = cacheType;
             this.sql = sql;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
index 2ee8cc4..73973e4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
@@ -218,7 +218,10 @@ public class CacheMetricsImpl implements CacheMetrics {
     /** {@inheritDoc} */
     @Override public long getOffHeapPrimaryEntriesCount() {
         try {
-            return cctx.offheap().entriesCount(true, false, cctx.affinity().affinityTopologyVersion());
+            return cctx.offheap().cacheEntriesCount(cctx.cacheId(),
+                true,
+                false,
+                cctx.affinity().affinityTopologyVersion());
         }
         catch (IgniteCheckedException ignored) {
             return 0;
@@ -228,7 +231,10 @@ public class CacheMetricsImpl implements CacheMetrics {
     /** {@inheritDoc} */
     @Override public long getOffHeapBackupEntriesCount() {
         try {
-            return cctx.offheap().entriesCount(false, true, cctx.affinity().affinityTopologyVersion());
+            return cctx.offheap().cacheEntriesCount(cctx.cacheId(),
+                false,
+                true,
+                cctx.affinity().affinityTopologyVersion());
         }
         catch (IgniteCheckedException ignored) {
             return 0;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheNodeCommonDiscoveryData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheNodeCommonDiscoveryData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheNodeCommonDiscoveryData.java
index 84a33dc..4c70cb9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheNodeCommonDiscoveryData.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheNodeCommonDiscoveryData.java
@@ -39,22 +39,41 @@ class CacheNodeCommonDiscoveryData implements Serializable {
     private final Map<String, CacheData> templates;
 
     /** */
+    @GridToStringInclude
+    private final Map<Integer, CacheGroupData> cacheGrps;
+
+    /** */
     private final Map<String, Map<UUID, Boolean>> clientNodesMap;
 
     /**
      * @param caches Started caches.
      * @param templates Configured templates.
+     * @param cacheGrps Started cache groups.
      * @param clientNodesMap Information about cache client nodes.
      */
     CacheNodeCommonDiscoveryData(Map<String, CacheData> caches,
         Map<String, CacheData> templates,
+        Map<Integer, CacheGroupData> cacheGrps,
         Map<String, Map<UUID, Boolean>> clientNodesMap) {
+        assert caches != null;
+        assert templates != null;
+        assert cacheGrps != null;
+        assert clientNodesMap != null;
+
         this.caches = caches;
         this.templates = templates;
+        this.cacheGrps = cacheGrps;
         this.clientNodesMap = clientNodesMap;
     }
 
     /**
+     * @return Started cache groups.
+     */
+    Map<Integer, CacheGroupData> cacheGroups() {
+        return cacheGrps;
+    }
+
+    /**
      * @return Started caches.
      */
     Map<String, CacheData> caches() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
index b4047a8..d737c8b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheOffheapEvictionManager.java
@@ -41,15 +41,10 @@ public class CacheOffheapEvictionManager extends GridCacheManagerAdapter impleme
             return;
 
         try {
-            if (e.markObsoleteIfEmpty(null) || e.obsolete()) {
-                e.context().cache().removeEntry(e);
+            boolean evicted = e.evictInternal(GridCacheVersionManager.EVICT_VER, null, false)
+                || e.markObsoleteIfEmpty(null);
 
-                return;
-            }
-
-            boolean evicted = cctx.userCache() && e.evictInternal(GridCacheVersionManager.EVICT_VER, null, false);
-
-            if (evicted)
+            if (evicted && !e.isDht()) // GridDhtCacheEntry removes entry when obsoleted.
                 cctx.cache().removeEntry(e);
         }
         catch (IgniteCheckedException ex) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
index 7ff5622..08c08c7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
@@ -43,8 +43,11 @@ import org.apache.ignite.internal.processors.query.QuerySchema;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.T2;
 import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.spi.discovery.DiscoveryDataBag;
+import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cache.CacheMode.LOCAL;
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
@@ -61,6 +64,9 @@ class ClusterCachesInfo {
     /** Dynamic caches. */
     private final ConcurrentMap<String, DynamicCacheDescriptor> registeredCaches = new ConcurrentHashMap<>();
 
+    /** */
+    private final ConcurrentMap<Integer, CacheGroupDescriptor> registeredCacheGrps = new ConcurrentHashMap<>();
+
     /** Cache templates. */
     private final ConcurrentMap<String, DynamicCacheDescriptor> registeredTemplates = new ConcurrentHashMap<>();
 
@@ -68,13 +74,13 @@ class ClusterCachesInfo {
     private final IgniteLogger log;
 
     /** */
-    private Map<String, DynamicCacheDescriptor> cachesOnDisconnect;
+    private CachesOnDisconnect cachesOnDisconnect;
 
     /** */
     private CacheJoinNodeDiscoveryData joinDiscoData;
 
     /** */
-    private CacheNodeCommonDiscoveryData gridData;
+    private GridData gridData;
 
     /** */
     private List<T2<DynamicCacheDescriptor, NearCacheConfiguration>> locJoinStartCaches;
@@ -93,11 +99,40 @@ class ClusterCachesInfo {
 
     /**
      * @param joinDiscoData Information about configured caches and templates.
+     * @throws IgniteCheckedException If configuration validation failed.
      */
-    void onStart(CacheJoinNodeDiscoveryData joinDiscoData) {
+    void onStart(CacheJoinNodeDiscoveryData joinDiscoData) throws IgniteCheckedException {
         this.joinDiscoData = joinDiscoData;
 
-        processJoiningNode(joinDiscoData, ctx.localNodeId());
+        Map<String, CacheConfiguration> grpCfgs = new HashMap<>();
+
+        for (CacheJoinNodeDiscoveryData.CacheInfo info : joinDiscoData.caches().values()) {
+            if (info.config().getGroupName() == null)
+                continue;
+
+            CacheConfiguration ccfg = grpCfgs.get(info.config().getGroupName());
+
+            if (ccfg == null)
+                grpCfgs.put(info.config().getGroupName(), info.config());
+            else
+                validateCacheGroupConfiguration(ccfg, info.config());
+        }
+
+        String conflictErr = processJoiningNode(joinDiscoData, ctx.localNodeId(), true);
+
+        if (conflictErr != null)
+            throw new IgniteCheckedException("Failed to start configured cache. " + conflictErr);
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @param grpName Group name.
+     * @return Group ID.
+     */
+    private int cacheGroupId(String cacheName, @Nullable String grpName) {
+        assert cacheName != null;
+
+        return grpName != null ? CU.cacheId(grpName) : CU.cacheId(cacheName);
     }
 
     /**
@@ -105,14 +140,19 @@ class ClusterCachesInfo {
      * @throws IgniteCheckedException If failed.
      */
     void onKernalStart(boolean checkConsistency) throws IgniteCheckedException {
+        if (gridData != null && gridData.conflictErr != null)
+            throw new IgniteCheckedException(gridData.conflictErr);
+
         if (checkConsistency && joinDiscoData != null && gridData != null) {
             for (CacheJoinNodeDiscoveryData.CacheInfo locCacheInfo : joinDiscoData.caches().values()) {
                 CacheConfiguration locCfg = locCacheInfo.config();
 
-                CacheData cacheData = gridData.caches().get(locCfg.getName());
+                CacheData cacheData = gridData.gridData.caches().get(locCfg.getName());
 
                 if (cacheData != null)
                     checkCache(locCacheInfo, cacheData, cacheData.receivedFrom());
+
+                validateStartCacheConfiguration(locCfg);
             }
         }
 
@@ -136,6 +176,9 @@ class ClusterCachesInfo {
         CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "cacheMode", "Cache mode",
             locAttr.cacheMode(), rmtAttr.cacheMode(), true);
 
+        CU.checkAttributeMismatch(log, rmtAttr.groupName(), rmt, "groupName", "Cache group name",
+            locAttr.groupName(), rmtAttr.groupName(), true);
+
         CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "sql", "SQL flag",
             locAttr.sql(), rmtAttr.sql(), true);
 
@@ -149,6 +192,9 @@ class ClusterCachesInfo {
             CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "cachePreloadMode",
                 "Cache preload mode", locAttr.cacheRebalanceMode(), rmtAttr.cacheRebalanceMode(), true);
 
+            CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "topologyValidator",
+                "Cache topology validator", locAttr.topologyValidatorClassName(), rmtAttr.topologyValidatorClassName(), true);
+
             ClusterNode rmtNode = ctx.discovery().node(rmt);
 
             if (CU.affinityNode(ctx.discovery().localNode(), locInfo.config().getNodeFilter())
@@ -249,6 +295,7 @@ class ClusterCachesInfo {
                     DynamicCacheDescriptor templateDesc = new DynamicCacheDescriptor(ctx,
                         ccfg,
                         req.cacheType(),
+                        null,
                         true,
                         req.initiatingNodeId(),
                         false,
@@ -276,6 +323,17 @@ class ClusterCachesInfo {
 
             if (req.start()) {
                 if (desc == null) {
+                    String conflictErr = checkCacheConflict(req.startCacheConfiguration());
+
+                    if (conflictErr != null) {
+                        U.warn(log, "Ignore cache start request. " + conflictErr);
+
+                        ctx.cache().completeCacheStartFuture(req, false, new IgniteCheckedException("Failed to start " +
+                            "cache. " + conflictErr));
+
+                        continue;
+                    }
+
                     if (req.clientStartOnly()) {
                         ctx.cache().completeCacheStartFuture(req, false, new IgniteCheckedException("Failed to start " +
                             "client cache (a cache with the given name is not started): " + req.cacheName()));
@@ -286,9 +344,19 @@ class ClusterCachesInfo {
                         assert req.cacheType() != null : req;
                         assert F.eq(ccfg.getName(), req.cacheName()) : req;
 
+                        int cacheId = CU.cacheId(req.cacheName());
+
+                        CacheGroupDescriptor grpDesc = registerCacheGroup(exchangeActions,
+                            topVer,
+                            ccfg,
+                            cacheId,
+                            req.initiatingNodeId(),
+                            req.deploymentId());
+
                         DynamicCacheDescriptor startDesc = new DynamicCacheDescriptor(ctx,
                             ccfg,
                             req.cacheType(),
+                            grpDesc,
                             false,
                             req.initiatingNodeId(),
                             false,
@@ -301,10 +369,9 @@ class ClusterCachesInfo {
                         assert old == null;
 
                         ctx.discovery().setCacheFilter(
+                            grpDesc.groupId(),
                             ccfg.getName(),
-                            ccfg.getNodeFilter(),
-                            ccfg.getNearConfiguration() != null,
-                            ccfg.getCacheMode());
+                            ccfg.getNearConfiguration() != null);
 
                         ctx.discovery().addClientNode(req.cacheName(),
                             req.initiatingNodeId(),
@@ -380,8 +447,6 @@ class ClusterCachesInfo {
                 }
             }
             else if (req.stop()) {
-                assert req.stop() ^ req.close() : req;
-
                 if (desc != null) {
                     if (req.sql() && !desc.sql()) {
                         ctx.cache().completeCacheStartFuture(req, false,
@@ -401,13 +466,27 @@ class ClusterCachesInfo {
 
                     DynamicCacheDescriptor old = registeredCaches.remove(req.cacheName());
 
-                    assert old != null : "Dynamic cache map was concurrently modified [req=" + req + ']';
+                    assert old != null && old == desc : "Dynamic cache map was concurrently modified [req=" + req + ']';
 
                     ctx.discovery().removeCacheFilter(req.cacheName());
 
                     needExchange = true;
 
                     exchangeActions.addCacheToStop(req, desc);
+
+                    CacheGroupDescriptor grpDesc = registeredCacheGrps.get(desc.groupId());
+
+                    assert grpDesc != null && grpDesc.groupId() == desc.groupId() : desc;
+
+                    grpDesc.onCacheStopped(desc.cacheName(), desc.cacheId());
+
+                    if (!grpDesc.hasCaches()) {
+                        registeredCacheGrps.remove(grpDesc.groupId());
+
+                        ctx.discovery().removeCacheGroup(grpDesc);
+
+                        exchangeActions.addCacheGroupToStop(grpDesc);
+                    }
                 }
             }
             else if (req.close()) {
@@ -487,10 +566,24 @@ class ClusterCachesInfo {
      */
     private Serializable joinDiscoveryData() {
         if (cachesOnDisconnect != null) {
+            Map<Integer, CacheClientReconnectDiscoveryData.CacheGroupInfo> cacheGrpsInfo = new HashMap<>();
             Map<String, CacheClientReconnectDiscoveryData.CacheInfo> cachesInfo = new HashMap<>();
 
+            Map<Integer, CacheGroupDescriptor> grps = cachesOnDisconnect.cacheGrps;
+            Map<String, DynamicCacheDescriptor> caches = cachesOnDisconnect.caches;
+
+            for (CacheGroupContext grp : ctx.cache().cacheGroups()) {
+                CacheGroupDescriptor desc = grps.get(grp.groupId());
+
+                assert desc != null : grp.cacheOrGroupName();
+
+                cacheGrpsInfo.put(grp.groupId(), new CacheClientReconnectDiscoveryData.CacheGroupInfo(desc.config(),
+                    desc.deploymentId(),
+                    0));
+            }
+
             for (IgniteInternalCache cache : ctx.cache().caches()) {
-                DynamicCacheDescriptor desc = cachesOnDisconnect.get(cache.name());
+                DynamicCacheDescriptor desc = caches.get(cache.name());
 
                 assert desc != null : cache.name();
 
@@ -498,10 +591,10 @@ class ClusterCachesInfo {
                     desc.cacheType(),
                     desc.deploymentId(),
                     cache.context().isNear(),
-                    (byte)0));
+                    0));
             }
 
-            return new CacheClientReconnectDiscoveryData(cachesInfo);
+            return new CacheClientReconnectDiscoveryData(cacheGrpsInfo, cachesInfo);
         }
         else {
             assert ctx.config().isDaemon() || joinDiscoData != null || !ctx.state().active();
@@ -564,6 +657,11 @@ class ClusterCachesInfo {
      */
     void onDiscoveryEvent(int type, ClusterNode node, AffinityTopologyVersion topVer) {
         if (type == EVT_NODE_JOINED && !ctx.isDaemon()) {
+            for (CacheGroupDescriptor desc : registeredCacheGrps.values()) {
+                if (node.id().equals(desc.receivedFrom()))
+                    desc.receivedFromStartVersion(topVer);
+            }
+
             for (DynamicCacheDescriptor desc : registeredCaches.values()) {
                 if (node.id().equals(desc.receivedFrom()))
                     desc.receivedFromStartVersion(topVer);
@@ -599,11 +697,27 @@ class ClusterCachesInfo {
      * @return Information about started caches.
      */
     private CacheNodeCommonDiscoveryData collectCommonDiscoveryData() {
+        Map<Integer, CacheGroupData> cacheGrps = new HashMap<>();
+
+        for (CacheGroupDescriptor grpDesc : registeredCacheGrps.values()) {
+            CacheGroupData grpData = new CacheGroupData(grpDesc.config(),
+                grpDesc.groupName(),
+                grpDesc.groupId(),
+                grpDesc.receivedFrom(),
+                grpDesc.startTopologyVersion(),
+                grpDesc.deploymentId(),
+                grpDesc.caches(),
+                0);
+
+            cacheGrps.put(grpDesc.groupId(), grpData);
+        }
+
         Map<String, CacheData> caches = new HashMap<>();
 
         for (DynamicCacheDescriptor desc : registeredCaches.values()) {
             CacheData cacheData = new CacheData(desc.cacheConfiguration(),
                 desc.cacheId(),
+                desc.groupId(),
                 desc.cacheType(),
                 desc.deploymentId(),
                 desc.schema(),
@@ -611,7 +725,7 @@ class ClusterCachesInfo {
                 desc.staticallyConfigured(),
                 desc.sql(),
                 false,
-                (byte)0);
+                0);
 
             caches.put(desc.cacheName(), cacheData);
         }
@@ -621,6 +735,7 @@ class ClusterCachesInfo {
         for (DynamicCacheDescriptor desc : registeredTemplates.values()) {
             CacheData cacheData = new CacheData(desc.cacheConfiguration(),
                 0,
+                0,
                 desc.cacheType(),
                 desc.deploymentId(),
                 desc.schema(),
@@ -628,12 +743,15 @@ class ClusterCachesInfo {
                 desc.staticallyConfigured(),
                 false,
                 true,
-                (byte)0);
+                0);
 
             templates.put(desc.cacheName(), cacheData);
         }
 
-        return new CacheNodeCommonDiscoveryData(caches, templates, ctx.discovery().clientNodesMap());
+        return new CacheNodeCommonDiscoveryData(caches,
+            templates,
+            cacheGrps,
+            ctx.discovery().clientNodesMap());
     }
 
     /**
@@ -648,11 +766,36 @@ class ClusterCachesInfo {
 
         CacheNodeCommonDiscoveryData cachesData = (CacheNodeCommonDiscoveryData)data.commonData();
 
+        // Replace locally registered data with actual data received from cluster.
+        registeredCaches.clear();
+        registeredCacheGrps.clear();
+        ctx.discovery().onLocalNodeJoin();
+
+        for (CacheGroupData grpData : cachesData.cacheGroups().values()) {
+            CacheGroupDescriptor grpDesc = new CacheGroupDescriptor(
+                grpData.config(),
+                grpData.groupName(),
+                grpData.groupId(),
+                grpData.receivedFrom(),
+                grpData.startTopologyVersion(),
+                grpData.deploymentId(),
+                grpData.caches());
+
+            CacheGroupDescriptor old = registeredCacheGrps.put(grpDesc.groupId(), grpDesc);
+
+            assert old == null : old;
+
+            ctx.discovery().addCacheGroup(grpDesc,
+                grpData.config().getNodeFilter(),
+                grpData.config().getCacheMode());
+        }
+
         for (CacheData cacheData : cachesData.templates().values()) {
             DynamicCacheDescriptor desc = new DynamicCacheDescriptor(
                 ctx,
                 cacheData.cacheConfiguration(),
                 cacheData.cacheType(),
+                null,
                 true,
                 cacheData.receivedFrom(),
                 cacheData.staticallyConfigured(),
@@ -664,12 +807,17 @@ class ClusterCachesInfo {
         }
 
         for (CacheData cacheData : cachesData.caches().values()) {
+            CacheGroupDescriptor grpDesc = registeredCacheGrps.get(cacheData.groupId());
+
+            assert grpDesc != null : cacheData.cacheConfiguration().getName();
+
             CacheConfiguration<?, ?> cfg = cacheData.cacheConfiguration();
 
             DynamicCacheDescriptor desc = new DynamicCacheDescriptor(
                 ctx,
                 cacheData.cacheConfiguration(),
                 cacheData.cacheType(),
+                grpDesc,
                 false,
                 cacheData.receivedFrom(),
                 cacheData.staticallyConfigured(),
@@ -682,10 +830,9 @@ class ClusterCachesInfo {
             registeredCaches.put(cacheData.cacheConfiguration().getName(), desc);
 
             ctx.discovery().setCacheFilter(
+                grpDesc.groupId(),
                 cfg.getName(),
-                cfg.getNodeFilter(),
-                cfg.getNearConfiguration() != null,
-                cfg.getCacheMode());
+                cfg.getNearConfiguration() != null);
         }
 
         if (!F.isEmpty(cachesData.clientNodesMap())) {
@@ -697,7 +844,24 @@ class ClusterCachesInfo {
             }
         }
 
-        gridData = cachesData;
+        String conflictErr = null;
+
+        if (joinDiscoData != null) {
+            for (Map.Entry<String, CacheJoinNodeDiscoveryData.CacheInfo> e : joinDiscoData.caches().entrySet()) {
+                if (!registeredCaches.containsKey(e.getKey())) {
+                    conflictErr = checkCacheConflict(e.getValue().config());
+
+                    if (conflictErr != null) {
+                        conflictErr = "Failed to start configured cache due to conflict with started caches. " +
+                            conflictErr;
+
+                        break;
+                    }
+                }
+            }
+        }
+
+        gridData = new GridData(cachesData, conflictErr);
 
         if (!disconnectedState())
             initStartCachesForLocalJoin(false);
@@ -730,6 +894,7 @@ class ClusterCachesInfo {
                     DynamicCacheDescriptor desc0 = new DynamicCacheDescriptor(ctx,
                             locCfg.config(),
                             desc.cacheType(),
+                            desc.groupDescriptor(),
                             desc.template(),
                             desc.receivedFrom(),
                             desc.staticallyConfigured(),
@@ -744,7 +909,9 @@ class ClusterCachesInfo {
                     desc = desc0;
                 }
 
-                if (locCfg != null || joinDiscoData.startCaches() || CU.affinityNode(ctx.discovery().localNode(), cfg.getNodeFilter())) {
+                if (locCfg != null ||
+                    joinDiscoData.startCaches() ||
+                    CU.affinityNode(ctx.discovery().localNode(), desc.groupDescriptor().config().getNodeFilter())) {
                     // Move system and internal caches first.
                     if (desc.cacheType().userCache())
                         locJoinStartCaches.add(new T2<>(desc, nearCfg));
@@ -773,7 +940,7 @@ class ClusterCachesInfo {
                     processClientReconnectData((CacheClientReconnectDiscoveryData) joiningNodeData, data.joiningNodeId());
             }
             else if (joiningNodeData instanceof CacheJoinNodeDiscoveryData)
-                processJoiningNode((CacheJoinNodeDiscoveryData)joiningNodeData, data.joiningNodeId());
+                processJoiningNode((CacheJoinNodeDiscoveryData)joiningNodeData, data.joiningNodeId(), false);
         }
     }
 
@@ -797,10 +964,59 @@ class ClusterCachesInfo {
     }
 
     /**
+     * @param cfg Cache configuration.
+     * @return {@code True} if validation passed.
+     */
+    private String checkCacheConflict(CacheConfiguration<?, ?> cfg) {
+        int cacheId = CU.cacheId(cfg.getName());
+
+        if (cacheGroupByName(cfg.getName()) != null)
+            return "Cache name conflict with existing cache group (change cache name) [cacheName=" + cfg.getName() + ']';
+
+        if (cfg.getGroupName() != null) {
+            DynamicCacheDescriptor desc = registeredCaches.get(cfg.getGroupName());
+
+            if (desc != null)
+                return "Cache group name conflict with existing cache (change group name) [cacheName=" + cfg.getName() +
+                    ", conflictingCacheName=" + desc.cacheName() + ']';
+        }
+
+        for (DynamicCacheDescriptor desc : registeredCaches.values()) {
+            if (desc.cacheId() == cacheId)
+                return "Cache ID conflict (change cache name) [cacheName=" + cfg.getName() +
+                    ", conflictingCacheName=" + desc.cacheName() + ']';
+        }
+
+        int grpId = cacheGroupId(cfg.getName(), cfg.getGroupName());
+
+        if (cfg.getGroupName() != null) {
+            if (cacheGroupByName(cfg.getGroupName()) == null) {
+                CacheGroupDescriptor desc = registeredCacheGrps.get(grpId);
+
+                if (desc != null)
+                    return "Cache group ID conflict (change cache group name) [cacheName=" + cfg.getName() +
+                        ", groupName=" + cfg.getGroupName() +
+                        (desc.sharedGroup() ? ", conflictingGroupName=" : ", conflictingCacheName=") + desc.cacheOrGroupName() + ']';
+            }
+        }
+        else {
+            CacheGroupDescriptor desc = registeredCacheGrps.get(grpId);
+
+            if (desc != null)
+                return "Cache group ID conflict (change cache name) [cacheName=" + cfg.getName() +
+                    (desc.sharedGroup() ? ", conflictingGroupName=" : ", conflictingCacheName=") + desc.cacheOrGroupName() + ']';
+        }
+
+        return null;
+    }
+
+    /**
      * @param joinData Joined node discovery data.
      * @param nodeId Joined node ID.
+     * @param locJoin {@code True} if called on local node join.
+     * @return Configuration conflict error.
      */
-    private void processJoiningNode(CacheJoinNodeDiscoveryData joinData, UUID nodeId) {
+    private String processJoiningNode(CacheJoinNodeDiscoveryData joinData, UUID nodeId, boolean locJoin) {
         for (CacheJoinNodeDiscoveryData.CacheInfo cacheInfo : joinData.templates().values()) {
             CacheConfiguration<?, ?> cfg = cacheInfo.config();
 
@@ -808,6 +1024,7 @@ class ClusterCachesInfo {
                 DynamicCacheDescriptor desc = new DynamicCacheDescriptor(ctx,
                     cfg,
                     cacheInfo.cacheType(),
+                    null,
                     true,
                     nodeId,
                     true,
@@ -825,9 +1042,35 @@ class ClusterCachesInfo {
             CacheConfiguration<?, ?> cfg = cacheInfo.config();
 
             if (!registeredCaches.containsKey(cfg.getName())) {
+                String conflictErr = checkCacheConflict(cfg);
+
+                if (conflictErr != null) {
+                    if (locJoin)
+                        return conflictErr;
+
+                    U.warn(log, "Ignore cache received from joining node. " + conflictErr);
+
+                    continue;
+                }
+
+                int cacheId = CU.cacheId(cfg.getName());
+
+                CacheGroupDescriptor grpDesc = registerCacheGroup(null,
+                    null,
+                    cfg,
+                    cacheId,
+                    nodeId,
+                    joinData.cacheDeploymentId());
+
+                ctx.discovery().setCacheFilter(
+                    grpDesc.groupId(),
+                    cfg.getName(),
+                    cfg.getNearConfiguration() != null);
+
                 DynamicCacheDescriptor desc = new DynamicCacheDescriptor(ctx,
                     cfg,
                     cacheInfo.cacheType(),
+                    grpDesc,
                     false,
                     nodeId,
                     true,
@@ -838,12 +1081,6 @@ class ClusterCachesInfo {
                 DynamicCacheDescriptor old = registeredCaches.put(cfg.getName(), desc);
 
                 assert old == null : old;
-
-                ctx.discovery().setCacheFilter(
-                    cfg.getName(),
-                    cfg.getNodeFilter(),
-                    cfg.getNearConfiguration() != null,
-                    cfg.getCacheMode());
             }
 
             ctx.discovery().addClientNode(cfg.getName(), nodeId, cfg.getNearConfiguration() != null);
@@ -856,6 +1093,158 @@ class ClusterCachesInfo {
                     desc.cacheConfiguration().getNearConfiguration() != null);
             }
         }
+
+        return null;
+    }
+
+    /**
+     * @param grpName Group name.
+     * @return Group descriptor if group found.
+     */
+    @Nullable private CacheGroupDescriptor cacheGroupByName(String grpName) {
+        assert grpName != null;
+
+        for (CacheGroupDescriptor grpDesc : registeredCacheGrps.values()) {
+            if (grpName.equals(grpDesc.groupName()))
+                return grpDesc;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param cacheName Cache name.
+     * @return Group descriptor.
+     */
+    @Nullable private CacheGroupDescriptor nonSharedCacheGroupByCacheName(String cacheName) {
+        assert cacheName != null;
+
+        for (CacheGroupDescriptor grpDesc : registeredCacheGrps.values()) {
+            if (!grpDesc.sharedGroup() && grpDesc.caches().containsKey(cacheName))
+                return grpDesc;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param exchActions Optional exchange actions to update if new group was added.
+     * @param curTopVer Current topology version if dynamic cache started.
+     * @param startedCacheCfg Cache configuration.
+     * @param cacheId Cache ID.
+     * @param rcvdFrom Node ID cache was recived from.
+     * @param deploymentId Deployment ID.
+     * @return Group descriptor.
+     */
+    private CacheGroupDescriptor registerCacheGroup(
+        @Nullable ExchangeActions exchActions,
+        @Nullable AffinityTopologyVersion curTopVer,
+        CacheConfiguration<?, ?> startedCacheCfg,
+        Integer cacheId,
+        UUID rcvdFrom,
+        IgniteUuid deploymentId) {
+        if (startedCacheCfg.getGroupName() != null) {
+            CacheGroupDescriptor desc = cacheGroupByName(startedCacheCfg.getGroupName());
+
+            if (desc != null) {
+                desc.onCacheAdded(startedCacheCfg.getName(), cacheId);
+
+                return desc;
+            }
+        }
+
+        int grpId = cacheGroupId(startedCacheCfg.getName(), startedCacheCfg.getGroupName());
+
+        Map<String, Integer> caches = Collections.singletonMap(startedCacheCfg.getName(), cacheId);
+
+        CacheGroupDescriptor grpDesc = new CacheGroupDescriptor(
+            startedCacheCfg,
+            startedCacheCfg.getGroupName(),
+            grpId,
+            rcvdFrom,
+            curTopVer != null ? curTopVer.nextMinorVersion() : null,
+            deploymentId,
+            caches);
+
+        CacheGroupDescriptor old = registeredCacheGrps.put(grpId, grpDesc);
+
+        assert old == null : old;
+
+        ctx.discovery().addCacheGroup(grpDesc, grpDesc.config().getNodeFilter(), startedCacheCfg.getCacheMode());
+
+        if (exchActions != null)
+            exchActions.addCacheGroupToStart(grpDesc);
+
+        return grpDesc;
+    }
+
+    /**
+     * @return Registered cache groups.
+     */
+    ConcurrentMap<Integer, CacheGroupDescriptor> registeredCacheGroups() {
+        return registeredCacheGrps;
+    }
+
+    /**
+     * @param ccfg Cache configuration to start.
+     * @throws IgniteCheckedException If failed.
+     */
+    void validateStartCacheConfiguration(CacheConfiguration ccfg) throws IgniteCheckedException {
+        if (ccfg.getGroupName() != null) {
+            CacheGroupDescriptor grpDesc = cacheGroupByName(ccfg.getGroupName());
+
+            if (grpDesc != null) {
+                assert ccfg.getGroupName().equals(grpDesc.groupName());
+
+                validateCacheGroupConfiguration(grpDesc.config(), ccfg);
+            }
+        }
+    }
+
+    /**
+     * @param cfg Existing configuration.
+     * @param startCfg Cache configuration to start.
+     * @throws IgniteCheckedException If validation failed.
+     */
+    private void validateCacheGroupConfiguration(CacheConfiguration cfg, CacheConfiguration startCfg)
+        throws IgniteCheckedException {
+        GridCacheAttributes attr1 = new GridCacheAttributes(cfg, false);
+        GridCacheAttributes attr2 = new GridCacheAttributes(startCfg, false);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "cacheMode", "Cache mode",
+            cfg.getCacheMode(), startCfg.getCacheMode(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "affinity", "Affinity function",
+            attr1.cacheAffinityClassName(), attr2.cacheAffinityClassName(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "affinityPartitionsCount",
+            "Affinity partitions count", attr1.affinityPartitionsCount(), attr2.affinityPartitionsCount(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "nodeFilter", "Node filter",
+            attr1.nodeFilterClassName(), attr2.nodeFilterClassName(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "memoryPolicyName", "Memory policy",
+            cfg.getMemoryPolicyName(), startCfg.getMemoryPolicyName(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "topologyValidator", "Topology validator",
+            attr1.topologyValidatorClassName(), attr2.topologyValidatorClassName(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "partitionLossPolicy", "Partition Loss Policy",
+            cfg.getPartitionLossPolicy(), startCfg.getPartitionLossPolicy(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "rebalanceMode", "Rebalance mode",
+            cfg.getRebalanceMode(), startCfg.getRebalanceMode(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "rebalanceDelay", "Rebalance delay",
+            cfg.getRebalanceDelay(), startCfg.getRebalanceDelay(), false);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "rebalanceOrder", "Rebalance order",
+            cfg.getRebalanceOrder(), startCfg.getRebalanceOrder(), false);
+
+        if (cfg.getCacheMode() == PARTITIONED) {
+            CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg, "backups", "Backups",
+                cfg.getBackups(), startCfg.getBackups(), true);
+        }
     }
 
     /**
@@ -876,8 +1265,11 @@ class ClusterCachesInfo {
      *
      */
     void onDisconnect() {
-        cachesOnDisconnect = new HashMap<>(registeredCaches);
+        cachesOnDisconnect = new CachesOnDisconnect(
+            new HashMap<>(registeredCacheGrps),
+            new HashMap<>(registeredCaches));
 
+        registeredCacheGrps.clear();
         registeredCaches.clear();
         registeredTemplates.clear();
 
@@ -885,14 +1277,41 @@ class ClusterCachesInfo {
     }
 
     /**
-     * @return Stopped caches names.
+     * @return Information about stopped caches and cache groups.
      */
-    Set<String> onReconnected() {
+    ClusterCachesReconnectResult onReconnected() {
         assert disconnectedState();
 
         Set<String> stoppedCaches = new HashSet<>();
+        Set<Integer> stoppedCacheGrps = new HashSet<>();
+
+        for (Map.Entry<Integer, CacheGroupDescriptor> e : cachesOnDisconnect.cacheGrps.entrySet()) {
+            CacheGroupDescriptor locDesc = e.getValue();
+
+            CacheGroupDescriptor desc;
+            boolean stopped = true;
 
-        for(Map.Entry<String, DynamicCacheDescriptor> e : cachesOnDisconnect.entrySet()) {
+            if (locDesc.sharedGroup()) {
+                desc = cacheGroupByName(locDesc.groupName());
+
+                if (desc != null && desc.deploymentId().equals(locDesc.deploymentId()))
+                    stopped = false;
+            }
+            else {
+                desc = nonSharedCacheGroupByCacheName(locDesc.config().getName());
+
+                if (desc != null &&
+                    (surviveReconnect(locDesc.config().getName()) || desc.deploymentId().equals(locDesc.deploymentId())))
+                    stopped = false;
+            }
+
+            if (stopped)
+                stoppedCacheGrps.add(locDesc.groupId());
+            else
+                assert locDesc.groupId() == desc.groupId();
+        }
+
+        for (Map.Entry<String, DynamicCacheDescriptor> e : cachesOnDisconnect.caches.entrySet()) {
             DynamicCacheDescriptor desc = e.getValue();
 
             String cacheName = e.getKey();
@@ -920,7 +1339,7 @@ class ClusterCachesInfo {
 
         cachesOnDisconnect = null;
 
-        return stoppedCaches;
+        return new ClusterCachesReconnectResult(stoppedCacheGrps, stoppedCaches);
     }
 
     /**
@@ -942,6 +1361,48 @@ class ClusterCachesInfo {
      *
      */
     void clearCaches() {
+        registeredCacheGrps.clear();
+
         registeredCaches.clear();
     }
+
+    /**
+     *
+     */
+    static class GridData {
+        /** */
+        private final CacheNodeCommonDiscoveryData gridData;
+
+        /** */
+        private final String conflictErr;
+
+        /**
+         * @param gridData Grid data.
+         * @param conflictErr Cache configuration conflict error.
+         */
+        GridData(CacheNodeCommonDiscoveryData gridData, String conflictErr) {
+            this.gridData = gridData;
+            this.conflictErr = conflictErr;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class CachesOnDisconnect {
+        /** */
+        final Map<Integer, CacheGroupDescriptor> cacheGrps;
+
+        /** */
+        final Map<String, DynamicCacheDescriptor> caches;
+
+        /**
+         * @param cacheGrps Cache groups.
+         * @param caches Caches.
+         */
+        CachesOnDisconnect(Map<Integer, CacheGroupDescriptor> cacheGrps, Map<String, DynamicCacheDescriptor> caches) {
+            this.cacheGrps = cacheGrps;
+            this.caches = caches;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesReconnectResult.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesReconnectResult.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesReconnectResult.java
new file mode 100644
index 0000000..23854c3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesReconnectResult.java
@@ -0,0 +1,61 @@
+/*
+ * 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.util.Set;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+class ClusterCachesReconnectResult {
+    /** */
+    private final Set<Integer> stoppedCacheGrps;
+
+    /** */
+    private final Set<String> stoppedCaches;
+
+    /**
+     * @param stoppedCacheGrps Stopped cache groups.
+     * @param stoppedCaches Stopped caches.
+     */
+    ClusterCachesReconnectResult(Set<Integer> stoppedCacheGrps,
+        Set<String> stoppedCaches) {
+        this.stoppedCacheGrps = stoppedCacheGrps;
+        this.stoppedCaches = stoppedCaches;
+    }
+
+    /**
+     * @return Stopped cache groups.
+     */
+    Set<Integer> stoppedCacheGroups() {
+        return stoppedCacheGrps;
+    }
+
+    /**
+     * @return Stopped caches.
+     */
+    Set<String> stoppedCaches() {
+        return stoppedCaches;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(ClusterCachesReconnectResult.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
index a682f63..315013d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/DynamicCacheDescriptor.java
@@ -93,10 +93,14 @@ public class DynamicCacheDescriptor {
     /** Current schema. */
     private QuerySchema schema;
 
+    /** */
+    private final CacheGroupDescriptor grpDesc;
+
     /**
      * @param ctx Context.
      * @param cacheCfg Cache configuration.
      * @param cacheType Cache type.
+     * @param grpDesc Group descriptor.
      * @param template {@code True} if this is template configuration.
      * @param rcvdFrom ID of node provided cache configuration
      * @param staticCfg {@code True} if cache statically configured.
@@ -108,6 +112,7 @@ public class DynamicCacheDescriptor {
     public DynamicCacheDescriptor(GridKernalContext ctx,
         CacheConfiguration cacheCfg,
         CacheType cacheType,
+        CacheGroupDescriptor grpDesc,
         boolean template,
         UUID rcvdFrom,
         boolean staticCfg,
@@ -115,6 +120,7 @@ public class DynamicCacheDescriptor {
         IgniteUuid deploymentId,
         QuerySchema schema) {
         assert cacheCfg != null;
+        assert grpDesc != null || template;
         assert schema != null;
 
         if (cacheCfg.getCacheMode() == CacheMode.REPLICATED && cacheCfg.getNearConfiguration() != null) {
@@ -125,6 +131,7 @@ public class DynamicCacheDescriptor {
 
         this.cacheCfg = cacheCfg;
         this.cacheType = cacheType;
+        this.grpDesc = grpDesc;
         this.template = template;
         this.rcvdFrom = rcvdFrom;
         this.staticCfg = staticCfg;
@@ -141,6 +148,24 @@ public class DynamicCacheDescriptor {
     }
 
     /**
+     * @return Cache group ID.
+     */
+    public int groupId() {
+        assert grpDesc != null : this;
+
+        return grpDesc.groupId();
+    }
+
+    /**
+     * @return Cache group descriptor.
+     */
+    public CacheGroupDescriptor groupDescriptor() {
+        assert grpDesc != null : this;
+
+        return grpDesc;
+    }
+
+    /**
      * @return Cache ID.
      */
     public Integer cacheId() {
@@ -203,6 +228,7 @@ public class DynamicCacheDescriptor {
      *
      * @param proc Object processor.
      * @return Cache object context.
+     * @throws IgniteCheckedException If failed.
      */
     public CacheObjectContext cacheObjectContext(IgniteCacheObjectProcessor proc) throws IgniteCheckedException {
         if (objCtx == null) {


[13/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index bf4691b..45e6a8b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
@@ -74,6 +74,7 @@ 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.affinity.GridAffinityAssignmentCache;
 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.IgniteCacheSnapshotManager;
@@ -176,6 +177,9 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     private GridCacheSharedContext<?, ?> sharedCtx;
 
     /** */
+    private final ConcurrentMap<Integer, CacheGroupContext> cacheGrps = new ConcurrentHashMap<>();
+
+    /** */
     private final Map<String, GridCacheAdapter<?, ?>> caches;
 
     /** Caches stopped from onKernalStop callback. */
@@ -526,7 +530,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      */
     private List<GridCacheManager> dhtManagers(GridCacheContext ctx) {
         return F.asList(ctx.store(), ctx.events(), ctx.evicts(), ctx.queries(), ctx.continuousQueries(),
-            ctx.dr(), ctx.offheap());
+            ctx.dr());
     }
 
     /**
@@ -538,7 +542,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         if (ctx.config().getCacheMode() == LOCAL || !isNearEnabled(ctx))
             return Collections.emptyList();
         else
-            return F.asList(ctx.queries(), ctx.continuousQueries(), ctx.store(), ctx.offheap());
+            return F.asList(ctx.queries(), ctx.continuousQueries(), ctx.store());
     }
 
     /**
@@ -552,7 +556,6 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         prepare(cfg, cfg.getAffinityMapper(), false);
         prepare(cfg, cfg.getEvictionFilter(), false);
         prepare(cfg, cfg.getInterceptor(), false);
-        prepare(cfg, cfg.getTopologyValidator(), false);
 
         NearCacheConfiguration nearCfg = cfg.getNearConfiguration();
 
@@ -590,7 +593,6 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         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())) {
@@ -607,6 +609,16 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     }
 
     /**
+     * @param grp Cache group.
+     */
+    private void cleanup(CacheGroupContext grp) {
+        CacheConfiguration cfg = grp.config();
+
+        for (Object obj : grp.configuredUserObjects())
+            cleanup(cfg, obj, false);
+    }
+
+    /**
      * @param cfg Cache configuration.
      * @param rsrc Resource.
      * @param near Near flag.
@@ -720,10 +732,10 @@ public class GridCacheProcessor extends GridProcessorAdapter {
             else
                 stopSeq.addFirst(cfg.getName());
 
-            caches.put(cfg.getName(), new CacheJoinNodeDiscoveryData.CacheInfo(cfg, cacheType, sql, (byte)0));
+            caches.put(cfg.getName(), new CacheJoinNodeDiscoveryData.CacheInfo(cfg, cacheType, sql, 0));
         }
         else
-            templates.put(cfg.getName(), new CacheJoinNodeDiscoveryData.CacheInfo(cfg, CacheType.USER, false, (byte)0));
+            templates.put(cfg.getName(), new CacheJoinNodeDiscoveryData.CacheInfo(cfg, CacheType.USER, false, 0));
     }
 
     /**
@@ -760,23 +772,21 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         assert !ctx.config().isDaemon();
 
         if (sharedCtx.pageStore() != null && sharedCtx.database().persistenceEnabled()) {
-            Set<String> savedCacheNames = sharedCtx.pageStore().savedCacheNames();
+            Map<String, CacheConfiguration> ccfgs = sharedCtx.pageStore().readCacheConfigurations();
 
-            savedCacheNames.removeAll(caches.keySet());
+            for (String cache : caches.keySet())
+                ccfgs.remove(cache);
 
-            savedCacheNames.removeAll(internalCaches);
+            for (String cache : internalCaches)
+                ccfgs.remove(cache);
 
-            if (!F.isEmpty(savedCacheNames)) {
+            if (!F.isEmpty(ccfgs)) {
                 if (log.isInfoEnabled())
-                    log.info("Register persistent caches: " + savedCacheNames);
-
-                for (String name : savedCacheNames) {
-                    CacheConfiguration cfg = sharedCtx.pageStore().readConfiguration(name);
+                    log.info("Register persistent caches: " + ccfgs.keySet());
 
-                    // TODO IGNITE-5306 - set correct SQL flag below.
-                    if (cfg != null)
-                        addCacheOnJoin(cfg, false, caches, templates);
-                }
+                // TODO IGNITE-5306 - set correct SQL flag below.
+                for (CacheConfiguration ccfg : ccfgs.values())
+                    addCacheOnJoin(ccfg, false, caches, templates);
             }
         }
     }
@@ -800,6 +810,21 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         internalCaches.add(CU.ATOMICS_CACHE_NAME);
     }
 
+    /**
+     * @param grpId Group ID.
+     * @return Cache group.
+     */
+    @Nullable public CacheGroupContext cacheGroup(int grpId) {
+        return cacheGrps.get(grpId);
+    }
+
+    /**
+     * @return Cache groups.
+     */
+    public Collection<CacheGroupContext> cacheGroups() {
+        return cacheGrps.values();
+    }
+
     /** {@inheritDoc} */
     @SuppressWarnings("unchecked")
     @Override public void onKernalStart(boolean activeOnStart) throws IgniteCheckedException {
@@ -824,14 +849,13 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                 for (CacheConfiguration conf : ctx.config().getCacheConfiguration()) {
                     assert conf.getName() != null;
 
-                    for (DynamicCacheDescriptor desc : cacheDescriptors()) {
+                    for (DynamicCacheDescriptor desc : cacheDescriptors().values()) {
                         CacheConfiguration c = desc.cacheConfiguration();
-                        IgnitePredicate filter = c.getNodeFilter();
+                        IgnitePredicate filter = desc.groupDescriptor().config().getNodeFilter();
 
                         if (c.getName().equals(conf.getName()) &&
                             ((desc.receivedOnDiscovery() && CU.affinityNode(locNode, filter)) ||
                                 CU.isSystemCache(c.getName()))) {
-
                             tmpCacheCfg.add(c);
 
                             break;
@@ -968,6 +992,9 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                 stopCache(cache, cancel, false);
         }
 
+        for (CacheGroupContext grp : cacheGrps.values())
+            stopCacheGroup(grp.groupId());
+
         cachesInfo.clearCaches();
     }
 
@@ -990,6 +1017,9 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         // No new caches should be added after this point.
         exch.onKernalStop(cancel);
 
+        for (CacheGroupContext grp : cacheGrps.values())
+            grp.onKernalStop();
+
         onKernalStopCaches(cancel);
 
         cancelFutures();
@@ -1009,11 +1039,13 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @param cancel Cancel.
      */
     public void onKernalStopCaches(boolean cancel){
-        for (GridCacheAdapter<?, ?> cache : caches.values()) {
-            GridCacheAffinityManager aff = cache.context().affinity();
+        IgniteCheckedException affErr =
+            new IgniteCheckedException("Failed to wait for topology update, node is stopping.");
 
-            if (aff != null)
-                aff.cancelFutures();
+        for (CacheGroupContext grp : cacheGrps.values()) {
+            GridAffinityAssignmentCache aff = grp.affinity();
+
+            aff.cancelFutures(affErr);
         }
 
         for (String cacheName : stopSeq) {
@@ -1049,6 +1081,9 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         for (IgniteInternalFuture fut : pendingTemplateFuts.values())
             ((GridFutureAdapter)fut).onDone(err);
 
+        for (CacheGroupContext grp : cacheGrps.values())
+            grp.onDisconnected(reconnectFut);
+
         for (GridCacheAdapter cache : caches.values()) {
             GridCacheContext cctx = cache.context();
 
@@ -1068,37 +1103,35 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         cachesInfo.onDisconnect();
     }
 
-    /** {@inheritDoc} */
-    @Override public IgniteInternalFuture<?> onReconnected(boolean clusterRestarted) throws IgniteCheckedException {
-        List<GridCacheAdapter> reconnected = new ArrayList<>(caches.size());
-
-        GridCompoundFuture<?, ?> stopFut = null;
+    /**
+     * @param cctx Cache context.
+     * @param stoppedCaches List where stopped cache should be added.
+     */
+    private void stopCacheOnReconnect(GridCacheContext cctx, List<GridCacheAdapter> stoppedCaches) {
+        cctx.gate().reconnected(true);
 
-        Set<String> stoppedCaches = cachesInfo.onReconnected();
+        sharedCtx.removeCacheContext(cctx);
 
-        for (final GridCacheAdapter cache : caches.values()) {
-            boolean stopped = stoppedCaches.contains(cache.name());
+        caches.remove(cctx.name());
+        jCacheProxies.remove(cctx.name());
 
-            if (stopped) {
-                cache.context().gate().reconnected(true);
+        stoppedCaches.add(cctx.cache());
+    }
 
-                sharedCtx.removeCacheContext(cache.ctx);
+    /** {@inheritDoc} */
+    @Override public IgniteInternalFuture<?> onReconnected(boolean clusterRestarted) throws IgniteCheckedException {
+        List<GridCacheAdapter> reconnected = new ArrayList<>(caches.size());
 
-                caches.remove(cache.name());
-                jCacheProxies.remove(cache.name());
+        ClusterCachesReconnectResult reconnectRes = cachesInfo.onReconnected();
 
-                IgniteInternalFuture<?> fut = ctx.closure().runLocalSafe(new Runnable() {
-                    @Override public void run() {
-                        onKernalStop(cache, true);
-                        stopCache(cache, true, false);
-                    }
-                });
+        final List<GridCacheAdapter> stoppedCaches = new ArrayList<>();
 
-                if (stopFut == null)
-                    stopFut = new GridCompoundFuture<>();
+        for (final GridCacheAdapter cache : caches.values()) {
+            boolean stopped = reconnectRes.stoppedCacheGroups().contains(cache.context().groupId())
+                || reconnectRes.stoppedCaches().contains(cache.name());
 
-                stopFut.add((IgniteInternalFuture)fut);
-            }
+            if (stopped)
+                stopCacheOnReconnect(cache.context(), stoppedCaches);
             else {
                 cache.onReconnected();
 
@@ -1110,7 +1143,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
                     DynamicCacheDescriptor desc = cacheDescriptor(cctx.name());
 
-                    assert desc != null;
+                    assert desc != null : cctx.name();
 
                     ctx.query().onCacheStop0(cctx.name());
                     ctx.query().onCacheStart0(cctx, desc.schema());
@@ -1118,13 +1151,37 @@ public class GridCacheProcessor extends GridProcessorAdapter {
             }
         }
 
+        final Set<Integer> stoppedGrps = reconnectRes.stoppedCacheGroups();
+
+        for (CacheGroupContext grp : cacheGrps.values()) {
+            if (stoppedGrps.contains(grp.groupId()))
+                cacheGrps.remove(grp.groupId());
+            else
+                grp.onReconnected();
+        }
+
         sharedCtx.onReconnected();
 
         for (GridCacheAdapter cache : reconnected)
             cache.context().gate().reconnected(false);
 
-        if (stopFut != null)
-            stopFut.markInitialized();
+        IgniteInternalFuture<?> stopFut = null;
+
+        if (!stoppedCaches.isEmpty()) {
+            stopFut = ctx.closure().runLocalSafe(new Runnable() {
+                @Override public void run() {
+                    for (GridCacheAdapter cache : stoppedCaches) {
+                        CacheGroupContext grp = cache.context().group();
+
+                        onKernalStop(cache, true);
+                        stopCache(cache, true, false);
+
+                        if (!grp.hasCaches())
+                            stopCacheGroup(grp);
+                    }
+                }
+            });
+        }
 
         return stopFut;
     }
@@ -1138,11 +1195,6 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     private void startCache(GridCacheAdapter<?, ?> cache, QuerySchema schema) throws IgniteCheckedException {
         GridCacheContext<?, ?> cacheCtx = cache.context();
 
-        ctx.continuous().onCacheStart(cacheCtx);
-
-        if (sharedCtx.pageStore() != null  && !ctx.clientNode())
-            sharedCtx.pageStore().initializeForCache(cacheCtx.config());
-
         CacheConfiguration cfg = cacheCtx.config();
 
         // Intentionally compare Boolean references using '!=' below to check if the flag has been explicitly set.
@@ -1173,18 +1225,22 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                 log.debug("Started DHT cache: " + dhtCtx.cache().name());
         }
 
+        ctx.continuous().onCacheStart(cacheCtx);
+
         cacheCtx.cache().start();
 
         ctx.query().onCacheStart(cacheCtx, schema);
 
         cacheCtx.onStarted();
 
-        if (log.isInfoEnabled())
-            log.info("Started cache [name=" + U.maskName(cfg.getName()) +
+
+        if (log.isInfoEnabled()) {
+            log.info("Started cache [name=" + cfg.getName() +
+                (cfg.getGroupName() != null ? ", group=" + cfg.getGroupName() : "") +
                 ", memoryPolicyName=" + cfg.getMemoryPolicyName() +
                 ", mode=" + cfg.getCacheMode() +
-                ", atomicity=" + cfg.getAtomicityMode() + ']'
-            );
+                ", atomicity=" + cfg.getAtomicityMode() + ']');
+}
     }
 
     /**
@@ -1248,10 +1304,16 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
         ctx.kernalContext().cache().context().snapshot().onCacheStop(ctx);
 
-        U.stopLifecycleAware(log, lifecycleAwares(cache.configuration(), ctx.store().configuredStore()));
+        ctx.group().stopCache(ctx, destroy);
 
-        if (log.isInfoEnabled())
-            log.info("Stopped cache: " + cache.name());
+        U.stopLifecycleAware(log, lifecycleAwares(ctx.group(), cache.configuration(), ctx.store().configuredStore()));
+
+        if (log.isInfoEnabled()) {
+            if (ctx.group().sharedGroup())
+                log.info("Stopped cache [cacheName=" + cache.name() + ", group=" + ctx.group().name() + ']');
+            else
+                log.info("Stopped cache [cacheName=" + cache.name() + ']');
+        }
 
         cleanup(ctx);
     }
@@ -1340,6 +1402,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
     /**
      * @param cfg Cache configuration to use to create cache.
+     * @param grp Cache group.
      * @param pluginMgr Cache plugin manager.
      * @param desc Cache descriptor.
      * @param locStartTopVer Current topology version.
@@ -1350,6 +1413,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @throws IgniteCheckedException If failed to create cache.
      */
     private GridCacheContext createCache(CacheConfiguration<?, ?> cfg,
+        CacheGroupContext grp,
         @Nullable CachePluginManager pluginMgr,
         DynamicCacheDescriptor desc,
         AffinityTopologyVersion locStartTopVer,
@@ -1394,7 +1458,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
         prepare(cfg, toPrepare);
 
-        U.startLifecycleAware(lifecycleAwares(cfg, cfgStore));
+        U.startLifecycleAware(lifecycleAwares(grp, cfg, cfgStore));
 
         boolean nearEnabled = GridCacheUtils.isNearEnabled(cfg);
 
@@ -1410,46 +1474,17 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         GridCacheDrManager drMgr = pluginMgr.createComponent(GridCacheDrManager.class);
         CacheStoreManager storeMgr = pluginMgr.createComponent(CacheStoreManager.class);
 
-        IgniteCacheOffheapManager offheapMgr;
-
-        if (ctx.config().getPersistentStoreConfiguration() != null) {
-            ClassLoader clsLdr = U.gridClassLoader();
-
-            try {
-                offheapMgr = (IgniteCacheOffheapManager) clsLdr
-                    .loadClass("org.apache.ignite.internal.processors.cache.database.GridCacheOffheapManager")
-                    .getConstructor()
-                    .newInstance();
-            }
-            catch (Exception e) {
-                throw new IgniteCheckedException("Failed to initialize offheap manager", e);
-            }
-        }
-        else
-            offheapMgr = new IgniteCacheOffheapManagerImpl();
-
-
         storeMgr.initialize(cfgStore, sesHolders);
 
-        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,
+            grp,
             desc.cacheType(),
             locStartTopVer,
-            desc.receivedFrom(),
             affNode,
             updatesAllowed,
-            memPlc,
-            freeList,
-            reuseList,
-
             /*
              * Managers in starting order!
              * ===========================
@@ -1462,7 +1497,6 @@ public class GridCacheProcessor extends GridProcessorAdapter {
             dataStructuresMgr,
             ttlMgr,
             drMgr,
-            offheapMgr,
             rslvrMgr,
             pluginMgr,
             affMgr
@@ -1518,14 +1552,14 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                         case TRANSACTIONAL: {
                             cache = cacheCtx.affinityNode() ?
                                 new GridDhtColocatedCache(cacheCtx) :
-                                new GridDhtColocatedCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
+                                new GridDhtColocatedCache(cacheCtx, new GridNoStorageCacheMap());
 
                             break;
                         }
                         case ATOMIC: {
                             cache = cacheCtx.affinityNode() ?
                                 new GridDhtAtomicCache(cacheCtx) :
-                                new GridDhtAtomicCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
+                                new GridDhtAtomicCache(cacheCtx, new GridNoStorageCacheMap());
 
                             break;
                         }
@@ -1574,15 +1608,11 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                 ctx,
                 sharedCtx,
                 cfg,
+                grp,
                 desc.cacheType(),
                 locStartTopVer,
-                desc.receivedFrom(),
                 affNode,
                 true,
-                memPlc,
-                freeList,
-                reuseList,
-
                 /*
                  * Managers in starting order!
                  * ===========================
@@ -1595,7 +1625,6 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                 dataStructuresMgr,
                 ttlMgr,
                 drMgr,
-                offheapMgr,
                 rslvrMgr,
                 pluginMgr,
                 affMgr
@@ -1613,7 +1642,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
                     GridDhtCache dhtCache = cacheCtx.affinityNode() ?
                         new GridDhtCache(cacheCtx) :
-                        new GridDhtCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
+                        new GridDhtCache(cacheCtx, new GridNoStorageCacheMap());
 
                     dhtCache.near(near);
 
@@ -1630,7 +1659,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
                     GridDhtAtomicCache dhtCache = cacheCtx.affinityNode() ?
                         new GridDhtAtomicCache(cacheCtx) :
-                        new GridDhtAtomicCache(cacheCtx, new GridNoStorageCacheMap(cacheCtx));
+                        new GridDhtAtomicCache(cacheCtx, new GridNoStorageCacheMap());
 
                     dhtCache.near(near);
 
@@ -1663,11 +1692,12 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @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();
-            }
-        });
+        return F.viewReadOnly(cacheDescriptors().values(),
+            new IgniteClosure<DynamicCacheDescriptor, String>() {
+                @Override public String apply(DynamicCacheDescriptor desc) {
+                    return desc.cacheConfiguration().getName();
+                }
+            });
     }
 
     /**
@@ -1712,7 +1742,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @return Collection of currently started public cache names
      */
     public Collection<String> publicCacheNames() {
-        return F.viewReadOnly(cacheDescriptors(),
+        return F.viewReadOnly(cacheDescriptors().values(),
             new IgniteClosure<DynamicCacheDescriptor, String>() {
                 @Override public String apply(DynamicCacheDescriptor desc) {
                     return desc.cacheConfiguration().getName();
@@ -1751,6 +1781,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         AffinityTopologyVersion exchTopVer)
         throws IgniteCheckedException {
         prepareCacheStart(
+            cacheDesc.groupDescriptor(),
             cacheDesc.cacheConfiguration(),
             nearCfg,
             cacheDesc,
@@ -1771,6 +1802,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                 DynamicCacheDescriptor desc = t.get1();
 
                 prepareCacheStart(
+                    desc.groupDescriptor(),
                     desc.cacheConfiguration(),
                     t.get2(),
                     desc,
@@ -1795,10 +1827,11 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
         if (started != null) {
             for (DynamicCacheDescriptor desc : started) {
-                IgnitePredicate<ClusterNode> filter = desc.cacheConfiguration().getNodeFilter();
+                IgnitePredicate<ClusterNode> filter = desc.groupDescriptor().config().getNodeFilter();
 
                 if (CU.affinityNode(ctx.discovery().localNode(), filter)) {
                     prepareCacheStart(
+                        desc.groupDescriptor(),
                         desc.cacheConfiguration(),
                         null,
                         desc,
@@ -1813,6 +1846,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     }
 
     /**
+     * @param grpDesc Cache group descriptor.
      * @param startCfg Start configuration.
      * @param reqNearCfg Near configuration if specified for client cache start request.
      * @param desc Cache descriptor.
@@ -1821,6 +1855,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @throws IgniteCheckedException If failed.
      */
     private void prepareCacheStart(
+        CacheGroupDescriptor grpDesc,
         CacheConfiguration startCfg,
         @Nullable NearCacheConfiguration reqNearCfg,
         DynamicCacheDescriptor desc,
@@ -1844,7 +1879,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
             ccfg.setNearConfiguration(null);
         }
-        else if (CU.affinityNode(ctx.discovery().localNode(), ccfg.getNodeFilter()))
+        else if (CU.affinityNode(ctx.discovery().localNode(), grpDesc.config().getNodeFilter()))
             affNode = true;
         else {
             affNode = false;
@@ -1852,7 +1887,40 @@ public class GridCacheProcessor extends GridProcessorAdapter {
             ccfg.setNearConfiguration(reqNearCfg);
         }
 
+        if (sharedCtx.pageStore() != null  && affNode)
+            sharedCtx.pageStore().initializeForCache(grpDesc, startCfg);
+
+        String grpName = startCfg.getGroupName();
+
+        CacheGroupContext grp = null;
+
+        if (grpName != null) {
+            for (CacheGroupContext grp0 : cacheGrps.values()) {
+                if (grp0.sharedGroup() && grpName.equals(grp0.name())) {
+                    grp = grp0;
+
+                    break;
+                }
+            }
+
+            if (grp == null) {
+                grp = startCacheGroup(grpDesc,
+                    desc.cacheType(),
+                    affNode,
+                    cacheObjCtx,
+                    exchTopVer);
+            }
+        }
+        else {
+            grp = startCacheGroup(grpDesc,
+                desc.cacheType(),
+                affNode,
+                cacheObjCtx,
+                exchTopVer);
+        }
+
         GridCacheContext cacheCtx = createCache(ccfg,
+            grp,
             null,
             desc,
             exchTopVer,
@@ -1870,6 +1938,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
         startCache(cache, schema != null ? schema : new QuerySchema());
 
+        grp.onCacheStarted(cacheCtx);
+
         onKernalStart(cache);
 
         if (proxyRestart)
@@ -1877,6 +1947,56 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     }
 
     /**
+     * @param desc Group descriptor.
+     * @param cacheType Cache type.
+     * @param affNode Affinity node flag.
+     * @param cacheObjCtx Cache object context.
+     * @param exchTopVer Current topology version.
+     * @return Started cache group.
+     * @throws IgniteCheckedException If failed.
+     */
+    private CacheGroupContext startCacheGroup(
+        CacheGroupDescriptor desc,
+        CacheType cacheType,
+        boolean affNode,
+        CacheObjectContext cacheObjCtx,
+        AffinityTopologyVersion exchTopVer)
+        throws IgniteCheckedException {
+        CacheConfiguration cfg = new CacheConfiguration(desc.config());
+
+        String memPlcName = cfg.getMemoryPolicyName();
+
+        MemoryPolicy memPlc = sharedCtx.database().memoryPolicy(memPlcName);
+        FreeList freeList = sharedCtx.database().freeList(memPlcName);
+        ReuseList reuseList = sharedCtx.database().reuseList(memPlcName);
+
+        CacheGroupContext grp = new CacheGroupContext(sharedCtx,
+            desc.groupId(),
+            desc.receivedFrom(),
+            cacheType,
+            cfg,
+            affNode,
+            memPlc,
+            cacheObjCtx,
+            freeList,
+            reuseList,
+            exchTopVer);
+
+        for (Object obj : grp.configuredUserObjects())
+            prepare(cfg, obj, false);
+
+        U.startLifecycleAware(grp.configuredUserObjects());
+
+        grp.start();
+
+        CacheGroupContext old = cacheGrps.put(desc.groupId(), grp);
+
+        assert old == null : old.name();
+
+        return grp;
+    }
+
+    /**
      * @param req Stop request.
      */
     void blockGateway(DynamicCacheChangeRequest req) {
@@ -1958,8 +2078,8 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         Throwable err
     ) {
         ExchangeActions actions = new ExchangeActions(){
-            @Override List<DynamicCacheChangeRequest> closeRequests(UUID nodeId) {
-                return Collections.singletonList(act.request());
+            @Override List<ActionData> closeRequests(UUID nodeId) {
+                return Collections.singletonList(act);
             }
         };
 
@@ -1992,11 +2112,12 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         }
 
         if (exchActions != null && (err == null || forceClose)) {
-            Collection<IgniteBiTuple<GridCacheContext, Boolean>> stopped = null;
+            Collection<IgniteBiTuple<CacheGroupContext, Boolean>> stoppedGrps = null;
 
-            GridCacheContext<?, ?> stopCtx = null;
-            boolean destroy = false;
             for (ExchangeActions.ActionData action : exchActions.cacheStopRequests()) {
+                GridCacheContext<?, ?> stopCtx;
+                boolean destroy;
+
                 stopGateway(action.request());
 
                 sharedCtx.database().checkpointReadLock();
@@ -2009,16 +2130,19 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                     sharedCtx.database().checkpointReadUnlock();
                 }
 
-                if (stopCtx != null) {
-                    if (stopped == null)
-                        stopped = new ArrayList<>();
+                if (stopCtx != null && !stopCtx.group().hasCaches()) {
+                    if (stoppedGrps == null)
+                        stoppedGrps = new ArrayList<>();
 
-                    stopped.add(F.<GridCacheContext, Boolean>t(stopCtx, destroy));
+                    stoppedGrps.add(F.t(stopCtx.group(), destroy));
                 }
             }
 
-            for (DynamicCacheChangeRequest req : exchActions.closeRequests(ctx.localNodeId())) {
-                String cacheName = req.cacheName();
+            for (CacheGroupDescriptor grpDesc : exchActions.cacheGroupsToStop())
+                stopCacheGroup(grpDesc.groupId());
+
+            for (ExchangeActions.ActionData req : exchActions.closeRequests(ctx.localNodeId())) {
+                String cacheName = req.request().cacheName();
 
                 IgniteCacheProxy<?, ?> proxy = jCacheProxies.remove(cacheName);
 
@@ -2031,7 +2155,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                         jCacheProxies.putIfAbsent(cacheName, new IgniteCacheProxy(cache.context(), cache, null, false));
                     }
                     else {
-                        if (req.restart())
+                        if (req.request().restart())
                             proxy.restart();
 
                         proxy.context().gate().onStopped();
@@ -2039,8 +2163,14 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                         sharedCtx.database().checkpointReadLock();
 
                         try {
-                            stopCtx = prepareCacheStop(req, forceClose);
-                            destroy = req.destroy();
+                            GridCacheContext<?, ?> stopCtx = prepareCacheStop(req.request(), forceClose);
+
+                            if (stopCtx != null && !stopCtx.group().hasCaches()) {
+                                assert  !stopCtx.group().affinityNode() : stopCtx.name();
+
+                                stopCacheGroup(stopCtx.groupId());
+                            }
+
                         }
                         finally {
                             sharedCtx.database().checkpointReadUnlock();
@@ -2048,24 +2178,37 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                     }
                 }
 
-                if (stopCtx != null) {
-                    if (stopped == null)
-                        stopped = new ArrayList<>();
-
-                    stopped.add(F.<GridCacheContext, Boolean>t(stopCtx, destroy));
-                }
-
                 if (forceClose)
-                    completeCacheStartFuture(req, false, err);
-
+                    completeCacheStartFuture(req.request(), false, err);
             }
 
-            if (stopped != null && !sharedCtx.kernalContext().clientNode())
-                sharedCtx.database().onCachesStopped(stopped);
+            if (stoppedGrps != null && !sharedCtx.kernalContext().clientNode())
+                sharedCtx.database().onCacheGroupsStopped(stoppedGrps);
         }
     }
 
     /**
+     * @param grpId Group ID.
+     */
+    private void stopCacheGroup(int grpId) {
+        CacheGroupContext grp = cacheGrps.remove(grpId);
+
+        if (grp != null)
+            stopCacheGroup(grp);
+    }
+
+    /**
+     * @param grp Cache group.
+     */
+    private void stopCacheGroup(CacheGroupContext grp) {
+        grp.stopGroup();
+
+        U.stopLifecycleAware(log, grp.configuredUserObjects());
+
+        cleanup(grp);
+    }
+
+    /**
      * @param cacheName Cache name.
      * @param deploymentId Future deployment ID.
      */
@@ -2607,17 +2750,13 @@ public class GridCacheProcessor extends GridProcessorAdapter {
         if (!ctx.config().isDaemon() &&
             sharedCtx.pageStore() != null &&
             sharedCtx.database().persistenceEnabled()) {
-            Set<String> savedCacheNames = sharedCtx.pageStore().savedCacheNames();
+            Map<String, CacheConfiguration> savedCaches = sharedCtx.pageStore().readCacheConfigurations();
 
-            for (String name : savedCacheNames) {
-                CacheConfiguration cfg = sharedCtx.pageStore().readConfiguration(name);
-
-                if (cfg != null)
-                    reqs.add(createRequest(cfg, false));
-            }
+            for (CacheConfiguration cfg : savedCaches.values())
+                reqs.add(createRequest(cfg, false));
 
             for (CacheConfiguration cfg : ctx.config().getCacheConfiguration()) {
-                if (!savedCacheNames.contains(cfg.getName()))
+                if (!savedCaches.containsKey(cfg.getName()))
                     reqs.add(createRequest(cfg, true));
             }
         }
@@ -2716,6 +2855,16 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                         }
                     }
                 }
+                if (req.start() && req.startCacheConfiguration() != null) {
+                    CacheConfiguration ccfg = req.startCacheConfiguration();
+
+                    try {
+                        cachesInfo.validateStartCacheConfiguration(ccfg);
+                    }
+                    catch (IgniteCheckedException e) {
+                        fut.onDone(e);
+                    }
+                }
 
                 if (fut.isDone())
                     continue;
@@ -2831,7 +2980,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
             }
             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 + ']');
+                    "the node) [cacheName=" + cfg.getName() + ", rebalanceOrder=" + rebalanceOrder + ']');
         }
 
         return maxOrder;
@@ -2848,7 +2997,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      */
     @Nullable private IgniteNodeValidationResult validateHashIdResolvers(ClusterNode node) {
         if (!node.isClient()) {
-            for (DynamicCacheDescriptor desc : cacheDescriptors()) {
+            for (DynamicCacheDescriptor desc : cacheDescriptors().values()) {
                 CacheConfiguration cfg = desc.cacheConfiguration();
 
                 if (cfg.getAffinity() instanceof RendezvousAffinityFunction) {
@@ -2862,11 +3011,11 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                         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() + ']';
+                                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() + ']';
+                                cfg.getName() + ", existingNodeId=" + topNode.id() + ']';
 
                             return new IgniteNodeValidationResult(topNode.id(), errMsg, sndMsg);
                         }
@@ -3125,8 +3274,15 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     /**
      * @return Cache descriptors.
      */
-    public Collection<DynamicCacheDescriptor> cacheDescriptors() {
-        return cachesInfo.registeredCaches().values();
+    public Map<String, DynamicCacheDescriptor> cacheDescriptors() {
+        return cachesInfo.registeredCaches();
+    }
+
+    /**
+     * @return Cache group descriptors.
+     */
+    public Map<Integer, CacheGroupDescriptor> cacheGroupDescriptors() {
+        return cachesInfo.registeredCacheGroups();
     }
 
     /**
@@ -3134,7 +3290,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
      * @return Cache descriptor.
      */
     @Nullable public DynamicCacheDescriptor cacheDescriptor(int cacheId) {
-        for (DynamicCacheDescriptor cacheDesc : cacheDescriptors()) {
+        for (DynamicCacheDescriptor cacheDesc : cacheDescriptors().values()) {
             CacheConfiguration ccfg = cacheDesc.cacheConfiguration();
 
             assert ccfg != null : cacheDesc;
@@ -3428,19 +3584,21 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     }
 
     /**
+     * @param grp Cache group.
      * @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) {
+    private Iterable<Object> lifecycleAwares(CacheGroupContext grp, CacheConfiguration ccfg, Object... objs) {
         Collection<Object> ret = new ArrayList<>(7 + objs.length);
 
-        ret.add(ccfg.getAffinity());
+        if (grp.affinityFunction() != ccfg.getAffinity())
+            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();
 
@@ -3811,12 +3969,12 @@ public class GridCacheProcessor extends GridProcessorAdapter {
             ctx.closure().runLocalSafe(new Runnable() {
                 @Override public void run() {
                     try {
-                        for (GridCacheContext cacheCtx : sharedCtx.cacheContexts()) {
-                            if (!cacheCtx.isLocal() && cacheCtx.affinityNode()) {
+                        for (CacheGroupContext grp : sharedCtx.cache().cacheGroups()) {
+                            if (!grp.isLocal() && grp.affinityNode()) {
                                 GridDhtPartitionTopology top = null;
 
                                 try {
-                                    top = cacheCtx.topology();
+                                    top = grp.topology();
                                 }
                                 catch (IllegalStateException ignore) {
                                     // Cache stopped.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
index 04f5b85..be1ab3d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedContext.java
@@ -415,7 +415,7 @@ public class GridCacheSharedContext<K, V> {
     /**
      * @param cacheCtx Cache context to remove.
      */
-    public void removeCacheContext(GridCacheContext cacheCtx) {
+    void removeCacheContext(GridCacheContext cacheCtx) {
         int cacheId = cacheCtx.cacheId();
 
         ctxMap.remove(cacheId, cacheCtx);
@@ -426,7 +426,7 @@ public class GridCacheSharedContext<K, V> {
             locStoreCnt.decrementAndGet();
 
         // Safely clean up the message listeners.
-        ioMgr.removeHandlers(cacheId);
+        ioMgr.removeCacheHandlers(cacheId);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
index 614b3e3..e7e6aec 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManager.java
@@ -38,7 +38,13 @@ import org.jsr166.LongAdder8;
  */
 public class GridCacheTtlManager extends GridCacheManagerAdapter {
     /** Entries pending removal. */
-    private  GridConcurrentSkipListSetEx pendingEntries;
+    private GridConcurrentSkipListSetEx pendingEntries;
+
+    /** */
+    private boolean eagerTtlEnabled;
+
+    /** */
+    private GridCacheContext dhtCtx;
 
     /** */
     private final IgniteInClosure2X<GridCacheEntryEx, GridCacheVersion> expireC =
@@ -70,6 +76,8 @@ public class GridCacheTtlManager extends GridCacheManagerAdapter {
 
     /** {@inheritDoc} */
     @Override protected void start0() throws IgniteCheckedException {
+        dhtCtx = cctx.isNear() ? cctx.near().dht().context() : cctx;
+
         boolean cleanupDisabled = cctx.kernalContext().isDaemon() ||
             !cctx.config().isEagerTtl() ||
             CU.isAtomicsCache(cctx.name()) ||
@@ -79,11 +87,20 @@ public class GridCacheTtlManager extends GridCacheManagerAdapter {
         if (cleanupDisabled)
             return;
 
+        eagerTtlEnabled = true;
+
         cctx.shared().ttl().register(this);
 
         pendingEntries = (!cctx.isLocal() && cctx.config().getNearConfiguration() != null) ? new GridConcurrentSkipListSetEx() : null;
     }
 
+    /**
+     * @return {@code True} if eager ttl is enabled for cache.
+     */
+    public boolean eagerTtlEnabled() {
+        return eagerTtlEnabled;
+    }
+
     /** {@inheritDoc} */
     @Override protected void onKernalStop0(boolean cancel) {
         if (pendingEntries != null)
@@ -153,7 +170,6 @@ public class GridCacheTtlManager extends GridCacheManagerAdapter {
 
         try {
             if (pendingEntries != null) {
-                //todo may be not only for near? may be for local too.
                 GridNearCacheAdapter nearCache = cctx.near();
 
                 GridCacheVersion obsoleteVer = null;
@@ -178,7 +194,7 @@ public class GridCacheTtlManager extends GridCacheManagerAdapter {
                 }
             }
 
-            boolean more = cctx.offheap().expire(expireC, amount);
+            boolean more = cctx.offheap().expire(dhtCtx, expireC, amount);
 
             if (more)
                 return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
index 4dc5f8e..0b11900 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
@@ -467,7 +467,7 @@ public class GridCacheUtils {
      * @return All nodes on which cache with the same name is started.
      */
     public static Collection<ClusterNode> affinityNodes(final GridCacheContext ctx) {
-        return ctx.discovery().cacheAffinityNodes(ctx.cacheId(), AffinityTopologyVersion.NONE);
+        return ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), AffinityTopologyVersion.NONE);
     }
 
     /**
@@ -478,7 +478,7 @@ public class GridCacheUtils {
      * @return Affinity nodes.
      */
     public static Collection<ClusterNode> affinityNodes(GridCacheContext ctx, AffinityTopologyVersion topOrder) {
-        return ctx.discovery().cacheAffinityNodes(ctx.cacheId(), topOrder);
+        return ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), topOrder);
     }
 
     /**
@@ -989,6 +989,44 @@ public class GridCacheUtils {
             }
         }
     }
+    /**
+     * @param cfg1 Existing configuration.
+     * @param cfg2 Cache configuration to start.
+     * @param attrName Short attribute name for error message.
+     * @param attrMsg Full attribute name for error message.
+     * @param val1 Attribute value in existing configuration.
+     * @param val2 Attribute value in starting configuration.
+     * @param fail If true throws IgniteCheckedException in case of attribute values mismatch, otherwise logs warning.
+     * @throws IgniteCheckedException If validation failed.
+     */
+    public static void validateCacheGroupsAttributesMismatch(IgniteLogger log,
+        CacheConfiguration cfg1,
+        CacheConfiguration cfg2,
+        String attrName,
+        String attrMsg,
+        Object val1,
+        Object val2,
+        boolean fail) throws IgniteCheckedException {
+        if (F.eq(val1, val2))
+            return;
+
+        if (fail) {
+            throw new IgniteCheckedException(attrMsg + " mismatch for caches related to the same group " +
+                "[groupName=" + cfg1.getGroupName() +
+                ", existingCache=" + cfg1.getName() +
+                ", existing" + capitalize(attrName) + "=" + val1 +
+                ", startingCache=" + cfg2.getName() +
+                ", starting" + capitalize(attrName) + "=" + val2 + ']');
+        }
+        else {
+            U.warn(log, attrMsg + " mismatch for caches related to the same group " +
+                "[groupName=" + cfg1.getGroupName() +
+                ", existingCache=" + cfg1.getName() +
+                ", existing" + capitalize(attrName) + "=" + val1 +
+                ", startingCache=" + cfg2.getName() +
+                ", starting" + capitalize(attrName) + "=" + val2 + ']');
+        }
+    }
 
     /**
      * @param str String.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridChangeGlobalStateMessageResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridChangeGlobalStateMessageResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridChangeGlobalStateMessageResponse.java
index 0d04ccb..e49be49 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridChangeGlobalStateMessageResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridChangeGlobalStateMessageResponse.java
@@ -59,6 +59,16 @@ public class GridChangeGlobalStateMessageResponse extends GridCacheMessage {
         this.err = err;
     }
 
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
     /**
      *
      */
@@ -106,13 +116,13 @@ public class GridChangeGlobalStateMessageResponse extends GridCacheMessage {
         }
 
         switch (writer.state()) {
-            case 3:
+            case 2:
                 if (!writer.writeByteArray("errBytes", errBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 4:
+            case 3:
                 if (!writer.writeUuid("requestId", requestId))
                     return false;
 
@@ -134,7 +144,7 @@ public class GridChangeGlobalStateMessageResponse extends GridCacheMessage {
             return false;
 
         switch (reader.state()) {
-            case 3:
+            case 2:
                 errBytes = reader.readByteArray("errBytes");
 
                 if (!reader.isLastRead())
@@ -142,7 +152,7 @@ public class GridChangeGlobalStateMessageResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 4:
+            case 3:
                 requestId = reader.readUuid("requestId");
 
                 if (!reader.isLastRead())
@@ -162,7 +172,7 @@ public class GridChangeGlobalStateMessageResponse extends GridCacheMessage {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 5;
+        return 4;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridNoStorageCacheMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridNoStorageCacheMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridNoStorageCacheMap.java
index 5e52c8b..77a9ba4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridNoStorageCacheMap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridNoStorageCacheMap.java
@@ -22,30 +22,19 @@ import java.util.Collections;
 import java.util.Set;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
-import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * Empty cache map that will never store any entries.
  */
 public class GridNoStorageCacheMap implements GridCacheConcurrentMap {
-    /** Context. */
-    private final GridCacheContext ctx;
-
-    /**
-     * @param ctx Cache context.
-     */
-    public GridNoStorageCacheMap(GridCacheContext ctx) {
-        this.ctx = ctx;
-    }
-
     /** {@inheritDoc} */
-    @Nullable @Override public GridCacheMapEntry getEntry(KeyCacheObject key) {
+    @Nullable @Override public GridCacheMapEntry getEntry(GridCacheContext ctx, KeyCacheObject key) {
         return null;
     }
 
     /** {@inheritDoc} */
-    @Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(AffinityTopologyVersion topVer,
+    @Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(GridCacheContext ctx, AffinityTopologyVersion topVer,
         KeyCacheObject key,
         boolean create,
         boolean touch) {
@@ -66,32 +55,27 @@ public class GridNoStorageCacheMap implements GridCacheConcurrentMap {
     }
 
     /** {@inheritDoc} */
-    @Override public int publicSize() {
+    @Override public int publicSize(int cacheId) {
         return 0;
     }
 
     /** {@inheritDoc} */
-    @Override public void incrementPublicSize(GridCacheEntryEx e) {
+    @Override public void incrementPublicSize(CacheMapHolder hld, GridCacheEntryEx e) {
         // No-op.
     }
 
     /** {@inheritDoc} */
-    @Override public void decrementPublicSize(GridCacheEntryEx e) {
+    @Override public void decrementPublicSize(CacheMapHolder hld, GridCacheEntryEx e) {
         // No-op.
     }
 
     /** {@inheritDoc} */
-    @Override public Iterable<GridCacheMapEntry> entries(CacheEntryPredicate... filter) {
-        return Collections.emptySet();
-    }
-
-    /** {@inheritDoc} */
-    @Override public Iterable<GridCacheMapEntry> allEntries(CacheEntryPredicate... filter) {
+    @Override public Iterable<GridCacheMapEntry> entries(int cacheId, CacheEntryPredicate... filter) {
         return Collections.emptySet();
     }
 
     /** {@inheritDoc} */
-    @Override public Set<GridCacheMapEntry> entrySet(CacheEntryPredicate... filter) {
+    @Override public Set<GridCacheMapEntry> entrySet(int cacheId, CacheEntryPredicate... filter) {
         return Collections.emptySet();
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
index 5bcefda..8951396 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.cache;
 
+import java.util.Map;
 import javax.cache.Cache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
@@ -38,7 +39,36 @@ import org.jetbrains.annotations.Nullable;
  *
  */
 @SuppressWarnings("WeakerAccess")
-public interface IgniteCacheOffheapManager extends GridCacheManager {
+public interface IgniteCacheOffheapManager {
+    /**
+     * @param ctx Context.
+     * @param grp Cache group.
+     * @throws IgniteCheckedException If failed.
+     */
+    public void start(GridCacheSharedContext ctx, CacheGroupContext grp) throws IgniteCheckedException;;
+
+    /**
+     * @param cctx Cache context.
+     * @throws IgniteCheckedException If failed.
+     */
+    public void onCacheStarted(GridCacheContext cctx) throws IgniteCheckedException;
+
+    /**
+     *
+     */
+    public void onKernalStop();
+
+    /**
+     * @param cacheId Cache ID.
+     * @param destroy Destroy data flag.
+     */
+    public void stopCache(int cacheId, boolean destroy);
+
+    /**
+     *
+     */
+    public void stop();
+
     /**
      * Partition counter update callback. May be overridden by plugin-provided subclasses.
      *
@@ -71,11 +101,12 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
     @Nullable public CacheDataRow read(GridCacheMapEntry entry) throws IgniteCheckedException;
 
     /**
+     * @param cctx Cache context.
      * @param key Key.
      * @return Cached row, if available, null otherwise.
      * @throws IgniteCheckedException If failed.
      */
-    @Nullable public CacheDataRow read(KeyCacheObject key) throws IgniteCheckedException;
+    @Nullable public CacheDataRow read(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException;
 
     /**
      * @param p Partition.
@@ -97,6 +128,7 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
 
     /**
      * @param store Data store.
+     * @throws IgniteCheckedException If failed.
      */
     public void destroyCacheDataStore(CacheDataStore store) throws IgniteCheckedException;
 
@@ -106,10 +138,12 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
     public boolean containsKey(GridCacheMapEntry entry);
 
     /**
+     * @param cctx Cache context.
      * @param c Closure.
      * @throws IgniteCheckedException If failed.
      */
-    public boolean expire(IgniteInClosure2X<GridCacheEntryEx, GridCacheVersion> c, int amount) throws IgniteCheckedException;
+    public boolean expire(GridCacheContext cctx, IgniteInClosure2X<GridCacheEntryEx, GridCacheVersion> c, int amount)
+        throws IgniteCheckedException;
 
     /**
      * Gets the number of entries pending expire.
@@ -120,15 +154,17 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
     public long expiredSize() throws IgniteCheckedException;
 
     /**
+     * @param cctx Cache context.
      * @param key Key.
      * @param part Partition.
      * @param c Tree update closure.
      * @throws IgniteCheckedException If failed.
      */
-    public void invoke(KeyCacheObject key, GridDhtLocalPartition part, OffheapInvokeClosure c)
+    public void invoke(GridCacheContext cctx, KeyCacheObject key, GridDhtLocalPartition part, OffheapInvokeClosure c)
         throws IgniteCheckedException;
 
     /**
+     * @param cctx Cache context.
      * @param key  Key.
      * @param val  Value.
      * @param ver  Version.
@@ -138,6 +174,7 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
      * @throws IgniteCheckedException If failed.
      */
     public void update(
+        GridCacheContext cctx,
         KeyCacheObject key,
         CacheObject val,
         GridCacheVersion ver,
@@ -147,22 +184,26 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
     ) throws IgniteCheckedException;
 
     /**
+     * @param cctx Cache context.
      * @param key Key.
      * @param part Partition.
      * @throws IgniteCheckedException If failed.
      */
     public void updateIndexes(
+        GridCacheContext cctx,
         KeyCacheObject key,
         GridDhtLocalPartition part
     ) throws IgniteCheckedException;
 
     /**
+     * @param cctx Cache context.
      * @param key Key.
      * @param partId Partition number.
      * @param part Partition.
      * @throws IgniteCheckedException If failed.
      */
     public void remove(
+        GridCacheContext cctx,
         KeyCacheObject key,
         int partId,
         GridDhtLocalPartition part
@@ -175,24 +216,37 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
     public int onUndeploy(ClassLoader ldr);
 
     /**
+     * @param cacheId Cache ID.
      * @param primary Primary entries flag.
      * @param backup Backup entries flag.
      * @param topVer Topology version.
      * @return Rows iterator.
      * @throws IgniteCheckedException If failed.
      */
-    public GridIterator<CacheDataRow> iterator(boolean primary, boolean backup, final AffinityTopologyVersion topVer)
+    public GridIterator<CacheDataRow> cacheIterator(int cacheId,
+        boolean primary,
+        boolean backup,
+        final AffinityTopologyVersion topVer)
         throws IgniteCheckedException;
 
     /**
+     * @param cacheId Cache ID.
      * @param part Partition.
      * @return Partition data iterator.
      * @throws IgniteCheckedException If failed.
      */
-    public GridIterator<CacheDataRow> iterator(final int part) throws IgniteCheckedException;
+    public GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, final int part) throws IgniteCheckedException;
+
+    /**
+     * @param part Partition number.
+     * @return Iterator for given partition.
+     * @throws IgniteCheckedException If failed.
+     */
+    public GridIterator<CacheDataRow> partitionIterator(final int part) throws IgniteCheckedException;
 
     /**
      * @param part Partition.
+     * @param topVer Topology version.
      * @param partCntr Partition counter to get historical data if available.
      * @return Partition data iterator.
      * @throws IgniteCheckedException If failed.
@@ -201,6 +255,7 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
         throws IgniteCheckedException;
 
     /**
+     * @param cctx Cache context.
      * @param primary Primary entries flag.
      * @param backup Backup entries flag.
      * @param topVer Topology version.
@@ -208,40 +263,47 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
      * @return Entries iterator.
      * @throws IgniteCheckedException If failed.
      */
-    public <K, V> GridCloseableIterator<Cache.Entry<K, V>> entriesIterator(final boolean primary,
+    public <K, V> GridCloseableIterator<Cache.Entry<K, V>> cacheEntriesIterator(
+        GridCacheContext cctx,
+        final boolean primary,
         final boolean backup,
         final AffinityTopologyVersion topVer,
         final boolean keepBinary) throws IgniteCheckedException;
 
     /**
+     * @param cacheId Cache ID.
      * @param part Partition.
      * @return Iterator.
      * @throws IgniteCheckedException If failed.
      */
-    public GridCloseableIterator<KeyCacheObject> keysIterator(final int part) throws IgniteCheckedException;
+    public GridCloseableIterator<KeyCacheObject> cacheKeysIterator(int cacheId, final int part)
+        throws IgniteCheckedException;
 
     /**
+     * @param cacheId Cache ID.
      * @param primary Primary entries flag.
      * @param backup Backup entries flag.
      * @param topVer Topology version.
      * @return Entries count.
      * @throws IgniteCheckedException If failed.
      */
-    public long entriesCount(boolean primary, boolean backup, AffinityTopologyVersion topVer)
+    public long cacheEntriesCount(int cacheId, boolean primary, boolean backup, AffinityTopologyVersion topVer)
         throws IgniteCheckedException;
 
     /**
      * Clears offheap entries.
      *
+     * @param cctx Cache context.
      * @param readers {@code True} to clear readers.
      */
-    public void clear(boolean readers);
+    public void clearCache(GridCacheContext cctx, boolean readers);
 
     /**
+     * @param cacheId Cache ID.
      * @param part Partition.
      * @return Number of entries in given partition.
      */
-    public long entriesCount(int part);
+    public long cacheEntriesCount(int cacheId, int part);
 
     /**
      * @return Offheap allocated size.
@@ -254,28 +316,38 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
     public GridAtomicLong globalRemoveId();
 
     /**
+     * @param cacheId Cache ID.
      * @param idxName Index name.
      * @return Root page for index tree.
      * @throws IgniteCheckedException If failed.
      */
-    public RootPage rootPageForIndex(String idxName) throws IgniteCheckedException;
+    public RootPage rootPageForIndex(int cacheId, String idxName) throws IgniteCheckedException;
 
     /**
+     * @param cacheId Cache ID.
      * @param idxName Index name.
      * @throws IgniteCheckedException If failed.
      */
-    public void dropRootPageForIndex(String idxName) throws IgniteCheckedException;
+    public void dropRootPageForIndex(int cacheId, String idxName) throws IgniteCheckedException;
 
     /**
+     * @param idxName Index name.
      * @return Reuse list for index tree.
+     * @throws IgniteCheckedException If failed.
      */
     public ReuseList reuseListForIndex(String idxName) throws IgniteCheckedException;
 
     /**
-     *
+     * @param cacheId Cache ID.
+     * @return Number of entries.
+     */
+    public long cacheEntriesCount(int cacheId);
+
+    /**
+     * @param part Partition.
      * @return Number of entries.
      */
-    public long entriesCount();
+    public int totalPartitionEntriesCount(int part);
 
     /**
      *
@@ -304,13 +376,25 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
         /**
          * @param size Size to init.
          * @param updCntr Update counter to init.
+         * @param cacheSizes Cache sizes if store belongs to group containing multiple caches.
          */
-        void init(long size, long updCntr);
+        void init(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes);
 
         /**
+         * @param cacheId Cache ID.
          * @return Size.
          */
-        int size();
+        int cacheSize(int cacheId);
+
+        /**
+         * @return Cache sizes if store belongs to group containing multiple caches.
+         */
+        Map<Integer, Long> cacheSizes();
+
+        /**
+         * @return Total size.
+         */
+        int fullSize();
 
         /**
          * @return Update counter.
@@ -333,6 +417,7 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
         public Long initialUpdateCounter();
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @param val Value.
          * @param ver Version.
@@ -341,13 +426,16 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
          * @return New row.
          * @throws IgniteCheckedException If failed.
          */
-        CacheDataRow createRow(KeyCacheObject key,
+        CacheDataRow createRow(
+            GridCacheContext cctx,
+            KeyCacheObject key,
             CacheObject val,
             GridCacheVersion ver,
             long expireTime,
             @Nullable CacheDataRow oldRow) throws IgniteCheckedException;
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @param val Value.
          * @param ver Version.
@@ -355,38 +443,44 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
          * @param oldRow Old row if available.
          * @throws IgniteCheckedException If failed.
          */
-        void update(KeyCacheObject key,
+        void update(
+            GridCacheContext cctx,
+            KeyCacheObject key,
             CacheObject val,
             GridCacheVersion ver,
             long expireTime,
             @Nullable CacheDataRow oldRow) throws IgniteCheckedException;
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @throws IgniteCheckedException If failed.
          */
-        void updateIndexes(KeyCacheObject key) throws IgniteCheckedException;
+        void updateIndexes(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException;
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @param c Closure.
          * @throws IgniteCheckedException If failed.
          */
-        public void invoke(KeyCacheObject key, OffheapInvokeClosure c) throws IgniteCheckedException;
+        public void invoke(GridCacheContext cctx, KeyCacheObject key, OffheapInvokeClosure c) throws IgniteCheckedException;
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @param partId Partition number.
          * @throws IgniteCheckedException If failed.
          */
-        public void remove(KeyCacheObject key, int partId) throws IgniteCheckedException;
+        public void remove(GridCacheContext cctx, KeyCacheObject key, int partId) throws IgniteCheckedException;
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @return Data row.
          * @throws IgniteCheckedException If failed.
          */
-        public CacheDataRow find(KeyCacheObject key) throws IgniteCheckedException;
+        public CacheDataRow find(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException;
 
         /**
          * @return Data cursor.
@@ -395,15 +489,34 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
         public GridCursor<? extends CacheDataRow> cursor() throws IgniteCheckedException;
 
         /**
+         * @param cacheId Cache ID.
+         * @return Data cursor.
+         * @throws IgniteCheckedException If failed.
+         */
+        public GridCursor<? extends CacheDataRow> cursor(int cacheId) throws IgniteCheckedException;
+
+        /**
+         * @param cacheId Cache ID.
          * @param lower Lower bound.
          * @param upper Upper bound.
          * @return Data cursor.
          * @throws IgniteCheckedException If failed.
          */
-        public GridCursor<? extends CacheDataRow> cursor(KeyCacheObject lower,
+        public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
             KeyCacheObject upper) throws IgniteCheckedException;
 
         /**
+         * @param cacheId Cache ID.
+         * @param lower Lower bound.
+         * @param upper Upper bound.
+         * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
+         * @return Data cursor.
+         * @throws IgniteCheckedException If failed.
+         */
+        public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
+            KeyCacheObject upper, Object x) throws IgniteCheckedException;
+
+        /**
          * Destroys the tree associated with the store.
          *
          * @throws IgniteCheckedException If failed.
@@ -411,6 +524,14 @@ public interface IgniteCacheOffheapManager extends GridCacheManager {
         public void destroy() throws IgniteCheckedException;
 
         /**
+         * Clears all the records associated with logical cache with given ID.
+         *
+         * @param cacheId Cache ID.
+         * @throws IgniteCheckedException If failed.
+         */
+        public void clear(int cacheId) throws IgniteCheckedException;
+
+        /**
          * @return Row store.
          */
         public RowStore rowStore();


[02/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheOffheapManager.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheOffheapManager.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheOffheapManager.java
index 9bced42..3904205 100644
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheOffheapManager.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheOffheapManager.java
@@ -17,9 +17,11 @@
 
 package org.apache.ignite.internal.processors.cache.database;
 
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.ignite.IgniteCheckedException;
@@ -39,6 +41,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateNextSna
 import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdatePartitionDataRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionDestroyRecord;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl;
@@ -48,6 +51,7 @@ import org.apache.ignite.internal.processors.cache.database.freelist.FreeListImp
 import org.apache.ignite.internal.processors.cache.database.pagemem.PageMemoryEx;
 import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
 import org.apache.ignite.internal.processors.cache.database.tree.io.PageMetaIO;
+import org.apache.ignite.internal.processors.cache.database.tree.io.PagePartitionCountersIO;
 import org.apache.ignite.internal.processors.cache.database.tree.io.PagePartitionMetaIO;
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseListImpl;
@@ -56,6 +60,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalP
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridUnsafe;
 import org.apache.ignite.internal.util.lang.GridCursor;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.T2;
@@ -80,60 +85,70 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
         RootPage reuseListRoot = metas.reuseListRoot;
 
-        reuseList = new ReuseListImpl(cctx.cacheId(),
-            cctx.name(),
-            cctx.memoryPolicy().pageMemory(),
-            cctx.shared().wal(),
+        reuseList = new ReuseListImpl(grp.groupId(),
+            grp.cacheOrGroupName(),
+            grp.memoryPolicy().pageMemory(),
+            ctx.wal(),
             reuseListRoot.pageId().pageId(),
             reuseListRoot.isAllocated());
 
         RootPage metastoreRoot = metas.treeRoot;
 
-        metaStore = new MetadataStorage(cctx.memoryPolicy().pageMemory(),
-            cctx.shared().wal(),
+        metaStore = new MetadataStorage(grp.memoryPolicy().pageMemory(),
+            ctx.wal(),
             globalRemoveId(),
-            cctx.cacheId(),
+            grp.groupId(),
             PageIdAllocator.INDEX_PARTITION,
             PageIdAllocator.FLAG_IDX,
             reuseList,
             metastoreRoot.pageId().pageId(),
             metastoreRoot.isAllocated());
 
-        if (cctx.shared().ttl().eagerTtlEnabled()) {
-            final String name = "PendingEntries";
+        ((GridCacheDatabaseSharedManager)ctx.database()).addCheckpointListener(this);
+    }
 
-            RootPage pendingRootPage = metaStore.getOrAllocateForTree(name);
+    /** {@inheritDoc} */
+    @Override public void onCacheStarted(GridCacheContext cctx) throws IgniteCheckedException {
+        if (cctx.affinityNode() && cctx.ttl().eagerTtlEnabled() && pendingEntries == null) {
+            ctx.database().checkpointReadLock();
 
-            pendingEntries = new PendingEntriesTree(
-                cctx,
-                name,
-                cctx.memoryPolicy().pageMemory(),
-                pendingRootPage.pageId().pageId(),
-                reuseList,
-                pendingRootPage.isAllocated()
-            );
+            try {
+                final String name = "PendingEntries";
+
+                RootPage pendingRootPage = metaStore.getOrAllocateForTree(name);
+
+                pendingEntries = new PendingEntriesTree(
+                    grp,
+                    name,
+                    grp.memoryPolicy().pageMemory(),
+                    pendingRootPage.pageId().pageId(),
+                    reuseList,
+                    pendingRootPage.isAllocated()
+                );
+            }
+            finally {
+                ctx.database().checkpointReadUnlock();
+            }
         }
-
-        ((GridCacheDatabaseSharedManager)cctx.shared().database()).addCheckpointListener(this);
     }
 
     /** {@inheritDoc} */
     @Override protected CacheDataStore createCacheDataStore0(final int p)
         throws IgniteCheckedException {
-        GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager)cctx.shared().database();
+        GridCacheDatabaseSharedManager dbMgr = (GridCacheDatabaseSharedManager)ctx.database();
 
-        if (!cctx.allowFastEviction())
-            dbMgr.cancelOrWaitPartitionDestroy(cctx, p);
+        if (!grp.allowFastEviction())
+            dbMgr.cancelOrWaitPartitionDestroy(grp.groupId(), p);
 
-        boolean exists = cctx.shared().pageStore() != null
-            && cctx.shared().pageStore().exists(cctx.cacheId(), p);
+        boolean exists = ctx.pageStore() != null
+            && ctx.pageStore().exists(grp.groupId(), p);
 
         return new GridCacheDataStore(p, exists);
     }
 
     /** {@inheritDoc} */
     @Override public void onCheckpointBegin(Context ctx) throws IgniteCheckedException {
-        assert cctx.memoryPolicy().pageMemory() instanceof PageMemoryEx;
+        assert grp.memoryPolicy().pageMemory() instanceof PageMemoryEx;
 
         reuseList.saveMetadata();
 
@@ -167,11 +182,11 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
             freeList.saveMetadata();
 
             long updCntr = store.updateCounter();
-            int size = store.size();
+            int size = store.fullSize();
             long rmvId = globalRemoveId().get();
 
-            PageMemoryEx pageMem = (PageMemoryEx)cctx.memoryPolicy().pageMemory();
-            IgniteWriteAheadLogManager wal = cctx.shared().wal();
+            PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
+            IgniteWriteAheadLogManager wal = this.ctx.wal();
 
             if (size > 0 || updCntr > 0) {
                 int state = -1;
@@ -180,7 +195,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                     state = GridDhtPartitionState.EVICTED.ordinal();
                 else {
                     // localPartition will not acquire writeLock here because create=false.
-                    GridDhtLocalPartition part = cctx.topology().localPartition(store.partId(),
+                    GridDhtLocalPartition part = grp.topology().localPartition(store.partId(),
                         AffinityTopologyVersion.NONE, false);
 
                     if (part != null && part.state() != GridDhtPartitionState.EVICTED)
@@ -191,12 +206,12 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                 if (state == -1)
                     return false;
 
-                int cacheId = cctx.cacheId();
-                long partMetaId = pageMem.partitionMetaPageId(cacheId, store.partId());
-                long partMetaPage = pageMem.acquirePage(cacheId, partMetaId);
+                int grpId = grp.groupId();
+                long partMetaId = pageMem.partitionMetaPageId(grpId, store.partId());
+                long partMetaPage = pageMem.acquirePage(grpId, partMetaId);
 
                 try {
-                    long pageAddr = pageMem.writeLock(cacheId, partMetaId, partMetaPage);
+                    long pageAddr = pageMem.writeLock(grpId, partMetaId, partMetaPage);
 
                     try {
                         PagePartitionMetaIO io = PageIO.getPageIO(pageAddr);
@@ -207,69 +222,132 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                         io.setPartitionState(pageAddr, (byte)state);
 
-                        int pageCount;
+                        long cntrsPageId;
+
+                        if (grp.sharedGroup()) {
+                            cntrsPageId = io.getCountersPageId(pageAddr);
+
+                            byte[] data = serializeCacheSizes(store.cacheSizes());
+
+                            int items = data.length / 12;
+                            int written = 0;
+                            int pageSize = pageMem.pageSize();
+
+                            boolean init = cntrsPageId == 0;
+
+                            if (init) {
+                                cntrsPageId = pageMem.allocatePage(grpId, store.partId(), PageIdAllocator.FLAG_DATA);
+                                io.setCountersPageId(pageAddr, cntrsPageId);
+                            }
+
+                            long nextId = cntrsPageId;
+
+                            while (written != items) {
+                                final long curId = nextId;
+                                final long curPage = pageMem.acquirePage(grpId, curId);
+
+                                try {
+                                    final long curAddr = pageMem.writeLock(grpId, curId, curPage);
+
+                                    assert curAddr != 0;
+
+                                    try {
+                                        PagePartitionCountersIO partMetaIo;
+
+                                        if (init) {
+                                            partMetaIo = PagePartitionCountersIO.VERSIONS.latest();
+                                            partMetaIo.initNewPage(curAddr, curId, pageSize);
+                                        }
+                                        else
+                                            partMetaIo = PageIO.getPageIO(curAddr);
+
+                                        written += partMetaIo.writeCacheSizes(pageSize, curAddr, data, written);
+
+                                        nextId = partMetaIo.getNextCountersPageId(curAddr);
+
+                                        if(written != items && (init = nextId == 0)) {
+                                            //allocate new counters page
+                                            nextId = pageMem.allocatePage(grpId, store.partId(), PageIdAllocator.FLAG_DATA);
+                                            partMetaIo.setNextCountersPageId(curAddr, nextId);
+                                        }
+                                    }
+                                    finally {
+                                        // Write full page
+                                        pageMem.writeUnlock(grpId, curId, curPage, Boolean.TRUE, true);
+                                    }
+                                }
+                                finally {
+                                    pageMem.releasePage(grpId, curId, curPage);
+                                }
+                            }
+                        }
+                        else
+                            cntrsPageId = 0L;
+
+                        int pageCnt;
 
                         if (beforeSnapshot) {
-                            pageCount = cctx.shared().pageStore().pages(cctx.cacheId(), store.partId());
-                            io.setCandidatePageCount(pageAddr, pageCount);
+                            pageCnt = this.ctx.pageStore().pages(grpId, store.partId());
+                            io.setCandidatePageCount(pageAddr, pageCnt);
 
                             if (saveMeta) {
-                                long metaPageId = pageMem.metaPageId(cctx.cacheId());
-                                long metaPage = pageMem.acquirePage(cctx.cacheId(), metaPageId);
+                                long metaPageId = pageMem.metaPageId(grpId);
+                                long metaPage = pageMem.acquirePage(grpId, metaPageId);
 
                                 try {
-                                    long metaPageAddr = pageMem.writeLock(cctx.cacheId(), metaPageId, metaPage);
+                                    long metaPageAddr = pageMem.writeLock(grpId, metaPageId, metaPage);
 
                                     try {
                                         long nextSnapshotTag = io.getNextSnapshotTag(metaPageAddr);
                                         io.setNextSnapshotTag(metaPageAddr, nextSnapshotTag + 1);
 
-                                        if (PageHandler.isWalDeltaRecordNeeded(pageMem, cctx.cacheId(), metaPageId,
+                                        if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, metaPageId,
                                             metaPage, wal, null))
-                                            wal.log(new MetaPageUpdateNextSnapshotId(cctx.cacheId(), metaPageId,
+                                            wal.log(new MetaPageUpdateNextSnapshotId(grpId, metaPageId,
                                                 nextSnapshotTag + 1));
 
-                                        addPartition(ctx.partitionStatMap(), metaPageAddr, io, cctx.cacheId(), PageIdAllocator.INDEX_PARTITION,
-                                            cctx.kernalContext().cache().context().pageStore().pages(cacheId, PageIdAllocator.INDEX_PARTITION));
+                                        addPartition(ctx.partitionStatMap(), metaPageAddr, io, grpId, PageIdAllocator.INDEX_PARTITION,
+                                            this.ctx.kernalContext().cache().context().pageStore().pages(grpId, PageIdAllocator.INDEX_PARTITION));
                                     }
                                     finally {
-                                        pageMem.writeUnlock(cctx.cacheId(), metaPageId, metaPage, null, true);
+                                        pageMem.writeUnlock(grpId, metaPageId, metaPage, null, true);
                                     }
                                 }
                                 finally {
-                                    pageMem.releasePage(cctx.cacheId(), metaPageId, metaPage);
+                                    pageMem.releasePage(grpId, metaPageId, metaPage);
                                 }
 
                                 wasSaveToMeta = true;
                             }
 
-                            GridDhtPartitionMap partMap = cctx.topology().localPartitionMap();
+                            GridDhtPartitionMap partMap = grp.topology().localPartitionMap();
 
                             if (partMap.containsKey(store.partId()) &&
                                 partMap.get(store.partId()) == GridDhtPartitionState.OWNING)
-                                addPartition(ctx.partitionStatMap(), pageAddr, io, cctx.cacheId(), store.partId(),
-                                    cctx.kernalContext().cache().context().pageStore().pages(cctx.cacheId(), store.partId()));
+                                addPartition(ctx.partitionStatMap(), pageAddr, io, grpId, store.partId(),
+                                    this.ctx.pageStore().pages(grpId, store.partId()));
                         }
                         else
-                            pageCount = io.getCandidatePageCount(pageAddr);
+                            pageCnt = io.getCandidatePageCount(pageAddr);
 
-                        if (PageHandler.isWalDeltaRecordNeeded(pageMem, cacheId, partMetaId, partMetaPage, wal, null))
+                        if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, partMetaId, partMetaPage, wal, null))
                             wal.log(new MetaPageUpdatePartitionDataRecord(
-                                cacheId,
+                                grpId,
                                 partMetaId,
                                 updCntr,
                                 rmvId,
                                 size,
+                                cntrsPageId,
                                 (byte)state,
-                                pageCount
+                                pageCnt
                             ));
                     }
                     finally {
-                        pageMem.writeUnlock(cacheId, partMetaId, partMetaPage, null, true);
+                        pageMem.writeUnlock(grpId, partMetaId, partMetaPage, null, true);
                     }
                 }
                 finally {
-                    pageMem.releasePage(cacheId, partMetaId, partMetaPage);
+                    pageMem.releasePage(grpId, partMetaId, partMetaPage);
                 }
             }
         }
@@ -278,6 +356,23 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /**
+     * @param cacheSizes Cache sizes.
+     * @return Serialized cache sizes
+     */
+    private byte[] serializeCacheSizes(Map<Integer, Long> cacheSizes) {
+        // Item size = 4 bytes (cache ID) + 8 bytes (cache size) = 12 bytes
+        byte[] data = new byte[cacheSizes.size() * 12];
+        long off = GridUnsafe.BYTE_ARR_OFF;
+
+        for (Map.Entry<Integer, Long> entry : cacheSizes.entrySet()) {
+            GridUnsafe.putInt(data, off, entry.getKey()); off += 4;
+            GridUnsafe.putLong(data, off, entry.getValue()); off += 8;
+        }
+
+        return data;
+    }
+
+    /**
      * @param map Map to add values to.
      * @param pageAddr page address
      * @param io Page Meta IO
@@ -304,23 +399,23 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
     /** {@inheritDoc} */
     @Override protected void destroyCacheDataStore0(CacheDataStore store) throws IgniteCheckedException {
-        cctx.shared().database().checkpointReadLock();
+        ctx.database().checkpointReadLock();
 
         try {
             int p = store.partId();
 
             saveStoreMetadata(store, null, false, true);
 
-            PageMemoryEx pageMemory = (PageMemoryEx)cctx.memoryPolicy().pageMemory();
+            PageMemoryEx pageMemory = (PageMemoryEx)grp.memoryPolicy().pageMemory();
 
-            int tag = pageMemory.invalidate(cctx.cacheId(), p);
+            int tag = pageMemory.invalidate(grp.groupId(), p);
 
-            cctx.shared().wal().log(new PartitionDestroyRecord(cctx.cacheId(), p));
+            ctx.wal().log(new PartitionDestroyRecord(grp.groupId(), p));
 
-            cctx.shared().pageStore().onPartitionDestroyed(cctx.cacheId(), p, tag);
+            ctx.pageStore().onPartitionDestroyed(grp.groupId(), p, tag);
         }
         finally {
-            cctx.shared().database().checkpointReadUnlock();
+            ctx.database().checkpointReadUnlock();
         }
     }
 
@@ -354,12 +449,18 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
-    @Override public RootPage rootPageForIndex(String idxName) throws IgniteCheckedException {
+    @Override public RootPage rootPageForIndex(int cacheId, String idxName) throws IgniteCheckedException {
+        if (grp.sharedGroup())
+            idxName = Integer.toString(cacheId) + "_" + idxName;
+
         return metaStore.getOrAllocateForTree(idxName);
     }
 
     /** {@inheritDoc} */
-    @Override public void dropRootPageForIndex(String idxName) throws IgniteCheckedException {
+    @Override public void dropRootPageForIndex(int cacheId, String idxName) throws IgniteCheckedException {
+        if (grp.sharedGroup())
+            idxName = Integer.toString(cacheId) + "_" + idxName;
+
         metaStore.dropRootPage(idxName);
     }
 
@@ -369,10 +470,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
-    @Override protected void destroyCacheDataStructures() {
-        assert cctx.affinityNode();
-
-        ((GridCacheDatabaseSharedManager)cctx.shared().database()).removeCheckpointListener(this);
+    @Override public void stop() {
+        if (grp.affinityNode())
+            ((GridCacheDatabaseSharedManager)ctx.database()).removeCheckpointListener(this);
     }
 
     /**
@@ -380,14 +480,15 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
      * @throws IgniteCheckedException If failed.
      */
     private Metas getOrAllocateCacheMetas() throws IgniteCheckedException {
-        PageMemoryEx pageMem = (PageMemoryEx) cctx.memoryPolicy().pageMemory();
-        IgniteWriteAheadLogManager wal = cctx.shared().wal();
+        PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
+        IgniteWriteAheadLogManager wal = ctx.wal();
+
+        int grpId = grp.groupId();
+        long metaId = pageMem.metaPageId(grpId);
+        long metaPage = pageMem.acquirePage(grpId, metaId);
 
-        int cacheId = cctx.cacheId();
-        long metaId = pageMem.metaPageId(cacheId);
-        long metaPage = pageMem.acquirePage(cacheId, metaId);
         try {
-            final long pageAddr = pageMem.writeLock(cacheId, metaId, metaPage);
+            final long pageAddr = pageMem.writeLock(grpId, metaId, metaPage);
 
             boolean allocated = false;
 
@@ -399,15 +500,15 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                     pageIO.initNewPage(pageAddr, metaId, pageMem.pageSize());
 
-                    metastoreRoot = pageMem.allocatePage(cacheId, PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX);
-                    reuseListRoot = pageMem.allocatePage(cacheId, PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX);
+                    metastoreRoot = pageMem.allocatePage(grpId, PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX);
+                    reuseListRoot = pageMem.allocatePage(grpId, PageIdAllocator.INDEX_PARTITION, PageMemory.FLAG_IDX);
 
                     pageIO.setTreeRoot(pageAddr, metastoreRoot);
                     pageIO.setReuseListRoot(pageAddr, reuseListRoot);
 
-                    if (PageHandler.isWalDeltaRecordNeeded(pageMem, cacheId, metaId, metaPage, wal, null))
+                    if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, metaId, metaPage, wal, null))
                         wal.log(new MetaPageInitRecord(
-                            cacheId,
+                            grpId,
                             metaId,
                             pageIO.getType(),
                             pageIO.getVersion(),
@@ -427,15 +528,15 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                 }
 
                 return new Metas(
-                    new RootPage(new FullPageId(metastoreRoot, cacheId), allocated),
-                    new RootPage(new FullPageId(reuseListRoot, cacheId), allocated));
+                    new RootPage(new FullPageId(metastoreRoot, grpId), allocated),
+                    new RootPage(new FullPageId(reuseListRoot, grpId), allocated));
             }
             finally {
-                pageMem.writeUnlock(cacheId, metaId, metaPage, null, allocated);
+                pageMem.writeUnlock(grpId, metaId, metaPage, null, allocated);
             }
         }
         finally {
-            pageMem.releasePage(cacheId, metaId, metaPage);
+            pageMem.releasePage(grpId, metaId, metaPage);
         }
     }
 
@@ -445,10 +546,10 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         if (partCntrSince == null)
             return super.rebalanceIterator(part, topVer, partCntrSince);
 
-        GridCacheDatabaseSharedManager database = (GridCacheDatabaseSharedManager)cctx.shared().database();
+        GridCacheDatabaseSharedManager database = (GridCacheDatabaseSharedManager)ctx.database();
 
         try {
-            WALPointer startPtr = database.searchPartitionCounter(cctx, part, partCntrSince);
+            WALPointer startPtr = database.searchPartitionCounter(grp.groupId(), part, partCntrSince);
 
             if (startPtr == null) {
                 assert false : "partCntr=" + partCntrSince + ", reservations=" + S.toString(Map.class, database.reservedForPreloading());
@@ -456,9 +557,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                 return super.rebalanceIterator(part, topVer, partCntrSince);
             }
 
-            WALIterator it = cctx.shared().wal().replay(startPtr);
+            WALIterator it = ctx.wal().replay(startPtr);
 
-            return new RebalanceIteratorAdapter(cctx, it, part);
+            return new RebalanceIteratorAdapter(grp, it, part);
         }
         catch (IgniteCheckedException e) {
             U.warn(log, "Failed to create WAL-based rebalance iterator (a full partition will transferred to a " +
@@ -475,14 +576,14 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         /** Serial version uid. */
         private static final long serialVersionUID = 0L;
 
-        /** Cache context. */
-        private GridCacheContext cctx;
+        /** Cache group caches. */
+        private final Set<Integer> cacheGrpCaches;
 
         /** WAL iterator. */
-        private WALIterator walIt;
+        private final WALIterator walIt;
 
         /** Partition to scan. */
-        private int part;
+        private final int part;
 
         /** */
         private Iterator<DataEntry> entryIt;
@@ -491,12 +592,12 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         private CacheDataRow next;
 
         /**
-         * @param cctx Cache context.
+         * @param grp Cache group.
          * @param walIt WAL iterator.
          * @param part Partition ID.
          */
-        private RebalanceIteratorAdapter(GridCacheContext cctx, WALIterator walIt, int part) {
-            this.cctx = cctx;
+        private RebalanceIteratorAdapter(CacheGroupContext grp, WALIterator walIt, int part) {
+            this.cacheGrpCaches = grp.cacheIds();
             this.walIt = walIt;
             this.part = part;
 
@@ -571,8 +672,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                     while (entryIt.hasNext()) {
                         DataEntry entry = entryIt.next();
 
-                        if (entry.cacheId() == cctx.cacheId() &&
-                            entry.partitionId() == part) {
+                        if (entry.partitionId() == part && cacheGrpCaches.contains(entry.cacheId())) {
 
                             next = new DataEntryRow(entry);
 
@@ -745,7 +845,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                     return null;
             }
 
-            IgniteCacheDatabaseSharedManager dbMgr = cctx.shared().database();
+            IgniteCacheDatabaseSharedManager dbMgr = ctx.database();
 
             dbMgr.checkpointReadLock();
 
@@ -756,12 +856,12 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                     RootPage reuseRoot = metas.reuseListRoot;
 
                     freeList = new FreeListImpl(
-                        cctx.cacheId(),
-                        cctx.name() + "-" + partId,
-                        (MemoryMetricsImpl)cctx.memoryPolicy().memoryMetrics(),
-                        cctx.memoryPolicy(),
+                        grp.groupId(),
+                        grp.cacheOrGroupName() + "-" + partId,
+                        grp.memoryPolicy().memoryMetrics(),
+                        grp.memoryPolicy(),
                         null,
-                        cctx.shared().wal(),
+                        ctx.wal(),
                         reuseRoot.pageId().pageId(),
                         reuseRoot.isAllocated()) {
                         @Override protected long allocatePageNoReuse() throws IgniteCheckedException {
@@ -769,15 +869,15 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         }
                     };
 
-                    CacheDataRowStore rowStore = new CacheDataRowStore(cctx, freeList, partId);
+                    CacheDataRowStore rowStore = new CacheDataRowStore(grp, freeList, partId);
 
                     RootPage treeRoot = metas.treeRoot;
 
                     CacheDataTree dataTree = new CacheDataTree(
+                        grp,
                         name,
                         freeList,
                         rowStore,
-                        cctx,
                         treeRoot.pageId().pageId(),
                         treeRoot.isAllocated()) {
                         @Override protected long allocatePageNoReuse() throws IgniteCheckedException {
@@ -785,31 +885,72 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         }
                     };
 
-                    PageMemoryEx pageMem = (PageMemoryEx)cctx.memoryPolicy().pageMemory();
+                    PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
 
                     delegate0 = new CacheDataStoreImpl(partId, name, rowStore, dataTree);
 
-                    int cacheId = cctx.cacheId();
-                    long partMetaId = pageMem.partitionMetaPageId(cacheId, partId);
-                    long partMetaPage = pageMem.acquirePage(cacheId, partMetaId);
+                    int grpId = grp.groupId();
+                    long partMetaId = pageMem.partitionMetaPageId(grpId, partId);
+                    long partMetaPage = pageMem.acquirePage(grpId, partMetaId);
+
                     try {
-                        long pageAddr = pageMem.readLock(cacheId, partMetaId, partMetaPage);
+                        long pageAddr = pageMem.readLock(grpId, partMetaId, partMetaPage);
 
                         try {
                             if (PageIO.getType(pageAddr) != 0) {
                                 PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.latest();
 
-                                delegate0.init(io.getSize(pageAddr), io.getUpdateCounter(pageAddr));
+                                Map<Integer, Long> cacheSizes = null;
+
+                                if (grp.sharedGroup()) {
+                                    long cntrsPageId = io.getCountersPageId(pageAddr);
+
+                                    if (cntrsPageId != 0L) {
+                                        cacheSizes = new HashMap<>();
+
+                                        long nextId = cntrsPageId;
+
+                                        while (true){
+                                            final long curId = nextId;
+                                            final long curPage = pageMem.acquirePage(grpId, curId);
+
+                                            try {
+                                                final long curAddr = pageMem.readLock(grpId, curId, curPage);
+
+                                                assert curAddr != 0;
 
-                                cctx.offheap().globalRemoveId().setIfGreater(io.getGlobalRemoveId(pageAddr));
+                                                try {
+                                                    PagePartitionCountersIO cntrsIO = PageIO.getPageIO(curAddr);
+
+                                                    if (cntrsIO.readCacheSizes(curAddr, cacheSizes))
+                                                        break;
+
+                                                    nextId = cntrsIO.getNextCountersPageId(curAddr);
+
+                                                    assert nextId != 0;
+                                                }
+                                                finally {
+                                                    pageMem.readUnlock(grpId, curId, curPage);
+                                                }
+                                            }
+                                            finally {
+                                                pageMem.releasePage(grpId, curId, curPage);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                delegate0.init(io.getSize(pageAddr), io.getUpdateCounter(pageAddr), cacheSizes);
+
+                                globalRemoveId().setIfGreater(io.getGlobalRemoveId(pageAddr));
                             }
                         }
                         finally {
-                            pageMem.readUnlock(cacheId, partMetaId, partMetaPage);
+                            pageMem.readUnlock(grpId, partMetaId, partMetaPage);
                         }
                     }
                     finally {
-                        pageMem.releasePage(cacheId, partMetaId, partMetaPage);
+                        pageMem.releasePage(grpId, partMetaId, partMetaPage);
                     }
 
                     delegate = delegate0;
@@ -838,15 +979,15 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
          * @return Partition metas.
          */
         private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException {
-            PageMemoryEx pageMem = (PageMemoryEx)cctx.memoryPolicy().pageMemory();
-            IgniteWriteAheadLogManager wal = cctx.shared().wal();
+            PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
+            IgniteWriteAheadLogManager wal = ctx.wal();
 
-            int cacheId = cctx.cacheId();
-            long partMetaId = pageMem.partitionMetaPageId(cacheId, partId);
-            long partMetaPage = pageMem.acquirePage(cacheId, partMetaId);
+            int grpId = grp.groupId();
+            long partMetaId = pageMem.partitionMetaPageId(grpId, partId);
+            long partMetaPage = pageMem.acquirePage(grpId, partMetaId);
             try {
                 boolean allocated = false;
-                long pageAddr = pageMem.writeLock(cacheId, partMetaId, partMetaPage);
+                long pageAddr = pageMem.writeLock(grpId, partMetaId, partMetaPage);
 
                 try {
                     long treeRoot, reuseListRoot;
@@ -857,8 +998,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                         io.initNewPage(pageAddr, partMetaId, pageMem.pageSize());
 
-                        treeRoot = pageMem.allocatePage(cacheId, partId, PageMemory.FLAG_DATA);
-                        reuseListRoot = pageMem.allocatePage(cacheId, partId, PageMemory.FLAG_DATA);
+                        treeRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
+                        reuseListRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
 
                         assert PageIdUtils.flag(treeRoot) == PageMemory.FLAG_DATA;
                         assert PageIdUtils.flag(reuseListRoot) == PageMemory.FLAG_DATA;
@@ -866,9 +1007,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         io.setTreeRoot(pageAddr, treeRoot);
                         io.setReuseListRoot(pageAddr, reuseListRoot);
 
-                        if (PageHandler.isWalDeltaRecordNeeded(pageMem, cacheId, partMetaId, partMetaPage, wal, null))
+                        if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, partMetaId, partMetaPage, wal, null))
                             wal.log(new MetaPageInitRecord(
-                                cctx.cacheId(),
+                                grpId,
                                 partMetaId,
                                 io.getType(),
                                 io.getVersion(),
@@ -885,21 +1026,21 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         reuseListRoot = io.getReuseListRoot(pageAddr);
 
                         assert PageIdUtils.flag(treeRoot) == PageMemory.FLAG_DATA :
-                            U.hexLong(treeRoot) + ", part=" + partId + ", cacheId=" + cacheId;
+                            U.hexLong(treeRoot) + ", part=" + partId + ", grpId=" + grpId;
                         assert PageIdUtils.flag(reuseListRoot) == PageMemory.FLAG_DATA :
-                            U.hexLong(reuseListRoot) + ", part=" + partId + ", cacheId=" + cacheId;
+                            U.hexLong(reuseListRoot) + ", part=" + partId + ", grpId=" + grpId;
                     }
 
                     return new Metas(
-                        new RootPage(new FullPageId(treeRoot, cacheId), allocated),
-                        new RootPage(new FullPageId(reuseListRoot, cacheId), allocated));
+                        new RootPage(new FullPageId(treeRoot, grpId), allocated),
+                        new RootPage(new FullPageId(reuseListRoot, grpId), allocated));
                 }
                 finally {
-                    pageMem.writeUnlock(cacheId, partMetaId, partMetaPage, null, allocated);
+                    pageMem.writeUnlock(grpId, partMetaId, partMetaPage, null, allocated);
                 }
             }
             finally {
-                pageMem.releasePage(cacheId, partMetaId, partMetaPage);
+                pageMem.releasePage(grpId, partMetaId, partMetaPage);
             }
         }
 
@@ -921,11 +1062,35 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public int size() {
+        @Override public int fullSize() {
+            try {
+                CacheDataStore delegate0 = init0(true);
+
+                return delegate0 == null ? 0 : delegate0.fullSize();
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public int cacheSize(int cacheId) {
+            try {
+                CacheDataStore delegate0 = init0(true);
+
+                return delegate0 == null ? 0 : delegate0.cacheSize(cacheId);
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public Map<Integer, Long> cacheSizes() {
             try {
                 CacheDataStore delegate0 = init0(true);
 
-                return delegate0 == null ? 0 : delegate0.size();
+                return delegate0 == null ? null : delegate0.cacheSizes();
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -945,7 +1110,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public void init(long size, long updCntr) {
+        @Override public void init(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes) {
             throw new IllegalStateException("Should be never called.");
         }
 
@@ -1001,6 +1166,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
         /** {@inheritDoc} */
         @Override public void update(
+            GridCacheContext cctx,
             KeyCacheObject key,
             CacheObject val,
             GridCacheVersion ver,
@@ -1009,47 +1175,51 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         ) throws IgniteCheckedException {
             CacheDataStore delegate = init0(false);
 
-            delegate.update(key, val, ver, expireTime, oldRow);
+            delegate.update(cctx, key, val, ver, expireTime, oldRow);
         }
 
         /** {@inheritDoc} */
-        @Override public void updateIndexes(KeyCacheObject key) throws IgniteCheckedException {
+        @Override public void updateIndexes(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
             CacheDataStore delegate = init0(false);
 
-            delegate.updateIndexes(key);
+            delegate.updateIndexes(cctx, key);
         }
 
         /** {@inheritDoc} */
-        @Override public CacheDataRow createRow(KeyCacheObject key,
+        @Override public CacheDataRow createRow(
+            GridCacheContext cctx,
+            KeyCacheObject key,
             CacheObject val,
             GridCacheVersion ver,
             long expireTime,
             @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
             CacheDataStore delegate = init0(false);
 
-            return delegate.createRow(key, val, ver, expireTime, oldRow);
+            return delegate.createRow(cctx, key, val, ver, expireTime, oldRow);
         }
 
         /** {@inheritDoc} */
-        @Override public void invoke(KeyCacheObject key, OffheapInvokeClosure c) throws IgniteCheckedException {
+        @Override public void invoke(GridCacheContext cctx, KeyCacheObject key, OffheapInvokeClosure c)
+            throws IgniteCheckedException {
             CacheDataStore delegate = init0(false);
 
-            delegate.invoke(key, c);
+            delegate.invoke(cctx, key, c);
         }
 
         /** {@inheritDoc} */
-        @Override public void remove(KeyCacheObject key, int partId) throws IgniteCheckedException {
+        @Override public void remove(GridCacheContext cctx, KeyCacheObject key, int partId)
+            throws IgniteCheckedException {
             CacheDataStore delegate = init0(false);
 
-            delegate.remove(key, partId);
+            delegate.remove(cctx, key, partId);
         }
 
         /** {@inheritDoc} */
-        @Override public CacheDataRow find(KeyCacheObject key) throws IgniteCheckedException {
+        @Override public CacheDataRow find(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
             CacheDataStore delegate = init0(true);
 
             if (delegate != null)
-                return delegate.find(key);
+                return delegate.find(cctx, key);
 
             return null;
         }
@@ -1065,12 +1235,28 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public GridCursor<? extends CacheDataRow> cursor(KeyCacheObject lower,
+        @Override public GridCursor<? extends CacheDataRow> cursor(
+            int cacheId,
+            KeyCacheObject lower,
             KeyCacheObject upper) throws IgniteCheckedException {
             CacheDataStore delegate = init0(true);
 
             if (delegate != null)
-                return delegate.cursor(lower, upper);
+                return delegate.cursor(cacheId, lower, upper);
+
+            return EMPTY_CURSOR;
+        }
+
+        /** {@inheritDoc} */
+        @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId,
+            KeyCacheObject lower,
+            KeyCacheObject upper,
+            Object x)
+            throws IgniteCheckedException {
+            CacheDataStore delegate = init0(true);
+
+            if (delegate != null)
+                return delegate.cursor(cacheId, lower, upper, x);
 
             return EMPTY_CURSOR;
         }
@@ -1079,6 +1265,24 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         @Override public void destroy() throws IgniteCheckedException {
             // No need to destroy delegate.
         }
+
+        /** {@inheritDoc} */
+        @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId) throws IgniteCheckedException {
+            CacheDataStore delegate = init0(true);
+
+            if (delegate != null)
+                return delegate.cursor(cacheId);
+
+            return EMPTY_CURSOR;
+        }
+
+        /** {@inheritDoc} */
+        @Override public void clear(int cacheId) throws IgniteCheckedException {
+            CacheDataStore delegate = init0(true);
+
+            if (delegate != null)
+                delegate.clear(cacheId);
+        }
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/file/FilePageStoreManager.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/file/FilePageStoreManager.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/file/FilePageStoreManager.java
index c233b1e..1343589 100755
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/file/FilePageStoreManager.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/file/FilePageStoreManager.java
@@ -29,7 +29,7 @@ import java.nio.ByteBuffer;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -43,10 +43,10 @@ import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
 import org.apache.ignite.internal.pagemem.store.PageStore;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.CacheGroupDescriptor;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedManagerAdapter;
 import org.apache.ignite.internal.processors.cache.database.IgniteCacheSnapshotManager;
-import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.marshaller.Marshaller;
 import org.apache.ignite.marshaller.jdk.JdkMarshaller;
@@ -72,6 +72,9 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     public static final String CACHE_DIR_PREFIX = "cache-";
 
     /** */
+    public static final String CACHE_GRP_DIR_PREFIX = "cacheGroup-";
+
+    /** */
     public static final String CACHE_CONF_FILENAME = "conf.dat";
 
     /** Marshaller. */
@@ -93,7 +96,7 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     private final long metaPageId = PageIdUtils.pageId(-1, PageMemory.FLAG_IDX, 0);
 
     /** */
-    private final Set<Integer> cachesWithoutIdx = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
+    private final Set<Integer> grpsWithoutIdx = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
 
     /**
      * @param ctx Kernal context.
@@ -189,25 +192,59 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /** {@inheritDoc} */
-    @Override public void initializeForCache(CacheConfiguration ccfg) throws IgniteCheckedException {
-        int cacheId = CU.cacheId(ccfg.getName());
+    @Override public void initializeForCache(CacheGroupDescriptor grpDesc, CacheConfiguration ccfg)
+        throws IgniteCheckedException {
+        int grpId = grpDesc.groupId();
 
-        if (!idxCacheStores.containsKey(cacheId)) {
-            CacheStoreHolder holder = initForCache(ccfg);
+        if (!idxCacheStores.containsKey(grpId)) {
+            CacheStoreHolder holder = initForCache(grpDesc, ccfg);
 
-            CacheStoreHolder old = idxCacheStores.put(cacheId, holder);
+            CacheStoreHolder old = idxCacheStores.put(grpId, holder);
 
             assert old == null : "Non-null old store holder for cache: " + ccfg.getName();
         }
+
+        storeCacheConfiguration(grpDesc, ccfg);
+    }
+
+    /**
+     * @param grpDesc Cache group descriptor.
+     * @param ccfg Cache configuration.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void storeCacheConfiguration(CacheGroupDescriptor grpDesc, CacheConfiguration ccfg)
+        throws IgniteCheckedException {
+        File cacheWorkDir = cacheWorkDirectory(grpDesc, ccfg);
+        File file;
+
+        assert cacheWorkDir.exists() : "Work directory does not exist: " + cacheWorkDir;
+
+        if (grpDesc.sharedGroup())
+            file = new File(cacheWorkDir, ccfg.getName() + CACHE_CONF_FILENAME);
+        else
+            file = new File(cacheWorkDir, CACHE_CONF_FILENAME);
+
+        if (!file.exists() || file.length() == 0) {
+            try {
+                file.createNewFile();
+
+                try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
+                    marshaller.marshal(ccfg, stream);
+                }
+            }
+            catch (IOException ex) {
+                throw new IgniteCheckedException("Failed to persist cache configuration: " + ccfg.getName(), ex);
+            }
+        }
     }
 
     /** {@inheritDoc} */
-    @Override public void shutdownForCache(GridCacheContext cacheCtx, boolean destroy) throws IgniteCheckedException {
-        cachesWithoutIdx.remove(cacheCtx.cacheId());
+    @Override public void shutdownForCacheGroup(CacheGroupContext grp, boolean destroy) throws IgniteCheckedException {
+        grpsWithoutIdx.remove(grp.groupId());
 
-        CacheStoreHolder old = idxCacheStores.remove(cacheCtx.cacheId());
+        CacheStoreHolder old = idxCacheStores.remove(grp.groupId());
 
-        assert old != null : "Missing cache store holder [cache=" + cacheCtx.name() +
+        assert old != null : "Missing cache store holder [cache=" + grp.cacheOrGroupName() +
             ", locNodeId=" + cctx.localNodeId() + ", gridName=" + cctx.igniteInstanceName() + ']';
 
         IgniteCheckedException ex = shutdown(old, /*clean files if destroy*/destroy, null);
@@ -217,17 +254,17 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /** {@inheritDoc} */
-    @Override public void onPartitionCreated(int cacheId, int partId) throws IgniteCheckedException {
+    @Override public void onPartitionCreated(int grpId, int partId) throws IgniteCheckedException {
         // No-op.
     }
 
     /** {@inheritDoc} */
-    @Override public void onPartitionDestroyed(int cacheId, int partId, int tag) throws IgniteCheckedException {
+    @Override public void onPartitionDestroyed(int grpId, int partId, int tag) throws IgniteCheckedException {
         assert partId <= PageIdAllocator.MAX_PARTITION_ID;
 
-        PageStore store = getStore(cacheId, partId);
+        PageStore store = getStore(grpId, partId);
 
-        assert store instanceof FilePageStore;
+        assert store instanceof FilePageStore : store;
 
         ((FilePageStore)store).truncate(tag);
     }
@@ -253,8 +290,8 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /** {@inheritDoc} */
-    @Override public boolean exists(int cacheId, int partId) throws IgniteCheckedException {
-        PageStore store = getStore(cacheId, partId);
+    @Override public boolean exists(int grpId, int partId) throws IgniteCheckedException {
+        PageStore store = getStore(grpId, partId);
 
         return store.exists();
     }
@@ -296,12 +333,31 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /**
+     * @param grpDesc Cache group descriptor.
+     * @param ccfg Cache configuration.
+     * @return Cache work directory.
+     */
+    private File cacheWorkDirectory(CacheGroupDescriptor grpDesc, CacheConfiguration ccfg) {
+        String dirName;
+
+        if (grpDesc.sharedGroup())
+            dirName = CACHE_GRP_DIR_PREFIX + ccfg.getGroupName();
+        else
+            dirName = CACHE_DIR_PREFIX + ccfg.getName();
+
+        return new File(storeWorkDir, dirName);
+    }
+
+    /**
+     * @param grpDesc Cache group descriptor.
      * @param ccfg Cache configuration.
      * @return Cache store holder.
      * @throws IgniteCheckedException If failed.
      */
-    private CacheStoreHolder initForCache(CacheConfiguration ccfg) throws IgniteCheckedException {
-        File cacheWorkDir = new File(storeWorkDir, CACHE_DIR_PREFIX + ccfg.getName());
+    private CacheStoreHolder initForCache(CacheGroupDescriptor grpDesc, CacheConfiguration ccfg) throws IgniteCheckedException {
+        assert !grpDesc.sharedGroup() || ccfg.getGroupName() != null : ccfg.getName();
+
+        File cacheWorkDir = cacheWorkDirectory(grpDesc, ccfg);
 
         boolean dirExisted = false;
 
@@ -358,32 +414,17 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
                 dirExisted = true;
         }
 
-        File file = new File(cacheWorkDir, CACHE_CONF_FILENAME);
-
-        if (!file.exists() || file.length() == 0) {
-            try {
-                file.createNewFile();
-
-                try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
-                    marshaller.marshal(ccfg, stream);
-                }
-            }
-            catch (IOException ex) {
-                throw new IgniteCheckedException("Failed to persist cache configuration: " + ccfg.getName(), ex);
-            }
-        }
-
         File idxFile = new File(cacheWorkDir, INDEX_FILE_NAME);
 
         if (dirExisted && !idxFile.exists())
-            cachesWithoutIdx.add(CU.cacheId(ccfg.getName()));
+            grpsWithoutIdx.add(grpDesc.groupId());
 
         FilePageStore idxStore = new FilePageStore(
             PageMemory.FLAG_IDX,
             idxFile,
             cctx.kernalContext().config().getMemoryConfiguration());
 
-        FilePageStore[] partStores = new FilePageStore[ccfg.getAffinity().partitions()];
+        FilePageStore[] partStores = new FilePageStore[grpDesc.config().getAffinity().partitions()];
 
         for (int partId = 0; partId < partStores.length; partId++) {
             FilePageStore partStore = new FilePageStore(
@@ -432,52 +473,74 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /** {@inheritDoc} */
-    @Override public Set<String> savedCacheNames() {
+    @Override public Map<String, CacheConfiguration> readCacheConfigurations() throws IgniteCheckedException {
         if (cctx.kernalContext().clientNode())
-            return Collections.emptySet();
+            return Collections.emptyMap();
 
         File[] files = storeWorkDir.listFiles();
 
         if (files == null)
-            return Collections.emptySet();
+            return Collections.emptyMap();
 
-        Set<String> cacheNames = new HashSet<>();
+        Map<String, CacheConfiguration> ccfgs = new HashMap<>();
 
         for (File file : files) {
-            if (file.isDirectory() && file.getName().startsWith(CACHE_DIR_PREFIX)) {
-                File conf = new File(file, CACHE_CONF_FILENAME);
-                if (conf.exists() && conf.length() > 0) {
-                    String name = file.getName().substring(CACHE_DIR_PREFIX.length());
+            if (file.isDirectory()) {
+                if (file.getName().startsWith(CACHE_DIR_PREFIX)) {
+                    File conf = new File(file, CACHE_CONF_FILENAME);
 
-                    // TODO remove when fixed null cache names.
-                    if ("null".equals(name))
-                        name = null;
+                    if (conf.exists() && conf.length() > 0) {
+                        CacheConfiguration ccfg = readCacheConfig(conf);
 
-                    cacheNames.add(name);
+                        ccfgs.put(ccfg.getName(), ccfg);
+                    }
                 }
+                else if (file.getName().startsWith(CACHE_GRP_DIR_PREFIX))
+                    readCacheGroupCaches(file, ccfgs);
             }
         }
 
-        return cacheNames;
+        return ccfgs;
     }
 
-    /** {@inheritDoc} */
-    @Override public CacheConfiguration readConfiguration(String cacheName) {
-        File file = new File(storeWorkDir, CACHE_DIR_PREFIX + cacheName);
+    /**
+     * @param grpDir Group directory.
+     * @param ccfgs Cache configurations.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void readCacheGroupCaches(File grpDir, Map<String, CacheConfiguration> ccfgs) throws IgniteCheckedException {
+        File[] files = grpDir.listFiles();
 
-        assert file.exists() && file.isDirectory();
+        if (files == null)
+            return;
 
-        try (InputStream stream = new BufferedInputStream(new FileInputStream(new File(file, CACHE_CONF_FILENAME)))) {
+        for (File file : files) {
+            if (!file.isDirectory() && file.getName().endsWith(CACHE_CONF_FILENAME) && file.length() > 0) {
+                CacheConfiguration ccfg = readCacheConfig(file);
+
+                ccfgs.put(ccfg.getName(), ccfg);
+            }
+        }
+    }
+
+    /**
+     * @param conf File with stored configuration.
+     * @return Cache configuration.
+     * @throws IgniteCheckedException If failed.
+     */
+    private CacheConfiguration readCacheConfig(File conf) throws IgniteCheckedException {
+        try (InputStream stream = new BufferedInputStream(new FileInputStream(conf))) {
             return marshaller.unmarshal(stream, U.resolveClassLoader(igniteCfg));
         }
-        catch (IOException | IgniteCheckedException e) {
-            throw new IllegalStateException("Failed to read cache configuration from disk for cache: " + cacheName, e);
+        catch (IOException e) {
+            throw new IgniteCheckedException("Failed to read cache configuration from disk for cache: " +
+                conf.getAbsolutePath(), e);
         }
     }
 
     /** {@inheritDoc} */
-    @Override public boolean hasIndexStore(int cacheId) {
-        return !cachesWithoutIdx.contains(cacheId);
+    @Override public boolean hasIndexStore(int grpId) {
+        return !grpsWithoutIdx.contains(grpId);
     }
 
     /**
@@ -488,11 +551,14 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /**
-     * @param cacheName Cache name.
+     * @param ccfg Cache configuration.
      * @return Store dir for given cache.
      */
-    public File cacheWorkDir(String cacheName) {
-        return new File(storeWorkDir, "cache-" + cacheName);
+    public File cacheWorkDir(CacheConfiguration ccfg) {
+        String dirName = ccfg.getGroupName() == null ?
+            CACHE_DIR_PREFIX + ccfg.getName() : CACHE_GRP_DIR_PREFIX + ccfg.getGroupName();
+
+        return new File(storeWorkDir, dirName);
     }
 
     /**
@@ -547,19 +613,19 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param partId Partition ID.
      * @return Page store for the corresponding parameters.
      * @throws IgniteCheckedException If cache or partition with the given ID was not created.
      *
      * Note: visible for testing.
      */
-    public PageStore getStore(int cacheId, int partId) throws IgniteCheckedException {
-        CacheStoreHolder holder = idxCacheStores.get(cacheId);
+    public PageStore getStore(int grpId, int partId) throws IgniteCheckedException {
+        CacheStoreHolder holder = idxCacheStores.get(grpId);
 
         if (holder == null)
             throw new IgniteCheckedException("Failed to get page store for the given cache ID " +
-                "(cache has not been started): " + cacheId);
+                "(cache has not been started): " + grpId);
 
         if (partId == PageIdAllocator.INDEX_PARTITION)
             return holder.idxStore;
@@ -571,7 +637,7 @@ public class FilePageStoreManager extends GridCacheSharedManagerAdapter implemen
 
         if (store == null)
             throw new IgniteCheckedException("Failed to get page store for the given partition ID " +
-                "(partition has not been created) [cacheId=" + cacheId + ", partId=" + partId + ']');
+                "(partition has not been created) [grpId=" + grpId + ", partId=" + partId + ']');
 
         return store;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
index e8ae554..ef84d83 100644
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
@@ -131,16 +131,16 @@ public interface PageMemoryEx extends PageMemory {
     public int invalidate(int cacheId, int partId);
 
     /**
-     * Clears internal metadata of destroyed cache.
+     * Clears internal metadata of destroyed cache group.
      *
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      */
-    public void onCacheDestroyed(int cacheId);
+    public void onCacheGroupDestroyed(int grpId);
 
     /**
      * Asynchronously clears pages satisfying the given predicate.
      *
-     * @param pred Predicate for cacheId, pageId and partition tag.
+     * @param pred Predicate for cache group id, pageId and partition tag.
      * @param cleanDirty Flag indicating that dirty pages collection should be cleaned.
      * @return Future that will be completed when all pages are cleared.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryImpl.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryImpl.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryImpl.java
index c05af57..f807229 100755
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryImpl.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryImpl.java
@@ -960,12 +960,12 @@ public class PageMemoryImpl implements PageMemoryEx {
     }
 
     /** {@inheritDoc} */
-    @Override public void onCacheDestroyed(int cacheId) {
+    @Override public void onCacheGroupDestroyed(int grpId) {
         for (Segment seg : segments) {
             seg.writeLock().lock();
 
             try {
-                seg.resetPartTags(cacheId);
+                seg.resetPartTags(grpId);
             }
             finally {
                 seg.writeLock().unlock();
@@ -1892,9 +1892,9 @@ public class PageMemoryImpl implements PageMemoryEx {
         }
 
         /**
-         * @param cacheId Cache id.
+         * @param grpId Cache group id.
          */
-        private void resetPartTags(int cacheId) {
+        private void resetPartTags(int grpId) {
             assert getWriteHoldCount() > 0;
 
             Iterator<T2<Integer, Integer>> iter = partTagMap.keySet().iterator();
@@ -1902,7 +1902,7 @@ public class PageMemoryImpl implements PageMemoryEx {
             while (iter.hasNext()) {
                 T2<Integer, Integer> t = iter.next();
 
-                if (t.get1() == cacheId)
+                if (t.get1() == grpId)
                     iter.remove();
             }
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/wal/serializer/RecordV1Serializer.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/wal/serializer/RecordV1Serializer.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/wal/serializer/RecordV1Serializer.java
index f67f617..4f532ae 100644
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/wal/serializer/RecordV1Serializer.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/wal/serializer/RecordV1Serializer.java
@@ -197,6 +197,7 @@ public class RecordV1Serializer implements RecordSerializer {
                 buf.putLong(partDataRec.updateCounter());
                 buf.putLong(partDataRec.globalRemoveId());
                 buf.putInt(partDataRec.partitionSize());
+                buf.putLong(partDataRec.countersPageId());
                 buf.put(partDataRec.state());
                 buf.putInt(partDataRec.allocatedIndexCandidate());
 
@@ -222,7 +223,7 @@ public class RecordV1Serializer implements RecordSerializer {
                     buf.putInt(walPtr.length());
                 }
 
-                putCacheStates(buf, cpRec.cacheStates());
+                putCacheStates(buf, cpRec.cacheGroupStates());
 
                 buf.put(cpRec.end() ? (byte)1 : 0);
 
@@ -730,7 +731,7 @@ public class RecordV1Serializer implements RecordSerializer {
 
                 CheckpointRecord cpRec = new CheckpointRecord(new UUID(msb, lsb), walPtr, end);
 
-                cpRec.cacheStates(states);
+                cpRec.cacheGroupStates(states);
 
                 res = cpRec;
 
@@ -756,10 +757,11 @@ public class RecordV1Serializer implements RecordSerializer {
                 long updCntr = in.readLong();
                 long rmvId = in.readLong();
                 int partSize = in.readInt();
+                long countersPageId = in.readLong();
                 byte state = in.readByte();
                 int allocatedIdxCandidate = in.readInt();
 
-                res = new MetaPageUpdatePartitionDataRecord(cacheId, pageId, updCntr, rmvId, partSize, state, allocatedIdxCandidate);
+                res = new MetaPageUpdatePartitionDataRecord(cacheId, pageId, updCntr, rmvId, partSize, countersPageId, state, allocatedIdxCandidate);
 
                 break;
 
@@ -1234,7 +1236,7 @@ public class RecordV1Serializer implements RecordSerializer {
                 assert cpRec.checkpointMark() == null || cpRec.checkpointMark() instanceof FileWALPointer :
                     "Invalid WAL record: " + cpRec;
 
-                int cacheStatesSize = cacheStatesSize(cpRec.cacheStates());
+                int cacheStatesSize = cacheStatesSize(cpRec.cacheGroupStates());
 
                 FileWALPointer walPtr = (FileWALPointer)cpRec.checkpointMark();
 
@@ -1244,7 +1246,7 @@ public class RecordV1Serializer implements RecordSerializer {
                 return 1 + /*cache ID*/4 + /*page ID*/8 + /*ioType*/2  + /*ioVer*/2 +  /*tree root*/8 + /*reuse root*/8 +  /*CRC*/4;
 
             case PARTITION_META_PAGE_UPDATE_COUNTERS:
-                return 1 + /*cache ID*/4 + /*page ID*/8 + /*upd cntr*/8 + /*rmv id*/8 + /*part size*/4 + /*state*/ 1
+                return 1 + /*cache ID*/4 + /*page ID*/8 + /*upd cntr*/8 + /*rmv id*/8 + /*part size*/4 + /*counters page id*/8 + /*state*/ 1
                     + /*allocatedIdxCandidate*/ 4 + /*CRC*/4;
 
             case MEMORY_RECOVERY:

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreCacheGroupsTest.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreCacheGroupsTest.java b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreCacheGroupsTest.java
new file mode 100644
index 0000000..7a6b34c
--- /dev/null
+++ b/modules/pds/src/test/java/org/apache/ignite/cache/database/IgnitePersistentStoreCacheGroupsTest.java
@@ -0,0 +1,516 @@
+/*
+ * 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.cache.database;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+import javax.cache.Cache;
+import javax.cache.expiry.ExpiryPolicy;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.query.SqlQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.BinaryConfiguration;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.MemoryConfiguration;
+import org.apache.ignite.configuration.PersistentStoreConfiguration;
+import org.apache.ignite.internal.processors.cache.database.wal.FileWriteAheadLogManager;
+import org.apache.ignite.internal.processors.platform.cache.expiry.PlatformExpiryPolicy;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+
+/**
+ *
+ */
+public class IgnitePersistentStoreCacheGroupsTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String GROUP1 = "grp1";
+
+    /** */
+    private static final String GROUP2 = "grp2";
+
+    /** */
+    private CacheConfiguration[] ccfgs;
+
+    /** */
+    private boolean activeOnStart = true;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        System.setProperty(FileWriteAheadLogManager.IGNITE_PDS_WAL_MODE, "LOG_ONLY");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        System.clearProperty(FileWriteAheadLogManager.IGNITE_PDS_WAL_MODE);
+
+        GridTestUtils.deleteDbFiles();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        GridTestUtils.deleteDbFiles();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+        cfg.setConsistentId(gridName);
+
+        cfg.setActiveOnStart(activeOnStart);
+
+        MemoryConfiguration memCfg = new MemoryConfiguration();
+        memCfg.setPageSize(1024);
+        memCfg.setDefaultMemoryPolicySize(10 * 1024 * 1024);
+
+        cfg.setMemoryConfiguration(memCfg);
+
+        cfg.setPersistentStoreConfiguration(new PersistentStoreConfiguration());
+
+        cfg.setBinaryConfiguration(new BinaryConfiguration().setCompactFooter(false));
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
+
+        if (ccfgs != null) {
+            cfg.setCacheConfiguration(ccfgs);
+
+            ccfgs = null;
+        }
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        GridTestUtils.deleteDbFiles();
+
+        super.afterTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testClusterRestartStaticCaches1() throws Exception {
+        clusterRestart(1, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testClusterRestartStaticCaches2() throws Exception {
+        clusterRestart(3, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testClusterRestartDynamicCaches1() throws Exception {
+        clusterRestart(1, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testClusterRestartDynamicCaches2() throws Exception {
+        clusterRestart(3, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @SuppressWarnings("unchecked")
+    public void testClusterRestartCachesWithH2Indexes() throws Exception {
+        CacheConfiguration[] ccfgs1 = new CacheConfiguration[5];
+
+        // Several caches with the same indexed type (and index names).
+        ccfgs1[0] = cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 1).
+            setIndexedTypes(Integer.class, Person.class);
+        ccfgs1[1] = cacheConfiguration(GROUP1, "c2", PARTITIONED, TRANSACTIONAL, 1).
+            setIndexedTypes(Integer.class, Person.class);
+        ccfgs1[2] = cacheConfiguration(GROUP2, "c3", PARTITIONED, ATOMIC, 1).
+            setIndexedTypes(Integer.class, Person.class);
+        ccfgs1[3] = cacheConfiguration(GROUP2, "c4", PARTITIONED, TRANSACTIONAL, 1).
+            setIndexedTypes(Integer.class, Person.class);
+        ccfgs1[4] = cacheConfiguration(null, "c5", PARTITIONED, ATOMIC, 1).
+            setIndexedTypes(Integer.class, Person.class);
+
+        String[] caches = {"c1", "c2", "c3", "c4", "c5"};
+
+        startGrids(3);
+
+        Ignite node = ignite(0);
+
+        node.createCaches(Arrays.asList(ccfgs1));
+
+        putPersons(caches, node);
+
+        checkPersons(caches, node);
+        checkPersonsQuery(caches, node);
+
+        stopAllGrids();
+
+        startGrids(3);
+
+        awaitPartitionMapExchange();
+
+        node = ignite(0);
+
+        checkPersons(caches, node);
+        checkPersonsQuery(caches, node);
+
+        Random rnd = ThreadLocalRandom.current();
+
+        int idx = rnd.nextInt(caches.length);
+
+        String cacheName = caches[idx];
+        CacheConfiguration cacheCfg = ccfgs1[idx];
+
+        node.destroyCache(cacheName);
+
+        node.createCache(cacheCfg);
+
+        putPersons(new String[]{cacheName}, node);
+
+        checkPersons(caches, node);
+        checkPersonsQuery(caches, node);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void _testExpiryPolicy() throws Exception {
+        long ttl = 10000;
+
+        activeOnStart = false;
+
+        CacheConfiguration[] ccfgs1 = new CacheConfiguration[5];
+
+        ccfgs1[0] = cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 1);
+        ccfgs1[1] = cacheConfiguration(GROUP1, "c2", PARTITIONED, TRANSACTIONAL, 1);
+        ccfgs1[2] = cacheConfiguration(GROUP2, "c3", PARTITIONED, ATOMIC, 1);
+        ccfgs1[3] = cacheConfiguration(GROUP2, "c4", PARTITIONED, TRANSACTIONAL, 1);
+        ccfgs1[4] = cacheConfiguration(null, "c5", PARTITIONED, ATOMIC, 1);
+
+        String[] caches = {"c1", "c2", "c3", "c4", "c5"};
+
+        startGrids(3);
+
+        Ignite node = ignite(0);
+
+        node.active(true);
+
+        node.createCaches(Arrays.asList(ccfgs1));
+
+        ExpiryPolicy plc = new PlatformExpiryPolicy(ttl, -2, -2);
+
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName).withExpiryPolicy(plc);
+
+            for (int i = 0; i < 10; i++)
+                cache.put(i, cacheName + i);
+        }
+
+        long deadline = System.currentTimeMillis() + (long)(ttl * 1.2);
+
+        stopAllGrids();
+
+        startGrids(3);
+
+        node = ignite(0);
+
+        node.active(true);
+
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            for (int i = 0; i < 10; i++)
+                assertEquals(cacheName + i, cache.get(i));
+
+            assertEquals(10, cache.size());
+        }
+
+        // Wait for expiration.
+        Thread.sleep(Math.max(deadline - System.currentTimeMillis(), 0));
+
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            assertEquals(0, cache.size());
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDropCache() throws Exception {
+        ccfgs = new CacheConfiguration[]{cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 1)
+            .setIndexedTypes(Integer.class, Person.class)};
+
+        Ignite ignite = startGrid();
+
+        ignite.cache("c1").destroy();
+
+        stopGrid();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDropCache1() throws Exception {
+        CacheConfiguration ccfg1 = cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 1);
+
+        CacheConfiguration ccfg2 = cacheConfiguration(GROUP1, "c2", PARTITIONED, ATOMIC, 1);
+
+        Ignite ignite = startGrid();
+
+        ignite.createCaches(Arrays.asList(ccfg1, ccfg2));
+
+
+        ignite.cache("c1").destroy();
+
+        ignite.cache("c2").destroy();
+
+        ignite.createCache(ccfg1);
+        ignite.createCache(ccfg2);
+
+        stopGrid();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCreateDropCache2() throws Exception {
+        CacheConfiguration ccfg1 = cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 1)
+            .setIndexedTypes(Integer.class, Person.class);
+
+        CacheConfiguration ccfg2 = cacheConfiguration(GROUP1, "c2", PARTITIONED, ATOMIC, 1)
+            .setIndexedTypes(Integer.class, Person.class);
+
+        Ignite ignite = startGrid();
+
+        ignite.createCaches(Arrays.asList(ccfg1, ccfg2));
+
+        ignite.cache("c1").destroy();
+
+        ignite.createCache(ccfg1);
+
+        stopGrid();
+    }
+
+    /**
+     * @param caches Cache names to put data into.
+     * @param node Ignite node.
+     */
+    private void putPersons(String[] caches, Ignite node) {
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            for (int i = 0; i < 10; i++)
+                cache.put(i, new Person("" + i, cacheName));
+        }
+    }
+
+    /**
+     * @param caches Cache names to invoke a query against to.
+     * @param node Ignite node.
+     */
+    private void checkPersons(String[] caches, Ignite node) {
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            for (int i = 0; i < 10; i++)
+                assertEquals(new Person("" + i, cacheName), cache.get(i));
+
+            assertEquals(10, cache.size());
+        }
+    }
+
+    /**
+     * @param caches Cache names to invoke a query against to.
+     * @param node Ignite node.
+     */
+    private void checkPersonsQuery(String[] caches, Ignite node) {
+        SqlQuery<Integer, Person> qry = new SqlQuery<>(
+            Person.class, "SELECT p.* FROM Person p WHERE p.lname=? ORDER BY p.fname");
+
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            List<Cache.Entry<Integer, Person>> persons = cache.query(qry.setArgs(cacheName)).getAll();
+
+            for (int i = 0; i < 10; i++)
+                assertEquals(new Person("" + i, cacheName), persons.get(i).getValue());
+
+            assertEquals(10, persons.size());
+        }
+    }
+
+    /**
+     * @param nodes Nodes number.
+     * @param staticCaches {@code True} if caches should be statically configured.
+     * @throws Exception If failed.
+     */
+    private void clusterRestart(int nodes, boolean staticCaches) throws Exception {
+        CacheConfiguration[] ccfgs = new CacheConfiguration[5];
+
+        ccfgs[0] = cacheConfiguration(GROUP1, "c1", PARTITIONED, ATOMIC, 1);
+        ccfgs[1] = cacheConfiguration(GROUP1, "c2", PARTITIONED, TRANSACTIONAL, 1);
+        ccfgs[2] = cacheConfiguration(GROUP2, "c3", PARTITIONED, ATOMIC, 1);
+        ccfgs[3] = cacheConfiguration(GROUP2, "c4", PARTITIONED, TRANSACTIONAL, 1);
+        ccfgs[4] = cacheConfiguration(null, "c5", PARTITIONED, ATOMIC, 1);
+
+        String[] caches = {"c1", "c2", "c3", "c4", "c5"};
+
+        for (int i = 0; i < nodes; i++) {
+            if (staticCaches)
+                this.ccfgs = ccfgs;
+
+            startGrid(i);
+        }
+
+        Ignite node = ignite(0);
+
+        if (!staticCaches)
+            node.createCaches(Arrays.asList(ccfgs));
+
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            for (int i = 0; i < 10; i++)  {
+                cache.put(i, cacheName + i);
+
+                assertEquals(cacheName + i, cache.get(i));
+            }
+
+            assertEquals(10, cache.size());
+        }
+
+        stopAllGrids();
+
+        node = startGrids(nodes);
+
+        awaitPartitionMapExchange();
+
+        for (String cacheName : caches) {
+            IgniteCache<Object, Object> cache = node.cache(cacheName);
+
+            for (int i = 0; i < 10; i++)
+                assertEquals(cacheName + i, cache.get(i));
+
+            assertEquals(10, cache.size());
+        }
+    }
+
+    /**
+     * @param grpName Cache group name.
+     * @param name Cache name.
+     * @param cacheMode Cache mode.
+     * @param atomicityMode Atomicity mode.
+     * @param backups Backups number.
+     * @return Cache configuration.
+     */
+    private CacheConfiguration cacheConfiguration(
+        String grpName,
+        String name,
+        CacheMode cacheMode,
+        CacheAtomicityMode atomicityMode,
+        int backups
+    ) {
+        CacheConfiguration ccfg = new CacheConfiguration();
+
+        ccfg.setName(name);
+        ccfg.setGroupName(grpName);
+        ccfg.setAtomicityMode(atomicityMode);
+        ccfg.setBackups(backups);
+        ccfg.setCacheMode(cacheMode);
+        ccfg.setWriteSynchronizationMode(FULL_SYNC);
+
+        return ccfg;
+    }
+
+    /**
+     *
+     */
+    static class Person implements Serializable {
+        /** */
+        @GridToStringInclude
+        @QuerySqlField(index = true, groups = "full_name")
+        String fName;
+
+        /** */
+        @GridToStringInclude
+        @QuerySqlField(index = true, groups = "full_name")
+        String lName;
+
+        /**
+         * @param fName First name.
+         * @param lName Last name.
+         */
+        public Person(String fName, String lName) {
+            this.fName = fName;
+            this.lName = lName;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(Person.class, this);
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+            Person person = (Person)o;
+            return Objects.equals(fName, person.fName) &&
+                Objects.equals(lName, person.lName);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return Objects.hash(fName, lName);
+        }
+    }
+}


[12/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
index 947421b..ae47443 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
@@ -18,7 +18,10 @@
 package org.apache.ignite.internal.processors.cache;
 
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -27,8 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import javax.cache.Cache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
-import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.configuration.DataPageEvictionMode;
+import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.pagemem.FullPageId;
 import org.apache.ignite.internal.pagemem.PageMemory;
@@ -50,10 +52,8 @@ import org.apache.ignite.internal.processors.cache.database.tree.io.IOVersions;
 import org.apache.ignite.internal.processors.cache.database.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtInvalidPartitionException;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
-import org.apache.ignite.internal.processors.cache.local.GridLocalCache;
 import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
-import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.util.GridAtomicLong;
 import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
 import org.apache.ignite.internal.util.GridEmptyCloseableIterator;
@@ -81,12 +81,21 @@ import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
  *
  */
 @SuppressWarnings("PublicInnerClass")
-public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter implements IgniteCacheOffheapManager {
+public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager {
     /** */
-    private CacheDataStore locCacheDataStore;
+    private static final int UNDEFINED_CACHE_ID = 0;
+
+    /** */
+    protected GridCacheSharedContext ctx;
+
+    /** */
+    protected CacheGroupContext grp;
+
+    /** */
+    protected IgniteLogger log;
 
     /** */
-    private boolean indexingEnabled;
+    private CacheDataStore locCacheDataStore;
 
     /** */
     protected final ConcurrentMap<Integer, CacheDataStore> partDataStores = new ConcurrentHashMap<>();
@@ -98,9 +107,6 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     private volatile boolean hasPendingEntries;
 
     /** */
-    private static final PendingRow START_PENDING_ROW = new PendingRow(Long.MIN_VALUE, 0);
-
-    /** */
     private final GridAtomicLong globalRmvId = new GridAtomicLong(U.currentTimeMillis() * 1000_000);
 
     /** */
@@ -118,79 +124,102 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     }
 
     /** {@inheritDoc} */
-    @Override protected void start0() throws IgniteCheckedException {
-        super.start0();
+    @Override public void start(GridCacheSharedContext ctx, CacheGroupContext grp) throws IgniteCheckedException {
+        this.ctx = ctx;
+        this.grp = grp;
+        this.log = ctx.logger(getClass());
 
-        indexingEnabled = QueryUtils.isEnabled(cctx.config());
+        updateValSizeThreshold = ctx.database().pageSize() / 2;
 
-        updateValSizeThreshold = cctx.shared().database().pageSize() / 2;
-
-        if (cctx.affinityNode()) {
-            cctx.shared().database().checkpointReadLock();
+        if (grp.affinityNode()) {
+            ctx.database().checkpointReadLock();
 
             try {
                 initDataStructures();
 
-                if (cctx.isLocal()) {
-                    assert cctx.cache() instanceof GridLocalCache : cctx.cache();
-
+                if (grp.isLocal())
                     locCacheDataStore = createCacheDataStore(0);
-                }
             }
             finally {
-                cctx.shared().database().checkpointReadUnlock();
+                ctx.database().checkpointReadUnlock();
             }
         }
     }
 
-    /**
-     * @throws IgniteCheckedException If failed.
-     */
-    protected void initDataStructures() throws IgniteCheckedException {
-        if (cctx.shared().ttl().eagerTtlEnabled()) {
+    /** {@inheritDoc} */
+    public void onCacheStarted(GridCacheContext cctx) throws IgniteCheckedException{
+        if (cctx.affinityNode() && cctx.ttl().eagerTtlEnabled() && pendingEntries == null) {
             String name = "PendingEntries";
 
             long rootPage = allocateForTree();
 
-            pendingEntries = new PendingEntriesTree(cctx,
+            pendingEntries = new PendingEntriesTree(
+                grp,
                 name,
-                cctx.memoryPolicy().pageMemory(),
+                grp.memoryPolicy().pageMemory(),
                 rootPage,
-                cctx.reuseList(),
+                grp.reuseList(),
                 true);
         }
     }
 
-    /** {@inheritDoc} */
-    @Override protected void stop0(final boolean cancel, final boolean destroy) {
-        super.stop0(cancel, destroy);
+    /**
+     * @throws IgniteCheckedException If failed.
+     */
+    protected void initDataStructures() throws IgniteCheckedException {
+        // No-op.
+    }
 
-        if (cctx.affinityNode())
-            destroyCacheDataStructures();
+    /** {@inheritDoc} */
+    @Override public void stopCache(int cacheId, final boolean destroy) {
+        if (destroy && grp.affinityNode())
+            removeCacheData(cacheId);
     }
 
     /** {@inheritDoc} */
-    @Override protected void onKernalStop0(boolean cancel) {
-        super.onKernalStop0(cancel);
+    @Override public void stop() {
+        try {
+            for (CacheDataStore store : cacheDataStores())
+                destroyCacheDataStore(store);
+
+            if (pendingEntries != null)
+                pendingEntries.destroy();
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e.getMessage(), e);
+        }
+    }
 
+    /** {@inheritDoc} */
+    @Override public void onKernalStop() {
         busyLock.block();
     }
 
     /**
      *
      */
-    protected void destroyCacheDataStructures() {
-        assert cctx.affinityNode();
+    private void removeCacheData(int cacheId) {
+        assert grp.affinityNode();
 
         try {
-            if (locCacheDataStore != null)
-                locCacheDataStore.destroy();
+            if (grp.sharedGroup()) {
+                assert cacheId != UNDEFINED_CACHE_ID;
 
-            if (pendingEntries != null)
-                pendingEntries.destroy();
+                for (CacheDataStore store : cacheDataStores())
+                    store.clear(cacheId);
 
-            for (CacheDataStore store : partDataStores.values())
-                destroyCacheDataStore(store);
+                if (pendingEntries != null) {
+                    PendingRow row = new PendingRow(cacheId);
+
+                    GridCursor<PendingRow> cursor = pendingEntries.find(row, row, PendingEntriesTree.WITHOUT_KEY);
+
+                    while (cursor.next()) {
+                        boolean res = pendingEntries.removex(cursor.get());
+
+                        assert res;
+                    }
+                }
+            }
         }
         catch (IgniteCheckedException e) {
             throw new IgniteException(e.getMessage(), e);
@@ -202,7 +231,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
      * @return Data store for given entry.
      */
     public CacheDataStore dataStore(GridDhtLocalPartition part) {
-        if (cctx.isLocal())
+        if (grp.isLocal())
             return locCacheDataStore;
         else {
             assert part != null;
@@ -212,76 +241,66 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     }
 
     /** {@inheritDoc} */
-    @Override public long entriesCount() {
-        if (cctx.isLocal())
-            return locCacheDataStore.size();
-
+    @Override public long cacheEntriesCount(int cacheId) {
         long size = 0;
 
-        for (CacheDataStore store : partDataStores.values())
-            size += store.size();
+        for (CacheDataStore store : cacheDataStores())
+            size += store.cacheSize(cacheId);
 
         return size;
     }
 
+    /** {@inheritDoc} */
+    @Override public int totalPartitionEntriesCount(int p) {
+        if (grp.isLocal())
+            return locCacheDataStore.fullSize();
+        else {
+            GridDhtLocalPartition part = grp.topology().localPartition(p, AffinityTopologyVersion.NONE, false, true);
+
+            return part != null ? part.dataStore().fullSize() : 0;
+        }
+    }
+
     /**
      * @param p Partition.
      * @return Partition data.
      */
     @Nullable private CacheDataStore partitionData(int p) {
-        if (cctx.isLocal())
+        if (grp.isLocal())
             return locCacheDataStore;
         else {
-            GridDhtLocalPartition part = cctx.topology().localPartition(p, AffinityTopologyVersion.NONE, false, true);
+            GridDhtLocalPartition part = grp.topology().localPartition(p, AffinityTopologyVersion.NONE, false, true);
 
             return part != null ? part.dataStore() : null;
         }
     }
 
     /** {@inheritDoc} */
-    @Override public long entriesCount(
+    @Override public long cacheEntriesCount(
+        int cacheId,
         boolean primary,
         boolean backup,
         AffinityTopologyVersion topVer
     ) throws IgniteCheckedException {
-        if (cctx.isLocal())
-            return entriesCount(0);
+        if (grp.isLocal())
+            return cacheEntriesCount(cacheId, 0);
         else {
-            ClusterNode locNode = cctx.localNode();
-
             long cnt = 0;
 
-            for (GridDhtLocalPartition locPart : cctx.topology().currentLocalPartitions()) {
-                if (primary) {
-                    if (cctx.affinity().primaryByPartition(locNode, locPart.id(), topVer)) {
-                        cnt += locPart.dataStore().size();
+            Iterator<CacheDataStore> it = cacheData(primary, backup, topVer);
 
-                        continue;
-                    }
-                }
-
-                if (backup) {
-                    if (cctx.affinity().backupByPartition(locNode, locPart.id(), topVer))
-                        cnt += locPart.dataStore().size();
-                }
-            }
+            while (it.hasNext())
+                cnt += it.next().cacheSize(cacheId);
 
             return cnt;
         }
     }
 
     /** {@inheritDoc} */
-    @Override public long entriesCount(int part) {
-        if (cctx.isLocal()) {
-            assert part == 0;
-
-            return locCacheDataStore.size();
-        }
-        else {
-            GridDhtLocalPartition locPart = cctx.topology().localPartition(part, AffinityTopologyVersion.NONE, false);
+    @Override public long cacheEntriesCount(int cacheId, int part) {
+        CacheDataStore store = partitionData(part);
 
-            return locPart == null ? 0 : locPart.dataStore().size();
-        }
+        return store == null ? 0 : store.cacheSize(cacheId);
     }
 
     /**
@@ -293,10 +312,10 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     private Iterator<CacheDataStore> cacheData(boolean primary, boolean backup, AffinityTopologyVersion topVer) {
         assert primary || backup;
 
-        if (cctx.isLocal())
-            return Collections.singleton(locCacheDataStore).iterator();
+        if (grp.isLocal())
+            return singletonIterator(locCacheDataStore);
         else {
-            final Iterator<GridDhtLocalPartition> it = cctx.topology().currentLocalPartitions().iterator();
+            final Iterator<GridDhtLocalPartition> it = grp.topology().currentLocalPartitions().iterator();
 
             if (primary && backup) {
                 return F.iterator(it, new IgniteClosure<GridDhtLocalPartition, CacheDataStore>() {
@@ -306,8 +325,8 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                 }, true);
             }
 
-            final Set<Integer> parts = primary ? cctx.affinity().primaryPartitions(cctx.localNodeId(), topVer) :
-                cctx.affinity().backupPartitions(cctx.localNodeId(), topVer);
+            final Set<Integer> parts = primary ? grp.affinity().primaryPartitions(ctx.localNodeId(), topVer) :
+                grp.affinity().backupPartitions(ctx.localNodeId(), topVer);
 
             return F.iterator(it, new IgniteClosure<GridDhtLocalPartition, CacheDataStore>() {
                     @Override public CacheDataStore apply(GridDhtLocalPartition part) {
@@ -323,15 +342,18 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     }
 
     /** {@inheritDoc} */
-    @Override public void invoke(KeyCacheObject key,
+    @Override public void invoke(
+        GridCacheContext cctx,
+        KeyCacheObject key,
         GridDhtLocalPartition part,
         OffheapInvokeClosure c)
         throws IgniteCheckedException {
-        dataStore(part).invoke(key, c);
+        dataStore(part).invoke(cctx, key, c);
     }
 
     /** {@inheritDoc} */
     @Override public void update(
+        GridCacheContext cctx,
         KeyCacheObject key,
         CacheObject val,
         GridCacheVersion ver,
@@ -341,21 +363,23 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     ) throws IgniteCheckedException {
         assert expireTime >= 0;
 
-        dataStore(part).update(key, val, ver, expireTime, oldRow);
+        dataStore(part).update(cctx, key, val, ver, expireTime, oldRow);
     }
 
     /** {@inheritDoc} */
-    @Override public void updateIndexes(KeyCacheObject key, GridDhtLocalPartition part) throws IgniteCheckedException {
-        dataStore(part).updateIndexes(key);
+    @Override public void updateIndexes(GridCacheContext cctx, KeyCacheObject key, GridDhtLocalPartition part)
+        throws IgniteCheckedException {
+        dataStore(part).updateIndexes(cctx, key);
     }
 
     /** {@inheritDoc} */
     @Override public void remove(
+        GridCacheContext cctx,
         KeyCacheObject key,
         int partId,
         GridDhtLocalPartition part
     ) throws IgniteCheckedException {
-        dataStore(part).remove(key, partId);
+        dataStore(part).remove(cctx, key, partId);
     }
 
     /** {@inheritDoc} */
@@ -364,21 +388,21 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         throws IgniteCheckedException {
         KeyCacheObject key = entry.key();
 
-        assert cctx.isLocal() || entry.localPartition() != null : entry;
+        assert grp.isLocal() || entry.localPartition() != null : entry;
 
-        return dataStore(entry.localPartition()).find(key);
+        return dataStore(entry.localPartition()).find(entry.context(), key);
     }
 
     /** {@inheritDoc} */
-    @Nullable @Override public CacheDataRow read(KeyCacheObject key) throws IgniteCheckedException {
+    @Nullable @Override public CacheDataRow read(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
         CacheDataRow row;
 
         if (cctx.isLocal())
-            row = locCacheDataStore.find(key);
+            row = locCacheDataStore.find(cctx, key);
         else {
             GridDhtLocalPartition part = cctx.topology().localPartition(cctx.affinity().partition(key), null, false);
 
-            row = part != null ? dataStore(part).find(key) : null;
+            row = part != null ? dataStore(part).find(cctx, key) : null;
         }
 
         assert row == null || row.value() != null : row;
@@ -419,10 +443,10 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
      * @param readers {@code True} to clear readers.
      */
     @SuppressWarnings("unchecked")
-    @Override public void clear(boolean readers) {
+    @Override public void clearCache(GridCacheContext cctx, boolean readers) {
         GridCacheVersion obsoleteVer = null;
 
-        GridIterator<CacheDataRow> it = rowsIterator(true, true, null);
+        GridIterator<CacheDataRow> it = iterator(cctx.cacheId(), cacheDataStores().iterator());
 
         while (it.hasNext()) {
             cctx.shared().database().checkpointReadLock();
@@ -432,7 +456,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
                 try {
                     if (obsoleteVer == null)
-                        obsoleteVer = cctx.versions().next();
+                        obsoleteVer = ctx.versions().next();
 
                     GridCacheEntryEx entry = cctx.cache().entryEx(key);
 
@@ -471,11 +495,13 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
      * @throws IgniteCheckedException If failed.
      */
     @SuppressWarnings("unchecked")
-    @Override public <K, V> GridCloseableIterator<Cache.Entry<K, V>> entriesIterator(final boolean primary,
+    @Override public <K, V> GridCloseableIterator<Cache.Entry<K, V>> cacheEntriesIterator(
+        final GridCacheContext cctx,
+        final boolean primary,
         final boolean backup,
         final AffinityTopologyVersion topVer,
         final boolean keepBinary) throws IgniteCheckedException {
-        final Iterator<CacheDataRow> it = rowsIterator(primary, backup, topVer);
+        final Iterator<CacheDataRow> it = cacheIterator(cctx.cacheId(), primary, backup, topVer);
 
         return new GridCloseableIteratorAdapter<Cache.Entry<K, V>>() {
             /** */
@@ -516,13 +542,14 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     }
 
     /** {@inheritDoc} */
-    @Override public GridCloseableIterator<KeyCacheObject> keysIterator(final int part) throws IgniteCheckedException {
+    @Override public GridCloseableIterator<KeyCacheObject> cacheKeysIterator(int cacheId, final int part) throws IgniteCheckedException {
         CacheDataStore data = partitionData(part);
 
         if (data == null)
             return new GridEmptyCloseableIterator<>();
 
-        final GridCursor<? extends CacheDataRow> cur = data.cursor();
+        final GridCursor<? extends CacheDataRow> cur =
+            data.cursor(cacheId, null, null, CacheDataRowAdapter.RowData.KEY_ONLY);
 
         return new GridCloseableIteratorAdapter<KeyCacheObject>() {
             /** */
@@ -552,21 +579,41 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
     }
 
     /** {@inheritDoc} */
-    @Override public GridIterator<CacheDataRow> iterator(boolean primary, boolean backups,
+    @Override public GridIterator<CacheDataRow> cacheIterator(
+        int cacheId,
+        boolean primary,
+        boolean backups,
         final AffinityTopologyVersion topVer)
         throws IgniteCheckedException {
-        return rowsIterator(primary, backups, topVer);
+        return iterator(cacheId, cacheData(primary, backups, topVer));
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridIterator<CacheDataRow> cachePartitionIterator(int cacheId, int part) throws IgniteCheckedException {
+        CacheDataStore data = partitionData(part);
+
+        if (data == null)
+            return new GridEmptyCloseableIterator<>();
+
+        return iterator(cacheId, singletonIterator(data));
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridIterator<CacheDataRow> partitionIterator(int part) throws IgniteCheckedException {
+        CacheDataStore data = partitionData(part);
+
+        if (data == null)
+            return new GridEmptyCloseableIterator<>();
+
+        return iterator(UNDEFINED_CACHE_ID, singletonIterator(data));
     }
 
     /**
-     * @param primary Primary entries flag.
-     * @param backups Backup entries flag.
-     * @param topVer Topology version.
-     * @return Iterator.
+     * @param cacheId Cache ID.
+     * @param dataIt Data store iterator.
+     * @return Rows iterator
      */
-    private GridIterator<CacheDataRow> rowsIterator(boolean primary, boolean backups, AffinityTopologyVersion topVer) {
-        final Iterator<CacheDataStore> dataIt = cacheData(primary, backups, topVer);
-
+    private GridIterator<CacheDataRow> iterator(final int cacheId, final Iterator<CacheDataStore> dataIt) {
         return new GridCloseableIteratorAdapter<CacheDataRow>() {
             /** */
             private GridCursor<? extends CacheDataRow> cur;
@@ -595,7 +642,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                             CacheDataStore ds = dataIt.next();
 
                             curPart = ds.partId();
-                            cur = ds.cursor();
+                            cur = cacheId == UNDEFINED_CACHE_ID ? ds.cursor() : ds.cursor(cacheId);
                         }
                         else
                             break;
@@ -616,35 +663,34 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         };
     }
 
-    /** {@inheritDoc} */
-    @Override public GridIterator<CacheDataRow> iterator(int part) throws IgniteCheckedException {
-        CacheDataStore data = partitionData(part);
-
-        if (data == null)
-            return new GridEmptyCloseableIterator<>();
-
-        final GridCursor<? extends CacheDataRow> cur = data.cursor();
-
-        return new GridCloseableIteratorAdapter<CacheDataRow>() {
+    /**
+     * @param item Item.
+     * @return Single item iterator.
+     */
+    private <T> Iterator<T> singletonIterator(final T item) {
+        return new Iterator<T>() {
             /** */
-            private CacheDataRow next;
-
-            @Override protected CacheDataRow onNext() {
-                CacheDataRow res = next;
-
-                next = null;
+            private boolean hasNext = true;
 
-                return res;
+            /** {@inheritDoc} */
+            @Override public boolean hasNext() {
+                return hasNext;
             }
 
-            @Override protected boolean onHasNext() throws IgniteCheckedException {
-                if (next != null)
-                    return true;
+            /** {@inheritDoc} */
+            @Override public T next() {
+                if (hasNext) {
+                    hasNext = false;
 
-                if (cur.next())
-                    next = cur.get();
+                    return item;
+                }
 
-                return next != null;
+                throw new NoSuchElementException();
+            }
+
+            /** {@inheritDoc} */
+            @Override public void remove() {
+                throw new UnsupportedOperationException();
             }
         };
     }
@@ -654,37 +700,37 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
      * @throws IgniteCheckedException If failed.
      */
     private long allocateForTree() throws IgniteCheckedException {
-        ReuseList reuseList = cctx.reuseList();
+        ReuseList reuseList = grp.reuseList();
 
         long pageId;
 
         if (reuseList == null || (pageId = reuseList.takeRecycledPage()) == 0L)
-            pageId = cctx.memoryPolicy().pageMemory().allocatePage(cctx.cacheId(), INDEX_PARTITION, FLAG_IDX);
+            pageId = grp.memoryPolicy().pageMemory().allocatePage(grp.groupId(), INDEX_PARTITION, FLAG_IDX);
 
         return pageId;
     }
 
     /** {@inheritDoc} */
-    @Override public RootPage rootPageForIndex(String idxName) throws IgniteCheckedException {
+    @Override public RootPage rootPageForIndex(int cacheId, String idxName) throws IgniteCheckedException {
         long pageId = allocateForTree();
 
-        return new RootPage(new FullPageId(pageId, cctx.cacheId()), true);
+        return new RootPage(new FullPageId(pageId, grp.groupId()), true);
     }
 
     /** {@inheritDoc} */
-    @Override public void dropRootPageForIndex(String idxName) throws IgniteCheckedException {
+    @Override public void dropRootPageForIndex(int cacheId, String idxName) throws IgniteCheckedException {
         // No-op.
     }
 
     /** {@inheritDoc} */
     @Override public ReuseList reuseListForIndex(String idxName) {
-        return cctx.reuseList();
+        return grp.reuseList();
     }
 
     /** {@inheritDoc} */
     @Override public IgniteRebalanceIterator rebalanceIterator(int part, AffinityTopologyVersion topVer, Long partCntr)
         throws IgniteCheckedException {
-        final GridIterator<CacheDataRow> it = iterator(part);
+        final GridIterator<CacheDataRow> it = partitionIterator(part);
 
         return new IgniteRebalanceIterator() {
             @Override public boolean historical() {
@@ -758,14 +804,15 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         throws IgniteCheckedException {
         final long rootPage = allocateForTree();
 
-        CacheDataRowStore rowStore = new CacheDataRowStore(cctx, cctx.freeList(), p);
+        CacheDataRowStore rowStore = new CacheDataRowStore(grp, grp.freeList(), p);
 
         String idxName = treeName(p);
 
-        CacheDataTree dataTree = new CacheDataTree(idxName,
-            cctx.reuseList(),
+        CacheDataTree dataTree = new CacheDataTree(
+            grp,
+            idxName,
+            grp.reuseList(),
             rowStore,
-            cctx,
             rootPage,
             true);
 
@@ -774,7 +821,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
     /** {@inheritDoc} */
     @Override public Iterable<CacheDataStore> cacheDataStores() {
-        if (cctx.isLocal())
+        if (grp.isLocal())
             return Collections.singleton(locCacheDataStore);
 
         return new Iterable<CacheDataStore>() {
@@ -823,9 +870,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
     /** {@inheritDoc} */
     @Override public boolean expire(
+        GridCacheContext cctx,
         IgniteInClosure2X<GridCacheEntryEx, GridCacheVersion> c,
         int amount
     ) throws IgniteCheckedException {
+        assert !cctx.isNear() : cctx.name();
+
         if (hasPendingEntries && pendingEntries != null) {
             cctx.shared().database().checkpointReadLock();
 
@@ -834,7 +884,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
                 long now = U.currentTimeMillis();
 
-                GridCursor<PendingRow> cur = pendingEntries.find(START_PENDING_ROW, new PendingRow(now, 0));
+                GridCursor<PendingRow> cur;
+
+            if (grp.sharedGroup())
+                cur = pendingEntries.find(new PendingRow(cctx.cacheId()), new PendingRow(cctx.cacheId(), now, 0));
+            else
+                cur = pendingEntries.find(null, new PendingRow(UNDEFINED_CACHE_ID, now, 0));
 
                 int cleared = 0;
 
@@ -847,9 +902,9 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                 if (row.key.partition() == -1)
                     row.key.partition(cctx.affinity().partition(row.key));assert row.key != null && row.link != 0 && row.expireTime != 0 : row;
 
-                    if (pendingEntries.remove(row) != null) {
+                    if (pendingEntries.removex(row)) {
                         if (obsoleteVer == null)
-                            obsoleteVer = cctx.versions().next();
+                            obsoleteVer = ctx.versions().next();
 
                         c.apply(cctx.cache().entryEx(row.key), obsoleteVer);
                     }
@@ -890,7 +945,10 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         protected final AtomicLong cntr = new AtomicLong();
 
         /** Partition size. */
-        protected final AtomicLong storageSize = new AtomicLong();
+        private final AtomicLong storageSize = new AtomicLong();
+
+        /** */
+        private final ConcurrentMap<Integer, AtomicLong> cacheSizes = new ConcurrentHashMap<>();
 
         /** Initialized update counter. */
         protected Long initCntr = 0L;
@@ -913,13 +971,73 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             this.dataTree = dataTree;
         }
 
+        /**
+         * @param cacheId Cache ID.
+         */
+        void incrementSize(int cacheId) {
+            storageSize.incrementAndGet();
+
+            if (grp.sharedGroup()) {
+                AtomicLong size = cacheSizes.get(cacheId);
+
+                if (size == null) {
+                    AtomicLong old = cacheSizes.putIfAbsent(cacheId, size = new AtomicLong());
+
+                    if (old != null)
+                        size = old;
+                }
+
+                size.incrementAndGet();
+            }
+        }
+
+        /**
+         * @param cacheId Cache ID.
+         */
+        void decrementSize(int cacheId) {
+            storageSize.decrementAndGet();
+
+            if (grp.sharedGroup()) {
+                AtomicLong size = cacheSizes.get(cacheId);
+
+                if (size == null)
+                    return;
+
+                size.decrementAndGet();
+            }
+        }
+
         /** {@inheritDoc} */
         @Override public int partId() {
             return partId;
         }
 
         /** {@inheritDoc} */
-        @Override public int size() {
+        @Override public int cacheSize(int cacheId) {
+            if (grp.sharedGroup()) {
+                AtomicLong size = cacheSizes.get(cacheId);
+
+                return size != null ? (int)size.get() : 0;
+            }
+
+            return (int)storageSize.get();
+        }
+
+        /** {@inheritDoc} */
+        @Override public Map<Integer, Long> cacheSizes() {
+            if (!grp.sharedGroup())
+                return null;
+
+            Map<Integer, Long> res = new HashMap<>();
+
+            for (Map.Entry<Integer, AtomicLong> e : cacheSizes.entrySet())
+                res.put(e.getKey(), e.getValue().longValue());
+
+            return res;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int fullSize() {
             return (int)storageSize.get();
         }
 
@@ -949,12 +1067,13 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /**
+         * @param cctx Cache context.
          * @param oldRow Old row.
          * @param dataRow New row.
          * @return {@code True} if it is possible to update old row data.
          * @throws IgniteCheckedException If failed.
          */
-        private boolean canUpdateOldRow(@Nullable CacheDataRow oldRow, DataRow dataRow)
+        private boolean canUpdateOldRow(GridCacheContext cctx, @Nullable CacheDataRow oldRow, DataRow dataRow)
             throws IgniteCheckedException {
             if (oldRow == null || cctx.queries().enabled())
                 return false;
@@ -975,13 +1094,15 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /** {@inheritDoc} */
-        @Override public void invoke(KeyCacheObject key, OffheapInvokeClosure c)
+        @Override public void invoke(GridCacheContext cctx, KeyCacheObject key, OffheapInvokeClosure c)
             throws IgniteCheckedException {
             if (!busyLock.enterBusy())
                 throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
 
             try {
-                dataTree.invoke(new SearchRow(key), CacheDataRowAdapter.RowData.NO_KEY, c);
+                int cacheId = grp.sharedGroup() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
+                dataTree.invoke(new SearchRow(cacheId, key), CacheDataRowAdapter.RowData.NO_KEY, c);
 
                 switch (c.operationType()) {
                     case PUT: {
@@ -989,7 +1110,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
                         CacheDataRow oldRow = c.oldRow();
 
-                        finishUpdate(c.newRow(), oldRow);
+                        finishUpdate(cctx, c.newRow(), oldRow);
 
                         break;
                     }
@@ -997,7 +1118,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                     case REMOVE: {
                         CacheDataRow oldRow = c.oldRow();
 
-                        finishRemove(key, oldRow);
+                        finishRemove(cctx, key, oldRow);
 
                         break;
                     }
@@ -1015,18 +1136,19 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /** {@inheritDoc} */
-        @Override public CacheDataRow createRow(KeyCacheObject key,
+        @Override public CacheDataRow createRow(
+            GridCacheContext cctx,
+            KeyCacheObject key,
             CacheObject val,
             GridCacheVersion ver,
             long expireTime,
             @Nullable CacheDataRow oldRow) throws IgniteCheckedException
         {
-            int cacheId = cctx.memoryPolicy().config().getPageEvictionMode() == DataPageEvictionMode.DISABLED ?
-                0 : cctx.cacheId();
+            int cacheId = grp.storeCacheIdInDataPage() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
 
             DataRow dataRow = new DataRow(key, val, ver, partId, expireTime, cacheId);
 
-            if (canUpdateOldRow(oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow))
+            if (canUpdateOldRow(cctx, oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow))
                 dataRow.link(oldRow.link());
             else {
                 CacheObjectContext coCtx = cctx.cacheObjectContext();
@@ -1039,12 +1161,15 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             assert dataRow.link() != 0 : dataRow;
 
+            if (grp.sharedGroup() && dataRow.cacheId() == UNDEFINED_CACHE_ID)
+                dataRow.cacheId(cctx.cacheId());
+
             return dataRow;
         }
 
         /** {@inheritDoc} */
-        @Override public void update(
-            KeyCacheObject key,
+        @Override public void update(GridCacheContext cctx,KeyCacheObject key,
+
             CacheObject val,
             GridCacheVersion ver,
             long expireTime,
@@ -1056,8 +1181,9 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                 throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
 
             try {
-                int cacheId = cctx.memoryPolicy().config().getPageEvictionMode() != DataPageEvictionMode.DISABLED ?
-                    cctx.cacheId() : 0;
+                int cacheId = grp.storeCacheIdInDataPage() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
+                assert oldRow == null || oldRow.cacheId() == cacheId : oldRow;
 
                 DataRow dataRow = new DataRow(key, val, ver, partId, expireTime, cacheId);
 
@@ -1069,7 +1195,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
                 CacheDataRow old;
 
-                if (canUpdateOldRow(oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow)) {
+                if (canUpdateOldRow(cctx, oldRow, dataRow) && rowStore.updateRow(oldRow.link(), dataRow)) {
                     old = oldRow;
 
                     dataRow.link(oldRow.link());
@@ -1079,6 +1205,9 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
                     assert dataRow.link() != 0 : dataRow;
 
+                    if (grp.sharedGroup() && dataRow.cacheId() == UNDEFINED_CACHE_ID)
+                        dataRow.cacheId(cctx.cacheId());
+
                     if (oldRow != null) {
                         old = oldRow;
 
@@ -1088,7 +1217,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                         old = dataTree.put(dataRow);
                 }
 
-                finishUpdate(dataRow, old);
+                finishUpdate(cctx, dataRow, old);
             }
             finally {
                 busyLock.leaveBusy();
@@ -1096,13 +1225,15 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /**
+         * @param cctx Cache context.
          * @param newRow New row.
          * @param oldRow Old row if available.
          * @throws IgniteCheckedException If failed.
          */
-        private void finishUpdate(CacheDataRow newRow, @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
+        private void finishUpdate(GridCacheContext cctx, CacheDataRow newRow, @Nullable CacheDataRow oldRow)
+            throws IgniteCheckedException {
             if (oldRow == null)
-                storageSize.incrementAndGet();
+                incrementSize(cctx.cacheId());
 
             KeyCacheObject key = newRow.key();
 
@@ -1110,6 +1241,8 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             GridCacheQueryManager qryMgr = cctx.queries();
 
+            int cacheId = grp.sharedGroup() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
             if (qryMgr.enabled()) {
                 if (oldRow != null) {
                     qryMgr.store(key,
@@ -1136,51 +1269,55 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                 assert oldRow.link() != 0 : oldRow;
 
                 if (pendingEntries != null && oldRow.expireTime() != 0)
-                    pendingEntries.removex(new PendingRow(oldRow.expireTime(), oldRow.link()));
+                    pendingEntries.removex(new PendingRow(cacheId, oldRow.expireTime(), oldRow.link()));
 
                 if (newRow.link() != oldRow.link())
                     rowStore.removeRow(oldRow.link());
             }
 
             if (pendingEntries != null && expireTime != 0) {
-                pendingEntries.putx(new PendingRow(expireTime, newRow.link()));
+                pendingEntries.putx(new PendingRow(cacheId, expireTime, newRow.link()));
 
                 hasPendingEntries = true;
             }
 
-            updateIgfsMetrics(key, (oldRow != null ? oldRow.value() : null), newRow.value());
+            updateIgfsMetrics(cctx, key, (oldRow != null ? oldRow.value() : null), newRow.value());
         }
 
         /** {@inheritDoc} */
-        @Override public void updateIndexes(KeyCacheObject key) throws IgniteCheckedException {
-            if (indexingEnabled) {
-                CacheDataRow row = dataTree.findOne(new SearchRow(key));
+        @Override public void updateIndexes(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
+            int cacheId = grp.sharedGroup() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
+            CacheDataRow row = dataTree.findOne(new SearchRow(cacheId, key), CacheDataRowAdapter.RowData.NO_KEY);
+
+            if (row != null) {
+                row.key(key);
 
                 GridCacheQueryManager qryMgr = cctx.queries();
 
-                if (row != null) {
-                    qryMgr.store(
-                        key,
-                        partId,
-                        null,
-                        null,
-                        row.value(),
-                        row.version(),
-                        row.expireTime(),
-                        row.link());
-                }
+                qryMgr.store(
+                    key,
+                    partId,
+                    null,
+                    null,
+                    row.value(),
+                    row.version(),
+                    row.expireTime(),
+                    row.link());
             }
         }
 
         /** {@inheritDoc} */
-        @Override public void remove(KeyCacheObject key, int partId) throws IgniteCheckedException {
+        @Override public void remove(GridCacheContext cctx, KeyCacheObject key, int partId) throws IgniteCheckedException {
             if (!busyLock.enterBusy())
                 throw new NodeStoppingException("Operation has been cancelled (node is stopping).");
 
             try {
-                CacheDataRow oldRow = dataTree.remove(new SearchRow(key));
+                int cacheId = grp.sharedGroup() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
+                CacheDataRow oldRow = dataTree.remove(new SearchRow(cacheId, key));
 
-                finishRemove(key, oldRow);
+                finishRemove(cctx, key, oldRow);
             }
             finally {
                 busyLock.leaveBusy();
@@ -1188,21 +1325,26 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @param oldRow Removed row.
          * @throws IgniteCheckedException If failed.
          */
-        private void finishRemove(KeyCacheObject key, @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
+        private void finishRemove(GridCacheContext cctx, KeyCacheObject key, @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
             CacheObject val = null;
             GridCacheVersion ver = null;
 
             if (oldRow != null) {
+                int cacheId = grp.sharedGroup() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
                 assert oldRow.link() != 0 : oldRow;
+                assert cacheId == UNDEFINED_CACHE_ID || oldRow.cacheId() == cacheId :
+                    "Incorrect cache ID [expected=" + cacheId + ", actual=" + oldRow.cacheId() + "].";
 
                 if (pendingEntries != null && oldRow.expireTime() != 0)
-                    pendingEntries.removex(new PendingRow(oldRow.expireTime(), oldRow.link()));
+                    pendingEntries.removex(new PendingRow(cacheId, oldRow.expireTime(), oldRow.link()));
 
-                storageSize.decrementAndGet();
+                decrementSize(cctx.cacheId());
 
                 val = oldRow.value();
 
@@ -1217,19 +1359,21 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             if (oldRow != null)
                 rowStore.removeRow(oldRow.link());
 
-            updateIgfsMetrics(key, (oldRow != null ? oldRow.value() : null), null);
+            updateIgfsMetrics(cctx, key, (oldRow != null ? oldRow.value() : null), null);
         }
 
         /** {@inheritDoc} */
-        @Override public CacheDataRow find(KeyCacheObject key) throws IgniteCheckedException {
+        @Override public CacheDataRow find(GridCacheContext cctx, KeyCacheObject key) throws IgniteCheckedException {
             key.valueBytes(cctx.cacheObjectContext());
 
-            CacheDataRow row = dataTree.findOne(new SearchRow(key), CacheDataRowAdapter.RowData.NO_KEY);
+            int cacheId = grp.sharedGroup() ? cctx.cacheId() : UNDEFINED_CACHE_ID;
+
+            CacheDataRow row = dataTree.findOne(new SearchRow(cacheId, key), CacheDataRowAdapter.RowData.NO_KEY);
 
             if (row != null) {
                 row.key(key);
 
-                cctx.memoryPolicy().evictionTracker().touchPage(row.link());
+                grp.memoryPolicy().evictionTracker().touchPage(row.link());
             }
 
             return row;
@@ -1240,19 +1384,36 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             return dataTree.find(null, null);
         }
 
+        /** {@inheritDoc}
+         * @param cacheId*/
+        @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId) throws IgniteCheckedException {
+            return cursor(cacheId, null, null);
+        }
+
         /** {@inheritDoc} */
-        @Override public GridCursor<? extends CacheDataRow> cursor(KeyCacheObject lower,
+        @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
             KeyCacheObject upper) throws IgniteCheckedException {
-            SearchRow lowerRow = null;
-            SearchRow upperRow = null;
+            return cursor(cacheId, lower, upper, null);
+        }
 
-            if (lower != null)
-                lowerRow = new SearchRow(lower);
+        /** {@inheritDoc} */
+        @Override public GridCursor<? extends CacheDataRow> cursor(int cacheId, KeyCacheObject lower,
+            KeyCacheObject upper, Object x) throws IgniteCheckedException {
+            SearchRow lowerRow;
+            SearchRow upperRow;
+
+            if (grp.sharedGroup()) {
+                assert cacheId != UNDEFINED_CACHE_ID;
 
-            if (upper != null)
-                upperRow = new SearchRow(upper);
+                lowerRow = lower != null ? new SearchRow(cacheId, lower) : new SearchRow(cacheId);
+                upperRow = upper != null ? new SearchRow(cacheId, upper) : new SearchRow(cacheId);
+            }
+            else {
+                lowerRow = lower != null ? new SearchRow(UNDEFINED_CACHE_ID, lower) : null;
+                upperRow = upper != null ? new SearchRow(UNDEFINED_CACHE_ID, upper) : null;
+            }
 
-            return dataTree.find(lowerRow, upperRow);
+            return dataTree.find(lowerRow, upperRow, x);
         }
 
         /** {@inheritDoc} */
@@ -1282,6 +1443,46 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /** {@inheritDoc} */
+        @Override public void clear(int cacheId) throws IgniteCheckedException {
+            assert cacheId != UNDEFINED_CACHE_ID;
+
+            if (cacheSize(cacheId) == 0)
+                return;
+
+            Exception ex = null;
+
+            GridCursor<? extends CacheDataRow> cur =
+                cursor(cacheId, null, null, CacheDataRowAdapter.RowData.KEY_ONLY);
+
+            while (cur.next()) {
+                CacheDataRow row = cur.get();
+
+                assert row.link() != 0 : row;
+
+                try {
+                    boolean res = dataTree.removex(row);
+
+                    assert res : row;
+
+                    rowStore.removeRow(row.link());
+
+                    decrementSize(cacheId);
+                }
+                catch (IgniteCheckedException e) {
+                    U.error(log, "Fail remove row [link=" + row.link() + "]");
+
+                    if (ex == null)
+                        ex = e;
+                    else
+                        ex.addSuppressed(e);
+                }
+            }
+
+            if (ex != null)
+                throw new IgniteCheckedException("Fail destroy store", ex);
+        }
+
+        /** {@inheritDoc} */
         @Override public RowStore rowStore() {
             return rowStore;
         }
@@ -1307,18 +1508,26 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         }
 
         /** {@inheritDoc} */
-        @Override public void init(long size, long updCntr) {
+        @Override public void init(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes) {
             initCntr = updCntr;
             storageSize.set(size);
             cntr.set(updCntr);
+
+            if (cacheSizes != null) {
+                for (Map.Entry<Integer, Long> e : cacheSizes.entrySet())
+                    this.cacheSizes.put(e.getKey(), new AtomicLong(e.getValue()));
+            }
         }
 
         /**
+         * @param cctx Cache context.
          * @param key Key.
          * @param oldVal Old value.
          * @param newVal New value.
+         * @throws IgniteCheckedException If failed.
          */
         private void updateIgfsMetrics(
+            GridCacheContext cctx,
             KeyCacheObject key,
             CacheObject oldVal,
             CacheObject newVal
@@ -1326,11 +1535,11 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             // In case we deal with IGFS cache, count updated data
             if (cctx.cache().isIgfsDataCache() &&
                 !cctx.isNear() &&
-                cctx.kernalContext()
+                ctx.kernalContext()
                     .igfsHelper()
                     .isIgfsBlockKey(key.value(cctx.cacheObjectContext(), false))) {
-                int oldSize = valueLength(oldVal);
-                int newSize = valueLength(newVal);
+                int oldSize = valueLength(cctx, oldVal);
+                int newSize = valueLength(cctx, newVal);
 
                 int delta = newSize - oldSize;
 
@@ -1342,10 +1551,11 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         /**
          * Isolated method to get length of IGFS block.
          *
+         * @param cctx Cache context.
          * @param val Value.
          * @return Length of value.
          */
-        private int valueLength(@Nullable CacheObject val) {
+        private int valueLength(GridCacheContext cctx, @Nullable CacheObject val) {
             if (val == null)
                 return 0;
 
@@ -1368,13 +1578,28 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         /** */
         private final int hash;
 
+        /** */
+        private final int cacheId;
+
         /**
+         * @param cacheId Cache ID.
          * @param key Key.
          */
-        SearchRow(KeyCacheObject key) {
+        SearchRow(int cacheId, KeyCacheObject key) {
             this.key = key;
+            this.hash = key.hashCode();
+            this.cacheId = cacheId;
+        }
 
-            hash = key.hashCode();
+        /**
+         * Instantiates a new fake search row as a logic cache based bound.
+         *
+         * @param cacheId Cache ID.
+         */
+        SearchRow(int cacheId) {
+            this.key = null;
+            this.hash = 0;
+            this.cacheId = cacheId;
         }
 
         /** {@inheritDoc} */
@@ -1391,6 +1616,11 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         @Override public int hash() {
             return hash;
         }
+
+        /** {@inheritDoc} */
+        @Override public int cacheId() {
+            return cacheId;
+        }
     }
 
     /**
@@ -1406,6 +1636,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         /**
          * @param hash Hash code.
          * @param link Link.
+         * @param part Partition.
          * @param rowData Required row data.
          */
         DataRow(int hash, long link, int part, CacheDataRowAdapter.RowData rowData) {
@@ -1417,7 +1648,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             try {
                 // We can not init data row lazily because underlying buffer can be concurrently cleared.
-                initFromLink(cctx, rowData);
+                initFromLink(grp, rowData);
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -1433,6 +1664,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
          * @param ver Version.
          * @param part Partition.
          * @param expireTime Expire time.
+         * @param cacheId Cache ID.
          */
         DataRow(KeyCacheObject key, CacheObject val, GridCacheVersion ver, int part, long expireTime, int cacheId) {
             super(0);
@@ -1460,6 +1692,13 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         @Override public void link(long link) {
             this.link = link;
         }
+
+        /**
+         * @param cacheId Cache ID.
+         */
+        void cacheId(int cacheId) {
+            this.cacheId = cacheId;
+        }
     }
 
     /**
@@ -1470,54 +1709,80 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         private final CacheDataRowStore rowStore;
 
         /** */
-        private final GridCacheContext cctx;
+        private final CacheGroupContext grp;
 
         /**
+         * @param grp Ccahe group.
          * @param name Tree name.
          * @param reuseList Reuse list.
          * @param rowStore Row store.
-         * @param cctx Context.
          * @param metaPageId Meta page ID.
          * @param initNew Initialize new index.
          * @throws IgniteCheckedException If failed.
          */
         public CacheDataTree(
+            CacheGroupContext grp,
             String name,
             ReuseList reuseList,
             CacheDataRowStore rowStore,
-            GridCacheContext cctx,
             long metaPageId,
             boolean initNew
         ) throws IgniteCheckedException {
             super(name,
-                cctx.cacheId(),
-                cctx.memoryPolicy().pageMemory(),
-                cctx.shared().wal(),
-                cctx.offheap().globalRemoveId(),
+                grp.groupId(),
+                grp.memoryPolicy().pageMemory(),
+                grp.shared().wal(),
+                grp.offheap().globalRemoveId(),
                 metaPageId,
                 reuseList,
-                DataInnerIO.VERSIONS,
-                DataLeafIO.VERSIONS);
+                grp.sharedGroup() ? CacheIdAwareDataInnerIO.VERSIONS : DataInnerIO.VERSIONS,
+                grp.sharedGroup() ? CacheIdAwareDataLeafIO.VERSIONS : DataLeafIO.VERSIONS);
 
             assert rowStore != null;
 
             this.rowStore = rowStore;
-            this.cctx = cctx;
+            this.grp = grp;
 
             initTree(initNew);
         }
 
         /** {@inheritDoc} */
-        @Override protected int compare(BPlusIO<CacheSearchRow> io, long pageAddr, int idx, CacheSearchRow row)
+        @Override protected int compare(BPlusIO<CacheSearchRow> iox, long pageAddr, int idx, CacheSearchRow row)
             throws IgniteCheckedException {
-            int hash = ((RowLinkIO)io).getHash(pageAddr, idx);
+            RowLinkIO io = (RowLinkIO)iox;
+
+            int cmp;
+
+            if (grp.sharedGroup()) {
+                assert row.cacheId() != UNDEFINED_CACHE_ID : "Cache ID is not provided: " + row;
+
+                int cacheId = io.getCacheId(pageAddr, idx);
+
+                assert cacheId != UNDEFINED_CACHE_ID : "Cache ID is not stored";
+
+                cmp = Integer.compare(cacheId, row.cacheId());
+
+                if (cmp != 0)
+                    return cmp;
+
+                if (row.key() == null) {
+                    assert row.getClass() == SearchRow.class : row;
 
-            int cmp = Integer.compare(hash, row.hash());
+                    // A search row with a cache ID only is used as a cache bound.
+                    // The found position will be shifted until the exact cache bound is found;
+                    // See for details:
+                    // o.a.i.i.p.c.database.tree.BPlusTree.ForwardCursor.findLowerBound()
+                    // o.a.i.i.p.c.database.tree.BPlusTree.ForwardCursor.findUpperBound()
+                    return cmp;
+                }
+            }
+
+            cmp = Integer.compare(io.getHash(pageAddr, idx), row.hash());
 
             if (cmp != 0)
                 return cmp;
 
-            long link = ((RowLinkIO)io).getLink(pageAddr, idx);
+            long link = io.getLink(pageAddr, idx);
 
             assert row.key() != null : row;
 
@@ -1527,14 +1792,15 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         /** {@inheritDoc} */
         @Override protected CacheDataRow getRow(BPlusIO<CacheSearchRow> io, long pageAddr, int idx, Object flags)
             throws IgniteCheckedException {
-            int hash = ((RowLinkIO)io).getHash(pageAddr, idx);
             long link = ((RowLinkIO)io).getLink(pageAddr, idx);
+            int hash = ((RowLinkIO)io).getHash(pageAddr, idx);
+            int cacheId = ((RowLinkIO)io).getCacheId(pageAddr, idx);
 
             CacheDataRowAdapter.RowData x = flags != null ?
                 (CacheDataRowAdapter.RowData)flags :
                 CacheDataRowAdapter.RowData.FULL;
 
-            return rowStore.dataRow(hash, link, x);
+            return rowStore.dataRow(cacheId, hash, link, x);
         }
 
         /**
@@ -1544,7 +1810,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
          * @throws IgniteCheckedException If failed.
          */
         private int compareKeys(KeyCacheObject key, final long link) throws IgniteCheckedException {
-            byte[] bytes = key.valueBytes(cctx.cacheObjectContext());
+            byte[] bytes = key.valueBytes(grp.cacheObjectContext());
 
             final long pageId = pageId(link);
             final long page = acquirePage(pageId);
@@ -1563,7 +1829,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
                     if (data.nextLink() == 0) {
                         long addr = pageAddr + data.offset();
 
-                        if (cctx.memoryPolicy().config().getPageEvictionMode() != DataPageEvictionMode.DISABLED)
+                        if (grp.storeCacheIdInDataPage())
                             addr += 4; // Skip cache id.
 
                         final int len = PageUtils.getInt(addr, 0);
@@ -1610,10 +1876,10 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             // TODO GG-11768.
             CacheDataRowAdapter other = new CacheDataRowAdapter(link);
-            other.initFromLink(cctx, CacheDataRowAdapter.RowData.KEY_ONLY);
+            other.initFromLink(grp, CacheDataRowAdapter.RowData.KEY_ONLY);
 
-            byte[] bytes1 = other.key().valueBytes(cctx.cacheObjectContext());
-            byte[] bytes2 = key.valueBytes(cctx.cacheObjectContext());
+            byte[] bytes1 = other.key().valueBytes(grp.cacheObjectContext());
+            byte[] bytes2 = key.valueBytes(grp.cacheObjectContext());
 
             int lenCmp = Integer.compare(bytes1.length, bytes2.length);
 
@@ -1655,48 +1921,50 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         private final int partId;
 
         /**
-         * @param cctx Cache context.
+         * @param grp Cache group.
          * @param freeList Free list.
+         * @param partId Partition number.
          */
-        public CacheDataRowStore(GridCacheContext<?, ?> cctx, FreeList freeList, int partId) {
-            super(cctx, freeList);
+        public CacheDataRowStore(CacheGroupContext grp, FreeList freeList, int partId) {
+            super(grp, freeList);
 
             this.partId = partId;
         }
 
         /**
+         * @param cacheId Cache ID.
          * @param hash Hash code.
          * @param link Link.
          * @return Search row.
          */
-        private CacheSearchRow keySearchRow(int hash, long link) {
-            return new DataRow(hash, link, partId, CacheDataRowAdapter.RowData.KEY_ONLY);
+        private CacheSearchRow keySearchRow(int cacheId, int hash, long link) {
+            DataRow dataRow = new DataRow(hash, link, partId, CacheDataRowAdapter.RowData.KEY_ONLY);
+
+            if (dataRow.cacheId() == UNDEFINED_CACHE_ID && grp.sharedGroup())
+                dataRow.cacheId(cacheId);
+
+            return dataRow;
         }
 
         /**
+         * @param cacheId Cache ID.
          * @param hash Hash code.
          * @param link Link.
          * @param rowData Required row data.
          * @return Data row.
          */
-        private CacheDataRow dataRow(int hash, long link, CacheDataRowAdapter.RowData rowData) {
-            return new DataRow(hash, link, partId, rowData);
+        private CacheDataRow dataRow(int cacheId, int hash, long link, CacheDataRowAdapter.RowData rowData) {
+            DataRow dataRow = new DataRow(hash, link, partId, rowData);
+
+            if (dataRow.cacheId() == UNDEFINED_CACHE_ID && grp.sharedGroup())
+                dataRow.cacheId(cacheId);
+
+            return dataRow;
         }
     }
 
     /**
-     * @param pageAddr Page address.
-     * @param off Offset.
-     * @param link Link.
-     * @param hash Hash.
-     */
-    private static void store0(long pageAddr, int off, long link, int hash) {
-        PageUtils.putLong(pageAddr, off, link);
-        PageUtils.putInt(pageAddr, off + 8, hash);
-    }
-
-    /**
-     *
+     *
      */
     private interface RowLinkIO {
         /**
@@ -1712,37 +1980,50 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
          * @return Key hash code.
          */
         public int getHash(long pageAddr, int idx);
+
+        /**
+         * @param pageAddr Page address.
+         * @param idx Index.
+         * @return Cache ID or {@code 0} if cache ID is not defined.
+         */
+        public int getCacheId(long pageAddr, int idx);
     }
 
     /**
      *
      */
-    public static final class DataInnerIO extends BPlusInnerIO<CacheSearchRow> implements RowLinkIO {
-        /** */
-        public static final IOVersions<DataInnerIO> VERSIONS = new IOVersions<>(
-            new DataInnerIO(1)
-        );
-
+    private static abstract class AbstractDataInnerIO extends BPlusInnerIO<CacheSearchRow> implements RowLinkIO {
         /**
+         * @param type Page type.
          * @param ver Page format version.
+         * @param canGetRow If we can get full row from this page.
+         * @param itemSize Single item size on page.
          */
-        DataInnerIO(int ver) {
-            super(T_DATA_REF_INNER, ver, true, 12);
+        protected AbstractDataInnerIO(int type, int ver, boolean canGetRow, int itemSize) {
+            super(type, ver, canGetRow, itemSize);
         }
 
         /** {@inheritDoc} */
         @Override public void storeByOffset(long pageAddr, int off, CacheSearchRow row) {
             assert row.link() != 0;
 
-            store0(pageAddr, off, row.link(), row.hash());
+            PageUtils.putLong(pageAddr, off, row.link());
+            PageUtils.putInt(pageAddr, off + 8, row.hash());
+
+            if (storeCacheId()) {
+                assert row.cacheId() != UNDEFINED_CACHE_ID : row;
+
+                PageUtils.putInt(pageAddr, off + 12, row.cacheId());
+            }
         }
 
         /** {@inheritDoc} */
         @Override public CacheSearchRow getLookupRow(BPlusTree<CacheSearchRow, ?> tree, long pageAddr, int idx) {
+            int cacheId = getCacheId(pageAddr, idx);
             int hash = getHash(pageAddr, idx);
             long link = getLink(pageAddr, idx);
 
-            return ((CacheDataTree)tree).rowStore.keySearchRow(hash, link);
+            return ((CacheDataTree)tree).rowStore.keySearchRow(cacheId, hash, link);
         }
 
         /** {@inheritDoc} */
@@ -1750,8 +2031,18 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             int srcIdx) {
             int hash = ((RowLinkIO)srcIo).getHash(srcPageAddr, srcIdx);
             long link = ((RowLinkIO)srcIo).getLink(srcPageAddr, srcIdx);
+            int off = offset(dstIdx);
+
+            PageUtils.putLong(dstPageAddr, off, link);
+            PageUtils.putInt(dstPageAddr, off + 8, hash);
+
+            if (storeCacheId()) {
+                int cacheId = ((RowLinkIO)srcIo).getCacheId(srcPageAddr, srcIdx);
+
+                assert cacheId != UNDEFINED_CACHE_ID;
 
-            store0(dstPageAddr, offset(dstIdx), link, hash);
+                PageUtils.putInt(dstPageAddr, off + 12, cacheId);
+            }
         }
 
         /** {@inheritDoc} */
@@ -1773,43 +2064,66 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             for (int i = 0; i < cnt; i++)
                 c.apply(new CacheDataRowAdapter(getLink(pageAddr, i)));
         }
+
+        /**
+         * @return {@code True} if cache ID has to be stored.
+         */
+        protected abstract boolean storeCacheId();
     }
 
     /**
      *
      */
-    public static final class DataLeafIO extends BPlusLeafIO<CacheSearchRow> implements RowLinkIO {
-        /** */
-        public static final IOVersions<DataLeafIO> VERSIONS = new IOVersions<>(
-            new DataLeafIO(1)
-        );
-
+    private static abstract class AbstractDataLeafIO extends BPlusLeafIO<CacheSearchRow> implements RowLinkIO {
         /**
+         * @param type Page type.
          * @param ver Page format version.
+         * @param itemSize Single item size on page.
          */
-        DataLeafIO(int ver) {
-            super(T_DATA_REF_LEAF, ver, 12);
+        protected AbstractDataLeafIO(int type, int ver, int itemSize) {
+            super(type, ver, itemSize);
         }
 
         /** {@inheritDoc} */
         @Override public void storeByOffset(long pageAddr, int off, CacheSearchRow row) {
             assert row.link() != 0;
 
-            store0(pageAddr, off, row.link(), row.hash());
+            PageUtils.putLong(pageAddr, off, row.link());
+            PageUtils.putInt(pageAddr, off + 8, row.hash());
+
+            if (storeCacheId()) {
+                assert row.cacheId() != UNDEFINED_CACHE_ID;
+
+                PageUtils.putInt(pageAddr, off + 12, row.cacheId());
+            }
         }
 
         /** {@inheritDoc} */
         @Override public void store(long dstPageAddr, int dstIdx, BPlusIO<CacheSearchRow> srcIo, long srcPageAddr,
             int srcIdx) {
-            store0(dstPageAddr, offset(dstIdx), getLink(srcPageAddr, srcIdx), getHash(srcPageAddr, srcIdx));
+            int hash = ((RowLinkIO)srcIo).getHash(srcPageAddr, srcIdx);
+            long link = ((RowLinkIO)srcIo).getLink(srcPageAddr, srcIdx);
+            int off = offset(dstIdx);
+
+            PageUtils.putLong(dstPageAddr, off, link);
+            PageUtils.putInt(dstPageAddr, off + 8, hash);
+
+            if (storeCacheId()) {
+                int cacheId = ((RowLinkIO)srcIo).getCacheId(srcPageAddr, srcIdx);
+
+                assert cacheId != UNDEFINED_CACHE_ID;
+
+                PageUtils.putInt(dstPageAddr, off + 12, cacheId);
+            }
         }
 
         /** {@inheritDoc} */
         @Override public CacheSearchRow getLookupRow(BPlusTree<CacheSearchRow, ?> tree, long buf, int idx) {
+            int cacheId = getCacheId(buf, idx);
             int hash = getHash(buf, idx);
             long link = getLink(buf, idx);
 
-            return ((CacheDataTree)tree).rowStore.keySearchRow(hash, link);
+            return ((CacheDataTree)tree).rowStore.keySearchRow(cacheId, hash, link);
         }
 
         /** {@inheritDoc} */
@@ -1831,6 +2145,119 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             for (int i = 0; i < cnt; i++)
                 c.apply(new CacheDataRowAdapter(getLink(pageAddr, i)));
         }
+
+        /**
+         * @return {@code True} if cache ID has to be stored.
+         */
+        protected abstract boolean storeCacheId();
+    }
+
+    /**
+     *
+     */
+    public static final class DataInnerIO extends AbstractDataInnerIO {
+        /** */
+        public static final IOVersions<DataInnerIO> VERSIONS = new IOVersions<>(
+            new DataInnerIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        DataInnerIO(int ver) {
+            super(T_DATA_REF_INNER, ver, true, 12);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return UNDEFINED_CACHE_ID;
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return false;
+        }
+    }
+
+    /**
+     *
+     */
+    public static final class DataLeafIO extends AbstractDataLeafIO {
+        /** */
+        public static final IOVersions<DataLeafIO> VERSIONS = new IOVersions<>(
+            new DataLeafIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        DataLeafIO(int ver) {
+            super(T_DATA_REF_LEAF, ver, 12);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return UNDEFINED_CACHE_ID;
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return false;
+        }
+    }
+
+    /**
+     *
+     */
+    public static final class CacheIdAwareDataInnerIO extends AbstractDataInnerIO {
+        /** */
+        public static final IOVersions<CacheIdAwareDataInnerIO> VERSIONS = new IOVersions<>(
+            new CacheIdAwareDataInnerIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        CacheIdAwareDataInnerIO(int ver) {
+            super(T_CACHE_ID_AWARE_DATA_REF_INNER, ver, true, 16);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return PageUtils.getInt(pageAddr, offset(idx) + 12);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return true;
+        }
+    }
+
+    /**
+     *
+     */
+    public static final class CacheIdAwareDataLeafIO extends AbstractDataLeafIO {
+        /** */
+        public static final IOVersions<CacheIdAwareDataLeafIO> VERSIONS = new IOVersions<>(
+            new CacheIdAwareDataLeafIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        CacheIdAwareDataLeafIO(int ver) {
+            super(T_CACHE_ID_AWARE_DATA_REF_LEAF, ver, 16);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return PageUtils.getInt(pageAddr, offset(idx) + 12);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return true;
+        }
     }
 
     /**
@@ -1843,38 +2270,47 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         /** Link. */
         private long link;
 
+        /** Cache ID. */
+        private int cacheId;
+
         /** */
         private KeyCacheObject key;
 
         /**
+         * Creates a new instance which represents an upper or lower bound
+         * inside a logical cache.
+         *
+         * @param cacheId Cache ID.
+         */
+        public PendingRow(int cacheId) {
+            this.cacheId = cacheId;
+        }
+
+        /**
+         * @param cacheId Cache ID.
          * @param expireTime Expire time.
          * @param link Link
          */
-        PendingRow(long expireTime, long link) {
+        PendingRow(int cacheId, long expireTime, long link) {
             assert expireTime != 0;
 
+            this.cacheId = cacheId;
             this.expireTime = expireTime;
             this.link = link;
         }
 
         /**
-         * @param cctx Context.
-         * @param expireTime Expire time.
-         * @param link Link.
+         * @param grp Cache group.
          * @return Row.
          * @throws IgniteCheckedException If failed.
          */
-        static PendingRow createRowWithKey(GridCacheContext cctx, long expireTime, long link)
-            throws IgniteCheckedException {
-            PendingRow row = new PendingRow(expireTime, link);
-
+        PendingRow initKey(CacheGroupContext grp) throws IgniteCheckedException {
             CacheDataRowAdapter rowData = new CacheDataRowAdapter(link);
+            rowData.initFromLink(grp, CacheDataRowAdapter.RowData.KEY_ONLY);
 
-            rowData.initFromLink(cctx, CacheDataRowAdapter.RowData.KEY_ONLY);
+            key = rowData.key();
 
-            row.key = rowData.key();
-
-            return row;
+            return this;
         }
 
         /** {@inheritDoc} */
@@ -1888,10 +2324,13 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
      */
     protected static class PendingEntriesTree extends BPlusTree<PendingRow, PendingRow> {
         /** */
-        private final GridCacheContext cctx;
+        private final static Object WITHOUT_KEY = new Object();
+
+        /** */
+        private final CacheGroupContext grp;
 
         /**
-         * @param cctx Cache context.
+         * @param grp Cache group.
          * @param name Tree name.
          * @param pageMem Page memory.
          * @param metaPageId Meta page ID.
@@ -1900,7 +2339,7 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
          * @throws IgniteCheckedException If failed.
          */
         public PendingEntriesTree(
-            GridCacheContext cctx,
+            CacheGroupContext grp,
             String name,
             PageMemory pageMem,
             long metaPageId,
@@ -1908,26 +2347,49 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             boolean initNew)
             throws IgniteCheckedException {
             super(name,
-                cctx.cacheId(),
+                grp.groupId(),
                 pageMem,
-                cctx.shared().wal(),
-                cctx.offheap().globalRemoveId(),
+                grp.shared().wal(),
+                grp.offheap().globalRemoveId(),
                 metaPageId,
                 reuseList,
-                PendingEntryInnerIO.VERSIONS,
-                PendingEntryLeafIO.VERSIONS);
+                grp.sharedGroup() ? CacheIdAwarePendingEntryInnerIO.VERSIONS : PendingEntryInnerIO.VERSIONS,
+                grp.sharedGroup() ? CacheIdAwarePendingEntryLeafIO.VERSIONS : PendingEntryLeafIO.VERSIONS);
 
-            this.cctx = cctx;
+            this.grp = grp;
 
             initTree(initNew);
         }
 
         /** {@inheritDoc} */
-        @Override protected int compare(BPlusIO<PendingRow> io, long pageAddr, int idx, PendingRow row)
+        @Override protected int compare(BPlusIO<PendingRow> iox, long pageAddr, int idx, PendingRow row)
             throws IgniteCheckedException {
-            long expireTime = ((PendingRowIO)io).getExpireTime(pageAddr, idx);
+            PendingRowIO io = (PendingRowIO)iox;
 
-            int cmp = Long.compare(expireTime, row.expireTime);
+            int cmp;
+
+            if (grp.sharedGroup()) {
+                assert row.cacheId != UNDEFINED_CACHE_ID : "Cache ID is not provided!";
+                assert io.getCacheId(pageAddr, idx) != UNDEFINED_CACHE_ID : "Cache ID is not stored!";
+
+                cmp = Integer.compare(io.getCacheId(pageAddr, idx), row.cacheId);
+
+                if (cmp != 0)
+                    return cmp;
+
+                if(cmp == 0 && row.expireTime == 0 && row.link == 0) {
+                    // A search row with a cach ID only is used as a cache bound.
+                    // The found position will be shifted until the exact cache bound is found;
+                    // See for details:
+                    // o.a.i.i.p.c.database.tree.BPlusTree.ForwardCursor.findLowerBound()
+                    // o.a.i.i.p.c.database.tree.BPlusTree.ForwardCursor.findUpperBound()
+                    return cmp;
+                }
+            }
+
+            long expireTime = io.getExpireTime(pageAddr, idx);
+
+            cmp = Long.compare(expireTime, row.expireTime);
 
             if (cmp != 0)
                 return cmp;
@@ -1935,15 +2397,17 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
             if (row.link == 0L)
                 return 0;
 
-            long link = ((PendingRowIO)io).getLink(pageAddr, idx);
+            long link = io.getLink(pageAddr, idx);
 
             return Long.compare(link, row.link);
         }
 
         /** {@inheritDoc} */
-        @Override protected PendingRow getRow(BPlusIO<PendingRow> io, long pageAddr, int idx, Object ignore)
+        @Override protected PendingRow getRow(BPlusIO<PendingRow> io, long pageAddr, int idx, Object flag)
             throws IgniteCheckedException {
-            return io.getLookupRow(this, pageAddr, idx);
+            PendingRow row = io.getLookupRow(this, pageAddr, idx);
+
+            return flag == WITHOUT_KEY ? row : row.initKey(grp);
         }
     }
 
@@ -1964,22 +2428,27 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
          * @return Link.
          */
         long getLink(long pageAddr, int idx);
+
+        /**
+         * @param pageAddr Page address.
+         * @param idx Index.
+         * @return Cache ID or {@code 0} if Cache ID is not defined.
+         */
+        int getCacheId(long pageAddr, int idx);
     }
 
     /**
      *
      */
-    public static class PendingEntryInnerIO extends BPlusInnerIO<PendingRow> implements PendingRowIO {
-        /** */
-        public static final IOVersions<PendingEntryInnerIO> VERSIONS = new IOVersions<>(
-            new PendingEntryInnerIO(1)
-        );
-
+    private static abstract class AbstractPendingEntryInnerIO extends BPlusInnerIO<PendingRow> implements PendingRowIO {
         /**
+         * @param type Page type.
          * @param ver Page format version.
+         * @param canGetRow If we can get full row from this page.
+         * @param itemSize Single item size on page.
          */
-        PendingEntryInnerIO(int ver) {
-            super(T_PENDING_REF_INNER, ver, true, 8 + 8);
+        protected AbstractPendingEntryInnerIO(int type, int ver, boolean canGetRow, int itemSize) {
+            super(type, ver, canGetRow, itemSize);
         }
 
         /** {@inheritDoc} */
@@ -1989,6 +2458,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             PageUtils.putLong(pageAddr, off, row.expireTime);
             PageUtils.putLong(pageAddr, off + 8, row.link);
+
+            if (storeCacheId()) {
+                assert row.cacheId != UNDEFINED_CACHE_ID;
+
+                PageUtils.putInt(pageAddr, off + 16, row.cacheId);
+            }
         }
 
         /** {@inheritDoc} */
@@ -2004,14 +2479,20 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             PageUtils.putLong(dstPageAddr, dstOff, expireTime);
             PageUtils.putLong(dstPageAddr, dstOff + 8, link);
+
+            if (storeCacheId()) {
+                int cacheId = ((PendingRowIO)srcIo).getCacheId(srcPageAddr, srcIdx);
+
+                assert cacheId != UNDEFINED_CACHE_ID;
+
+                PageUtils.putInt(dstPageAddr, dstOff + 16, cacheId);
+            }
         }
 
         /** {@inheritDoc} */
         @Override public PendingRow getLookupRow(BPlusTree<PendingRow, ?> tree, long pageAddr, int idx)
             throws IgniteCheckedException {
-            return PendingRow.createRowWithKey(((PendingEntriesTree)tree).cctx,
-                getExpireTime(pageAddr, idx),
-                getLink(pageAddr, idx));
+            return new PendingRow(getCacheId(pageAddr, idx), getExpireTime(pageAddr, idx), getLink(pageAddr, idx));
         }
 
         /** {@inheritDoc} */
@@ -2023,22 +2504,24 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         @Override public long getLink(long pageAddr, int idx) {
             return PageUtils.getLong(pageAddr, offset(idx) + 8);
         }
+
+        /**
+         * @return {@code True} if cache ID has to be stored.
+         */
+        protected abstract boolean storeCacheId();
     }
 
     /**
      *
      */
-    public static class PendingEntryLeafIO extends BPlusLeafIO<PendingRow> implements PendingRowIO {
-        /** */
-        public static final IOVersions<PendingEntryLeafIO> VERSIONS = new IOVersions<>(
-            new PendingEntryLeafIO(1)
-        );
-
+    private static abstract class AbstractPendingEntryLeafIO extends BPlusLeafIO<PendingRow> implements PendingRowIO {
         /**
+         * @param type Page type.
          * @param ver Page format version.
+         * @param itemSize Single item size on page.
          */
-        PendingEntryLeafIO(int ver) {
-            super(T_PENDING_REF_LEAF, ver, 8 + 8);
+        protected AbstractPendingEntryLeafIO(int type, int ver, int itemSize) {
+            super(type, ver, itemSize);
         }
 
         /** {@inheritDoc} */
@@ -2048,6 +2531,12 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             PageUtils.putLong(pageAddr, off, row.expireTime);
             PageUtils.putLong(pageAddr, off + 8, row.link);
+
+            if (storeCacheId()) {
+                assert row.cacheId != UNDEFINED_CACHE_ID;
+
+                PageUtils.putInt(pageAddr, off + 16, row.cacheId);
+            }
         }
 
         /** {@inheritDoc} */
@@ -2063,14 +2552,20 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
 
             PageUtils.putLong(dstPageAddr, dstOff, expireTime);
             PageUtils.putLong(dstPageAddr, dstOff + 8, link);
+
+            if (storeCacheId()) {
+                int cacheId = ((PendingRowIO)srcIo).getCacheId(srcPageAddr, srcIdx);
+
+                assert cacheId != UNDEFINED_CACHE_ID;
+
+                PageUtils.putInt(dstPageAddr, dstOff + 16, cacheId);
+            }
         }
 
         /** {@inheritDoc} */
         @Override public PendingRow getLookupRow(BPlusTree<PendingRow, ?> tree, long pageAddr, int idx)
             throws IgniteCheckedException {
-            return PendingRow.createRowWithKey(((PendingEntriesTree)tree).cctx,
-                getExpireTime(pageAddr, idx),
-                getLink(pageAddr, idx));
+            return new PendingRow(getCacheId(pageAddr, idx), getExpireTime(pageAddr, idx), getLink(pageAddr, idx));
         }
 
         /** {@inheritDoc} */
@@ -2082,5 +2577,118 @@ public class IgniteCacheOffheapManagerImpl extends GridCacheManagerAdapter imple
         @Override public long getLink(long pageAddr, int idx) {
             return PageUtils.getLong(pageAddr, offset(idx) + 8);
         }
+
+        /**
+         * @return {@code True} if cache ID has to be stored.
+         */
+        protected abstract boolean storeCacheId();
+    }
+
+    /**
+     *
+     */
+    public static final class PendingEntryInnerIO extends AbstractPendingEntryInnerIO {
+        /** */
+        public static final IOVersions<PendingEntryInnerIO> VERSIONS = new IOVersions<>(
+            new PendingEntryInnerIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        PendingEntryInnerIO(int ver) {
+            super(T_PENDING_REF_INNER, ver, true, 16);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return UNDEFINED_CACHE_ID;
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return false;
+        }
+    }
+
+    /**
+     *
+     */
+    public static final class PendingEntryLeafIO extends AbstractPendingEntryLeafIO {
+        /** */
+        public static final IOVersions<PendingEntryLeafIO> VERSIONS = new IOVersions<>(
+            new PendingEntryLeafIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        PendingEntryLeafIO(int ver) {
+            super(T_PENDING_REF_LEAF, ver, 16);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return UNDEFINED_CACHE_ID;
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return false;
+        }
+    }
+
+    /**
+     *
+     */
+    public static final class CacheIdAwarePendingEntryInnerIO extends AbstractPendingEntryInnerIO {
+        /** */
+        public static final IOVersions<CacheIdAwarePendingEntryInnerIO> VERSIONS = new IOVersions<>(
+            new CacheIdAwarePendingEntryInnerIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        CacheIdAwarePendingEntryInnerIO(int ver) {
+            super(T_CACHE_ID_AWARE_PENDING_REF_INNER, ver, true, 20);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return PageUtils.getInt(pageAddr, offset(idx) + 16);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return true;
+        }
+    }
+
+    /**
+     *
+     */
+    public static final class CacheIdAwarePendingEntryLeafIO extends AbstractPendingEntryLeafIO {
+        /** */
+        public static final IOVersions<CacheIdAwarePendingEntryLeafIO> VERSIONS = new IOVersions<>(
+            new CacheIdAwarePendingEntryLeafIO(1)
+        );
+
+        /**
+         * @param ver Page format version.
+         */
+        CacheIdAwarePendingEntryLeafIO(int ver) {
+            super(T_CACHE_ID_AWARE_PENDING_REF_LEAF, ver, 20);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int getCacheId(long pageAddr, int idx) {
+            return PageUtils.getInt(pageAddr, offset(idx) + 16);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected boolean storeCacheId() {
+            return true;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/affinity/GridCacheAffinityImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/affinity/GridCacheAffinityImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/affinity/GridCacheAffinityImpl.java
index 41b3281..1cdeb3f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/affinity/GridCacheAffinityImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/affinity/GridCacheAffinityImpl.java
@@ -63,12 +63,7 @@ public class GridCacheAffinityImpl<K, V> implements Affinity<K> {
 
     /** {@inheritDoc} */
     @Override public int partitions() {
-        CacheConfiguration ccfg = cctx.config();
-
-        if (ccfg == null)
-            throw new IgniteException(FAILED_TO_FIND_CACHE_ERR_MSG + cctx.name());
-
-        return ccfg.getAffinity().partitions();
+        return cctx.group().affinityFunction().partitions();
     }
 
     /** {@inheritDoc} */
@@ -196,7 +191,7 @@ public class GridCacheAffinityImpl<K, V> implements Affinity<K> {
         int nodesCnt;
 
         if (!cctx.isLocal())
-            nodesCnt = cctx.discovery().cacheAffinityNodes(cctx.cacheId(), topVer).size();
+            nodesCnt = cctx.discovery().cacheGroupAffinityNodes(cctx.groupId(), topVer).size();
         else
             nodesCnt = 1;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
index e0076d5..cc26b21 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/CacheDataRow.java
@@ -36,11 +36,6 @@ public interface CacheDataRow extends CacheSearchRow {
     public GridCacheVersion version();
 
     /**
-     * @return Cache id. Stored only if memory policy with configured per-page eviction is used.
-     */
-    public int cacheId();
-
-    /**
      * @return Expire time.
      */
     public long expireTime();


[17/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
index 962f137..44bb04f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
@@ -72,7 +72,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     private boolean lateAffAssign;
 
     /** Affinity information for all started caches (initialized on coordinator). */
-    private ConcurrentMap<Integer, CacheHolder> caches = new ConcurrentHashMap<>();
+    private ConcurrentMap<Integer, CacheGroupHolder> grpHolders = new ConcurrentHashMap<>();
 
     /** Last topology version when affinity was calculated (updated from exchange thread). */
     private AffinityTopologyVersion affCalcVer;
@@ -81,7 +81,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     private AffinityTopologyVersion lastAffVer;
 
     /** Registered caches (updated from exchange thread). */
-    private final Map<Integer, DynamicCacheDescriptor> registeredCaches = new HashMap<>();
+    private final Map<Integer, CacheGroupDescriptor> registeredGrps = new HashMap<>();
 
     /** */
     private WaitRebalanceInfo waitInfo;
@@ -131,14 +131,14 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     void onDiscoveryEvent(int type, ClusterNode node, AffinityTopologyVersion topVer) {
         if (type == EVT_NODE_JOINED && node.isLocal()) {
             // Clean-up in case of client reconnect.
-            registeredCaches.clear();
+            registeredGrps.clear();
 
             affCalcVer = null;
 
             lastAffVer = null;
 
-            for (DynamicCacheDescriptor desc : cctx.cache().cacheDescriptors())
-                registeredCaches.put(desc.cacheId(), desc);
+            for (CacheGroupDescriptor desc : cctx.cache().cacheGroupDescriptors().values())
+                registeredGrps.put(desc.groupId(), desc);
         }
 
         if (!CU.clientNode(node) && (type == EVT_NODE_FAILED || type == EVT_NODE_JOINED || type == EVT_NODE_LEFT)) {
@@ -159,7 +159,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         if (msg.exchangeId() != null) {
             if (log.isDebugEnabled()) {
-                log.debug("Need process affinity change message [lastAffVer=" + lastAffVer +
+                log.debug("Ignore affinity change message [lastAffVer=" + lastAffVer +
                     ", msgExchId=" + msg.exchangeId() +
                     ", msgVer=" + msg.topologyVersion() + ']');
             }
@@ -193,14 +193,14 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     /**
      * @param topVer Expected topology version.
      */
-    private void onCacheStopped(AffinityTopologyVersion topVer) {
+    private void onCacheGroupStopped(AffinityTopologyVersion topVer) {
         CacheAffinityChangeMessage msg = null;
 
         synchronized (mux) {
             if (waitInfo == null || !waitInfo.topVer.equals(topVer))
                 return;
 
-            if (waitInfo.waitCaches.isEmpty()) {
+            if (waitInfo.waitGrps.isEmpty()) {
                 msg = affinityChangeMessage(waitInfo);
 
                 waitInfo = null;
@@ -218,9 +218,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
     /**
      * @param top Topology.
-     * @param checkCacheId Cache ID.
+     * @param checkGrpId Group ID.
      */
-    void checkRebalanceState(GridDhtPartitionTopology top, Integer checkCacheId) {
+    void checkRebalanceState(GridDhtPartitionTopology top, Integer checkGrpId) {
         if (!lateAffAssign)
             return;
 
@@ -234,14 +234,14 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
             assert affCalcVer.equals(waitInfo.topVer) : "Invalid affinity version [calcVer=" + affCalcVer +
                 ", waitVer=" + waitInfo.topVer + ']';
 
-            Map<Integer, UUID> partWait = waitInfo.waitCaches.get(checkCacheId);
+            Map<Integer, UUID> partWait = waitInfo.waitGrps.get(checkGrpId);
 
             boolean rebalanced = true;
 
             if (partWait != null) {
-                CacheHolder cache = caches.get(checkCacheId);
+                CacheGroupHolder grpHolder = grpHolders.get(checkGrpId);
 
-                if (cache != null) {
+                if (grpHolder != null) {
                     for (Iterator<Map.Entry<Integer, UUID>> it = partWait.entrySet().iterator(); it.hasNext(); ) {
                         Map.Entry<Integer, UUID> e = it.next();
 
@@ -261,9 +261,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                 }
 
                 if (rebalanced) {
-                    waitInfo.waitCaches.remove(checkCacheId);
+                    waitInfo.waitGrps.remove(checkGrpId);
 
-                    if (waitInfo.waitCaches.isEmpty()) {
+                    if (waitInfo.waitGrps.isEmpty()) {
                         msg = affinityChangeMessage(waitInfo);
 
                         waitInfo = null;
@@ -292,7 +292,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         Map<Integer, Map<Integer, List<UUID>>> assignmentsChange = U.newHashMap(waitInfo.assignments.size());
 
         for (Map.Entry<Integer, Map<Integer, List<ClusterNode>>> e : waitInfo.assignments.entrySet()) {
-            Integer cacheId = e.getKey();
+            Integer grpId = e.getKey();
 
             Map<Integer, List<ClusterNode>> assignment = e.getValue();
 
@@ -301,23 +301,23 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
             for (Map.Entry<Integer, List<ClusterNode>> e0 : assignment.entrySet())
                 assignment0.put(e0.getKey(), toIds0(e0.getValue()));
 
-            assignmentsChange.put(cacheId, assignment0);
+            assignmentsChange.put(grpId, assignment0);
         }
 
         return new CacheAffinityChangeMessage(waitInfo.topVer, assignmentsChange, waitInfo.deploymentIds);
     }
 
     /**
-     * @param cctx Cache context.
+     * @param grp Cache group.
      */
-    public void onCacheCreated(GridCacheContext cctx) {
-        final Integer cacheId = cctx.cacheId();
+    void onCacheGroupCreated(CacheGroupContext grp) {
+        final Integer grpId = grp.groupId();
 
-        if (!caches.containsKey(cctx.cacheId())) {
-            cctx.io().addHandler(cacheId, GridDhtAffinityAssignmentResponse.class,
+        if (!grpHolders.containsKey(grp.groupId())) {
+            cctx.io().addCacheGroupHandler(grpId, GridDhtAffinityAssignmentResponse.class,
                 new IgniteBiInClosure<UUID, GridDhtAffinityAssignmentResponse>() {
                     @Override public void apply(UUID nodeId, GridDhtAffinityAssignmentResponse res) {
-                        processAffinityAssignmentResponse(nodeId, res);
+                        processAffinityAssignmentResponse(grpId, nodeId, res);
                     }
                 });
         }
@@ -327,28 +327,14 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @param exchActions Cache change requests to execute on exchange.
      */
     private void updateCachesInfo(ExchangeActions exchActions) {
-        for (ExchangeActions.ActionData action : exchActions.cacheStopRequests()) {
-            DynamicCacheDescriptor desc = registeredCaches.remove(action.descriptor().cacheId());
+        for (CacheGroupDescriptor stopDesc : exchActions.cacheGroupsToStop()) {
+            CacheGroupDescriptor rmvd = registeredGrps.remove(stopDesc.groupId());
 
-            assert desc != null : action.request().cacheName();
+            assert rmvd != null : stopDesc.cacheOrGroupName();
         }
 
-        for (ExchangeActions.ActionData action : exchActions.cacheStartRequests()) {
-            DynamicCacheChangeRequest req = action.request();
-
-            Integer cacheId = action.descriptor().cacheId();
-
-            DynamicCacheDescriptor desc = new DynamicCacheDescriptor(cctx.kernalContext(),
-                req.startCacheConfiguration(),
-                req.cacheType(),
-                false,
-                action.descriptor().receivedFrom(),
-                action.descriptor().staticallyConfigured(),
-                action.descriptor().sql(),
-                req.deploymentId(),
-                req.schema());
-
-            DynamicCacheDescriptor old = registeredCaches.put(cacheId, desc);
+        for (CacheGroupDescriptor startDesc : exchActions.cacheGroupsToStart()) {
+            CacheGroupDescriptor old = registeredGrps.put(startDesc.groupId(), startDesc);
 
             assert old == null : old;
         }
@@ -365,7 +351,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      */
     public boolean onCacheChangeRequest(final GridDhtPartitionsExchangeFuture fut,
         boolean crd,
-        ExchangeActions exchActions)
+        final ExchangeActions exchActions)
         throws IgniteCheckedException
     {
         assert exchActions != null && !exchActions.empty() : exchActions;
@@ -373,9 +359,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         updateCachesInfo(exchActions);
 
         // Affinity did not change for existing caches.
-        forAllCaches(crd && lateAffAssign, new IgniteInClosureX<GridAffinityAssignmentCache>() {
+        forAllCacheGroups(crd && lateAffAssign, new IgniteInClosureX<GridAffinityAssignmentCache>() {
             @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException {
-                if (fut.stopping(aff.cacheId()))
+                if (exchActions.cacheGroupStopping(aff.groupId()))
                     return;
 
                 aff.clientEventTopologyChange(fut.discoveryEvent(), fut.topologyVersion());
@@ -397,8 +383,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                 nearCfg = req.nearCacheConfiguration();
             }
             else {
-                startCache = cctx.cacheContext(action.descriptor().cacheId()) == null &&
-                    CU.affinityNode(cctx.localNode(), req.startCacheConfiguration().getNodeFilter());
+                startCache = cctx.cacheContext(cacheDesc.cacheId()) == null &&
+                    CU.affinityNode(cctx.localNode(), cacheDesc.groupDescriptor().config().getNodeFilter());
             }
 
             try {
@@ -406,102 +392,122 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                     cctx.cache().prepareCacheStart(cacheDesc, nearCfg, fut.topologyVersion());
 
                     if (fut.cacheAddedOnExchange(cacheDesc.cacheId(), cacheDesc.receivedFrom())) {
-                        if (fut.discoCache().cacheAffinityNodes(req.cacheName()).isEmpty())
+                        if (fut.discoCache().cacheGroupAffinityNodes(cacheDesc.groupId()).isEmpty())
                             U.quietAndWarn(log, "No server nodes found for cache client: " + req.cacheName());
                     }
                 }
+            }
+            catch (IgniteCheckedException e) {
+                U.error(log, "Failed to initialize cache. Will try to rollback cache start routine. " +
+                    "[cacheName=" + req.cacheName() + ']', e);
 
-                if (!crd || !lateAffAssign) {
-                    GridCacheContext cacheCtx = cctx.cacheContext(cacheDesc.cacheId());
-
-                    if (cacheCtx != null && !cacheCtx.isLocal()) {
-                        boolean clientCacheStarted =
-                            req.clientStartOnly() && req.initiatingNodeId().equals(cctx.localNodeId());
+                cctx.cache().forceCloseCache(fut.topologyVersion(), action, e);
+            }
+        }
 
-                        if (clientCacheStarted)
-                            initAffinity(cacheDesc, cacheCtx.affinity().affinityCache(), fut, lateAffAssign);
-                        else if (!req.clientStartOnly()) {
-                            assert fut.topologyVersion().equals(cacheCtx.startTopologyVersion());
+            Set<Integer> gprs = new HashSet<>();
 
-                            GridAffinityAssignmentCache aff = cacheCtx.affinity().affinityCache();
+                for (ExchangeActions.ActionData action : exchActions.newAndClientCachesStartRequests()) {
+                    Integer grpId = action.descriptor().groupId();
 
-                            assert aff.lastVersion().equals(AffinityTopologyVersion.NONE) : aff.lastVersion();
+                    if (gprs.add(grpId)) {
+                        if (crd && lateAffAssign)
+                    initStartedGroupOnCoordinator(fut, action.descriptor().groupDescriptor());else  {
+                        CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-                            List<List<ClusterNode>> assignment = aff.calculate(fut.topologyVersion(),
-                                fut.discoveryEvent(), fut.discoCache());
+                        if (grp != null && !grp.isLocal() && grp.localStartVersion().equals(fut.topologyVersion())) {
+                        assert grp.affinity().lastVersion().equals(AffinityTopologyVersion.NONE) : grp.affinity().lastVersion();
 
-                            aff.initialize(fut.topologyVersion(), assignment);
-                        }
+                        initAffinity(registeredGrps.get(grp.groupId()), grp.affinity(), fut);
                     }
                 }
-                else
-                    initStartedCacheOnCoordinator(fut, cacheDesc.cacheId());
-            }
-            catch (IgniteCheckedException e) {
-                U.error(log, "Failed to initialize cache. Will try to rollback cache start routine. " +
-                    "[cacheName=" + req.cacheName() + ']', e);
-
-                cctx.cache().forceCloseCache(fut.topologyVersion(), action, e);
             }
         }
 
-        for (DynamicCacheChangeRequest req : exchActions.closeRequests(cctx.localNodeId())) {
-            Integer cacheId = CU.cacheId(req.cacheName());
+        List<ExchangeActions.ActionData> closeReqs = exchActions.closeRequests(cctx.localNodeId());
 
-            cctx.cache().blockGateway(req);
+        for (ExchangeActions.ActionData req : closeReqs) {
+            cctx.cache().blockGateway(req.request());
 
             if (crd) {
-                GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+                CacheGroupContext grp = cctx.cache().cacheGroup(req.descriptor().groupId());
+
+                assert grp != null;
+
+                if (grp.affinityNode())
+                    continue;
 
-                // Client cache was stopped, need create 'client' CacheHolder.
-                if (cacheCtx != null && !cacheCtx.affinityNode()) {
-                    CacheHolder cache = caches.remove(cacheId);
+                boolean grpClosed = false;
 
-                    assert !cache.client() : cache;
+                if (grp.sharedGroup()) {
+                    boolean cacheRemaining = false;
 
-                    cache = CacheHolder2.create(cctx,
-                        cctx.cache().cacheDescriptor(cacheId),
-                        fut,
-                        cache.affinity());
+                    for (GridCacheContext ctx : cctx.cacheContexts()) {
+                        if (ctx.group() == grp && !cacheClosed(ctx.cacheId(), closeReqs)) {
+                            cacheRemaining = true;
 
-                    caches.put(cacheId, cache);
+                            break;
+                        }
+                    }
+
+                    if (!cacheRemaining)
+                        grpClosed = true;
                 }
-            }
-        }
+                else
+                    grpClosed = true;
 
-        Set<Integer> stoppedCaches = null;
+                // All client cache groups were stopped, need create 'client' CacheGroupHolder.
+                if (grpClosed) {
+                    CacheGroupHolder grpHolder = grpHolders.remove(grp.groupId());
 
-        for (ExchangeActions.ActionData action : exchActions.cacheStopRequests()) {
-            DynamicCacheDescriptor desc = action.descriptor();
+                    if (grpHolder != null) {
+                        assert !grpHolder.client() : grpHolder;
 
+                        grpHolder = CacheGroupHolder2.create(cctx,
+                            registeredGrps.get(grp.groupId()),
+                            fut,
+                            grp.affinity());
+
+                        grpHolders.put(grp.groupId(), grpHolder);
+                    }
+                }
+            }
+        }
+
+        for (ExchangeActions.ActionData action : exchActions.cacheStopRequests())
             cctx.cache().blockGateway(action.request());
 
-            if (crd && lateAffAssign && desc.cacheConfiguration().getCacheMode() != LOCAL) {
-                CacheHolder cache = caches.remove(desc.cacheId());
+        Set<Integer> stoppedGrps = null;
+
+        if (crd && lateAffAssign) {
+            for (CacheGroupDescriptor grpDesc : exchActions.cacheGroupsToStop()) {
+                if (grpDesc.config().getCacheMode() != LOCAL) {
+                    CacheGroupHolder cacheGrp = grpHolders.remove(grpDesc.groupId());
 
-                assert cache != null : action.request();
+                    assert cacheGrp != null : grpDesc;
 
-                if (stoppedCaches == null)
-                    stoppedCaches = new HashSet<>();
+                    if (stoppedGrps == null)
+                        stoppedGrps = new HashSet<>();
 
-                stoppedCaches.add(cache.cacheId());
+                    stoppedGrps.add(cacheGrp.groupId());
 
-                cctx.io().removeHandler(desc.cacheId(), GridDhtAffinityAssignmentResponse.class);
+                    cctx.io().removeHandler(true, cacheGrp.groupId(), GridDhtAffinityAssignmentResponse.class);
+                }
             }
         }
 
-        if (stoppedCaches != null) {
+        if (stoppedGrps != null) {
             boolean notify = false;
 
             synchronized (mux) {
                 if (waitInfo != null) {
-                    for (Integer cacheId : stoppedCaches) {
-                        boolean rmv = waitInfo.waitCaches.remove(cacheId) != null;
+                    for (Integer grpId : stoppedGrps) {
+                        boolean rmv = waitInfo.waitGrps.remove(grpId) != null;
 
                         if (rmv) {
                             notify = true;
 
-                            waitInfo.assignments.remove(cacheId);
+                            waitInfo.assignments.remove(grpId);
                         }
                     }
                 }
@@ -512,7 +518,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
                 cctx.kernalContext().closure().runLocalSafe(new Runnable() {
                     @Override public void run() {
-                        onCacheStopped(topVer);
+                        onCacheGroupStopped(topVer);
                     }
                 });
             }
@@ -522,12 +528,26 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     }
 
     /**
+     * @param cacheId Cache ID.
+     * @param closeReqs Close requests.
+     * @return {@code True} if requests contain request for given cache ID.
+     */
+    private boolean cacheClosed(int cacheId, List<ExchangeActions.ActionData> closeReqs) {
+        for (ExchangeActions.ActionData req : closeReqs) {
+            if (req.descriptor().cacheId() == cacheId)
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
      *
      */
     public void removeAllCacheInfo() {
-        caches.clear();
+        grpHolders.clear();
 
-        registeredCaches.clear();
+        registeredGrps.clear();
     }
 
     /**
@@ -555,13 +575,13 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         final Map<Object, List<List<ClusterNode>>> affCache = new HashMap<>();
 
-        forAllCaches(crd, new IgniteInClosureX<GridAffinityAssignmentCache>() {
+        forAllCacheGroups(crd, new IgniteInClosureX<GridAffinityAssignmentCache>() {
             @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException {
                 List<List<ClusterNode>> idealAssignment = aff.idealAssignment();
 
                 assert idealAssignment != null;
 
-                Map<Integer, List<UUID>> cacheAssignment = assignment.get(aff.cacheId());
+                Map<Integer, List<UUID>> cacheAssignment = assignment.get(aff.groupId());
 
                 List<List<ClusterNode>> newAssignment;
 
@@ -611,25 +631,25 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         final Map<Object, List<List<ClusterNode>>> affCache = new HashMap<>();
 
-        forAllCaches(crd, new IgniteInClosureX<GridAffinityAssignmentCache>() {
+        forAllCacheGroups(crd, new IgniteInClosureX<GridAffinityAssignmentCache>() {
             @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException {
                 AffinityTopologyVersion affTopVer = aff.lastVersion();
 
                 assert affTopVer.topologyVersion() > 0 : affTopVer;
 
-                DynamicCacheDescriptor desc = registeredCaches.get(aff.cacheId());
+                CacheGroupDescriptor desc = registeredGrps.get(aff.groupId());
 
-                assert desc != null : aff.cacheName();
+                assert desc != null : aff.cacheOrGroupName();
 
                 IgniteUuid deploymentId = desc.deploymentId();
 
-                if (!deploymentId.equals(deploymentIds.get(aff.cacheId()))) {
+                if (!deploymentId.equals(deploymentIds.get(aff.groupId()))) {
                     aff.clientEventTopologyChange(exchFut.discoveryEvent(), topVer);
 
                     return;
                 }
 
-                Map<Integer, List<UUID>> change = affChange.get(aff.cacheId());
+                Map<Integer, List<UUID>> change = affChange.get(aff.groupId());
 
                 if (change != null) {
                     assert !change.isEmpty() : msg;
@@ -644,7 +664,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                         List<ClusterNode> nodes = toNodes(topVer, e.getValue());
 
                         assert !nodes.equals(assignment.get(part)) : "Assignment did not change " +
-                            "[cache=" + aff.cacheName() +
+                            "[cacheGrp=" + aff.cacheOrGroupName() +
                             ", part=" + part +
                             ", cur=" + F.nodeIds(assignment.get(part)) +
                             ", new=" + F.nodeIds(nodes) +
@@ -680,7 +700,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         if (lateAffAssign) {
             if (!locJoin) {
-                forAllCaches(crd, new IgniteInClosureX<GridAffinityAssignmentCache>() {
+                forAllCacheGroups(crd, new IgniteInClosureX<GridAffinityAssignmentCache>() {
                     @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException {
                         AffinityTopologyVersion topVer = fut.topologyVersion();
 
@@ -693,7 +713,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         }
         else {
             if (!locJoin) {
-                forAllCaches(false, new IgniteInClosureX<GridAffinityAssignmentCache>() {
+                forAllCacheGroups(false, new IgniteInClosureX<GridAffinityAssignmentCache>() {
                     @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException {
                         AffinityTopologyVersion topVer = fut.topologyVersion();
 
@@ -726,11 +746,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     }
 
     /**
+     * @param grpId Cache group ID.
      * @param nodeId Node ID.
      * @param res Response.
      */
-    private void processAffinityAssignmentResponse(UUID nodeId,
-        GridDhtAffinityAssignmentResponse res) {
+    private void processAffinityAssignmentResponse(Integer grpId, UUID nodeId, GridDhtAffinityAssignmentResponse res) {
         if (log.isDebugEnabled())
             log.debug("Processing affinity assignment response [node=" + nodeId + ", res=" + res + ']');
 
@@ -744,11 +764,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @param c Cache closure.
      * @throws IgniteCheckedException If failed
      */
-    private void forAllRegisteredCaches(IgniteInClosureX<DynamicCacheDescriptor> c) throws IgniteCheckedException {
+    private void forAllRegisteredCacheGroups(IgniteInClosureX<CacheGroupDescriptor> c) throws IgniteCheckedException {
         assert lateAffAssign;
 
-        for (DynamicCacheDescriptor cacheDesc : registeredCaches.values()) {
-            if (cacheDesc.cacheConfiguration().getCacheMode() == LOCAL)
+        for (CacheGroupDescriptor cacheDesc : registeredGrps.values()) {
+            if (cacheDesc.config().getCacheMode() == LOCAL)
                 continue;
 
             c.applyx(cacheDesc);
@@ -759,56 +779,60 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @param crd Coordinator flag.
      * @param c Closure.
      */
-    private void forAllCaches(boolean crd, IgniteInClosureX<GridAffinityAssignmentCache> c) {
+    private void forAllCacheGroups(boolean crd, IgniteInClosureX<GridAffinityAssignmentCache> c) {
         if (crd) {
-            for (CacheHolder cache : caches.values())
-                c.apply(cache.affinity());
+            for (CacheGroupHolder grp : grpHolders.values())
+                c.apply(grp.affinity());
         }
         else {
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (cacheCtx.isLocal())
+            for (CacheGroupContext grp : cctx.kernalContext().cache().cacheGroups()) {
+                if (grp.isLocal())
                     continue;
 
-                c.apply(cacheCtx.affinity().affinityCache());
+                c.apply(grp.affinity());
             }
         }
     }
 
     /**
      * @param fut Exchange future.
-     * @param cacheId Cache ID.
+     * @param grpDesc Cache group descriptor.
      * @throws IgniteCheckedException If failed.
      */
-    private void initStartedCacheOnCoordinator(GridDhtPartitionsExchangeFuture fut, final Integer cacheId)
+    private void initStartedGroupOnCoordinator(GridDhtPartitionsExchangeFuture fut, final CacheGroupDescriptor grpDesc)
         throws IgniteCheckedException {
-        CacheHolder cache = caches.get(cacheId);
+        assert grpDesc != null && grpDesc.groupId() != 0 : grpDesc;
 
-        GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+        if (grpDesc.config().getCacheMode() == LOCAL)
+            return;
 
-        if (cache == null) {
-            DynamicCacheDescriptor desc = cctx.cache().cacheDescriptor(cacheId);
+        Integer grpId = grpDesc.groupId();
 
-            assert desc != null : cacheId;
+        CacheGroupHolder grpHolder = grpHolders.get(grpId);
 
-            if (desc.cacheConfiguration().getCacheMode() == LOCAL)
-                return;
+        CacheGroupContext grp = cctx.kernalContext().cache().cacheGroup(grpId);
 
-            cache = cacheCtx != null ? new CacheHolder1(cacheCtx, null) : CacheHolder2.create(cctx, desc, fut, null);
+        if (grpHolder == null) {
+            grpHolder = grp != null ?
+                new CacheGroupHolder1(grp, null) :
+                CacheGroupHolder2.create(cctx, grpDesc, fut, null);
 
-            CacheHolder old = caches.put(cacheId, cache);
+            CacheGroupHolder old = grpHolders.put(grpId, grpHolder);
 
             assert old == null : old;
 
-            List<List<ClusterNode>> newAff = cache.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
+            List<List<ClusterNode>> newAff = grpHolder.affinity().calculate(fut.topologyVersion(),
+                fut.discoveryEvent(),
+                fut.discoCache());
 
-            cache.affinity().initialize(fut.topologyVersion(), newAff);
+            grpHolder.affinity().initialize(fut.topologyVersion(), newAff);
         }
-        else if (cache.client() && cacheCtx != null) {
-            assert cache.affinity().idealAssignment() != null;
+        else if (grpHolder.client() && grp != null) {
+            assert grpHolder.affinity().idealAssignment() != null;
 
-            cache = new CacheHolder1(cacheCtx, cache.affinity());
+            grpHolder = new CacheGroupHolder1(grp, grpHolder.affinity());
 
-            caches.put(cacheId, cache);
+            grpHolders.put(grpId, grpHolder);
         }
     }
 
@@ -824,14 +848,16 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         final GridDhtPartitionsExchangeFuture fut,
         Collection<DynamicCacheDescriptor> descs) throws IgniteCheckedException {
         for (DynamicCacheDescriptor desc : descs) {
-            if (!registeredCaches.containsKey(desc.cacheId()))
-                registeredCaches.put(desc.cacheId(), desc);
+            CacheGroupDescriptor grpDesc = desc.groupDescriptor();
+
+            if (!registeredGrps.containsKey(grpDesc.groupId()))
+                registeredGrps.put(grpDesc.groupId(), grpDesc);
         }
 
         if (crd && lateAffAssign) {
-            forAllRegisteredCaches(new IgniteInClosureX<DynamicCacheDescriptor>() {
-                @Override public void applyx(DynamicCacheDescriptor desc) throws IgniteCheckedException {
-                    CacheHolder cache = cache(fut, desc);
+            forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
+                @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException {
+                    CacheGroupHolder cache = groupHolder(fut, desc);
 
                     if (cache.affinity().lastVersion().equals(AffinityTopologyVersion.NONE)) {
                         List<List<ClusterNode>> assignment =
@@ -843,30 +869,28 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
             });
         }
         else {
-            forAllCaches(false, new IgniteInClosureX<GridAffinityAssignmentCache>() {
+            forAllCacheGroups(false, new IgniteInClosureX<GridAffinityAssignmentCache>() {
                 @Override public void applyx(GridAffinityAssignmentCache aff) throws IgniteCheckedException {
                     if (aff.lastVersion().equals(AffinityTopologyVersion.NONE))
-                        initAffinity(registeredCaches.get(aff.cacheId()), aff, fut, false);
+                        initAffinity(registeredGrps.get(aff.groupId()), aff, fut);
                 }
             });
         }
     }
 
     /**
-     * @param desc Cache descriptor.
+     * @param desc Cache group descriptor.
      * @param aff Affinity.
      * @param fut Exchange future.
-     * @param fetch Force fetch flag.
      * @throws IgniteCheckedException If failed.
      */
-    private void initAffinity(DynamicCacheDescriptor desc,
+    private void initAffinity(CacheGroupDescriptor desc,
         GridAffinityAssignmentCache aff,
-        GridDhtPartitionsExchangeFuture fut,
-        boolean fetch)
+        GridDhtPartitionsExchangeFuture fut)
         throws IgniteCheckedException {
-        assert desc != null;
+        assert desc != null : aff.cacheOrGroupName();
 
-        if (!fetch && canCalculateAffinity(desc, aff, fut)) {
+        if (canCalculateAffinity(desc, aff, fut)) {
             List<List<ClusterNode>> assignment = aff.calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
 
             aff.initialize(fut.topologyVersion(), assignment);
@@ -884,26 +908,26 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     }
 
     /**
-     * @param desc Cache descriptor.
+     * @param desc Cache group descriptor.
      * @param aff Affinity.
      * @param fut Exchange future.
      * @return {@code True} if local node can calculate affinity on it's own for this partition map exchange.
      */
-    private boolean canCalculateAffinity(DynamicCacheDescriptor desc,
+    private boolean canCalculateAffinity(CacheGroupDescriptor desc,
         GridAffinityAssignmentCache aff,
         GridDhtPartitionsExchangeFuture fut) {
-        assert desc != null : aff.cacheName();
+        assert desc != null : aff.cacheOrGroupName();
 
         // Do not request affinity from remote nodes if affinity function is not centralized.
-        if (!aff.centralizedAffinityFunction())
+        if (!lateAffAssign && !aff.centralizedAffinityFunction())
             return true;
 
         // If local node did not initiate exchange or local node is the only cache node in grid.
-        Collection<ClusterNode> affNodes = cctx.discovery().cacheAffinityNodes(aff.cacheId(), fut.topologyVersion());
+        Collection<ClusterNode> affNodes = fut.discoCache().cacheGroupAffinityNodes(aff.groupId());
 
-        return fut.cacheAddedOnExchange(aff.cacheId(), desc.receivedFrom()) ||
+        return fut.cacheGroupAddedOnExchange(aff.groupId(), desc.receivedFrom()) ||
             !fut.exchangeId().nodeId().equals(cctx.localNodeId()) ||
-            (affNodes.size() == 1 && affNodes.contains(cctx.localNode()));
+            (affNodes.isEmpty() || (affNodes.size() == 1 && affNodes.contains(cctx.localNode())));
     }
 
     /**
@@ -923,13 +947,15 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         if (lateAffAssign) {
             if (locJoin) {
                 if (crd) {
-                    forAllRegisteredCaches(new IgniteInClosureX<DynamicCacheDescriptor>() {
-                        @Override public void applyx(DynamicCacheDescriptor cacheDesc) throws IgniteCheckedException {
+                    forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
+                        @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException {
                             AffinityTopologyVersion topVer = fut.topologyVersion();
 
-                            CacheHolder cache = cache(fut, cacheDesc);
+                            CacheGroupHolder cache = groupHolder(fut, desc);
 
-                            List<List<ClusterNode>> newAff = cache.affinity().calculate(topVer, fut.discoveryEvent(), fut.discoCache());
+                            List<List<ClusterNode>> newAff = cache.affinity().calculate(topVer,
+                                fut.discoveryEvent(),
+                                fut.discoCache());
 
                             cache.affinity().initialize(topVer, newAff);
                         }
@@ -954,21 +980,21 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
             if (crd && lateAffAssign) {
                 if (log.isDebugEnabled()) {
                     log.debug("Computed new affinity after node join [topVer=" + fut.topologyVersion() +
-                        ", waitCaches=" + (info != null ? cacheNames(info.waitCaches.keySet()) : null) + ']');
+                        ", waitGrps=" + (info != null ? groupNames(info.waitGrps.keySet()) : null) + ']');
                 }
             }
         }
     }
 
     /**
-     * @param cacheIds Cache IDs.
+     * @param grpIds Cache group IDs.
      * @return Cache names.
      */
-    private String cacheNames(Collection<Integer> cacheIds) {
+    private String groupNames(Collection<Integer> grpIds) {
         StringBuilder names = new StringBuilder();
 
-        for (Integer cacheId : cacheIds) {
-            String name = registeredCaches.get(cacheId).cacheConfiguration().getName();
+        for (Integer grpId : grpIds) {
+            String name = registeredGrps.get(grpId).cacheOrGroupName();
 
             if (names.length() != 0)
                 names.append(", ");
@@ -988,21 +1014,24 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         List<GridDhtAssignmentFetchFuture> fetchFuts = new ArrayList<>();
 
-        for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (cacheCtx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            DynamicCacheDescriptor cacheDesc = registeredCaches.get(cacheCtx.cacheId());
-
-            if (cctx.localNodeId().equals(cacheDesc.receivedFrom())) {
-                List<List<ClusterNode>> assignment =
-                    cacheCtx.affinity().affinityCache().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
+            if (fut.cacheGroupAddedOnExchange(grp.groupId(), grp.receivedFrom())) {
+                List<List<ClusterNode>> assignment = grp.affinity().calculate(fut.topologyVersion(),
+                    fut.discoveryEvent(),
+                    fut.discoCache());
 
-                cacheCtx.affinity().affinityCache().initialize(fut.topologyVersion(), assignment);
+                grp.affinity().initialize(fut.topologyVersion(), assignment);
             }
             else {
+                CacheGroupDescriptor grpDesc = registeredGrps.get(grp.groupId());
+
+                assert grpDesc != null : grp.cacheOrGroupName();
+
                 GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(cctx,
-                    cacheDesc,
+                    grpDesc,
                     topVer,
                     fut.discoCache());
 
@@ -1015,9 +1044,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         for (int i = 0; i < fetchFuts.size(); i++) {
             GridDhtAssignmentFetchFuture fetchFut = fetchFuts.get(i);
 
-            Integer cacheId = fetchFut.cacheId();
+            Integer grpId = fetchFut.groupId();
 
-            fetchAffinity(fut, cctx.cacheContext(cacheId).affinity().affinityCache(), fetchFut);
+            fetchAffinity(fut, cctx.cache().cacheGroup(grpId).affinity(), fetchFut);
         }
     }
 
@@ -1076,11 +1105,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         boolean centralizedAff;
 
         if (lateAffAssign) {
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (cacheCtx.isLocal())
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (grp.isLocal())
                     continue;
 
-                cacheCtx.affinity().affinityCache().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
+                grp.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
             }
 
             centralizedAff = true;
@@ -1107,11 +1136,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     private void initAffinityNoLateAssignment(GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException {
         assert !lateAffAssign;
 
-        for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (cacheCtx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            initAffinity(registeredCaches.get(cacheCtx.cacheId()), cacheCtx.affinity().affinityCache(), fut, false);
+            initAffinity(registeredGrps.get(grp.groupId()), grp.affinity(), fut);
         }
     }
 
@@ -1122,35 +1151,38 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      */
     private IgniteInternalFuture<?> initCoordinatorCaches(final GridDhtPartitionsExchangeFuture fut)
         throws IgniteCheckedException {
+        assert lateAffAssign;
+
         final List<IgniteInternalFuture<AffinityTopologyVersion>> futs = new ArrayList<>();
 
-        forAllRegisteredCaches(new IgniteInClosureX<DynamicCacheDescriptor>() {
-            @Override public void applyx(DynamicCacheDescriptor desc) throws IgniteCheckedException {
-                CacheHolder cache = caches.get(desc.cacheId());
+        forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
+            @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException {
+                CacheGroupHolder grpHolder = grpHolders.get(desc.groupId());
 
-                if (cache != null) {
-                    if (cache.client())
-                        cache.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
+                if (grpHolder != null) {
+                    if (grpHolder.client()) // Affinity for non-client holders calculated in {@link #onServerLeft}.
+                        grpHolder.affinity().calculate(fut.topologyVersion(), fut.discoveryEvent(), fut.discoCache());
 
                     return;
                 }
 
-                final Integer cacheId = desc.cacheId();
+                // Need initialize holders and affinity if this node became coordinator during this exchange.
+                final Integer grpId = desc.groupId();
 
-                GridCacheContext cacheCtx = cctx.cacheContext(cacheId);
+                CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-                if (cacheCtx == null) {
-                    cctx.io().addHandler(desc.cacheId(), GridDhtAffinityAssignmentResponse.class,
+                if (grp == null) {
+                    cctx.io().addCacheGroupHandler(desc.groupId(), GridDhtAffinityAssignmentResponse.class,
                         new IgniteBiInClosure<UUID, GridDhtAffinityAssignmentResponse>() {
                             @Override public void apply(UUID nodeId, GridDhtAffinityAssignmentResponse res) {
-                                processAffinityAssignmentResponse(nodeId, res);
+                                processAffinityAssignmentResponse(grpId, nodeId, res);
                             }
                         }
                     );
 
-                    cache = CacheHolder2.create(cctx, desc, fut, null);
+                    grpHolder = CacheGroupHolder2.create(cctx, desc, fut, null);
 
-                    final GridAffinityAssignmentCache aff = cache.affinity();
+                    final GridAffinityAssignmentCache aff = grpHolder.affinity();
 
                     List<GridDhtPartitionsExchangeFuture> exchFuts = cctx.exchange().exchangeFutures();
 
@@ -1160,9 +1192,10 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                         ", total=" + exchFuts.size() + ']';
 
                     final GridDhtPartitionsExchangeFuture prev = exchFuts.get(idx + 1);
+
                     if (log.isDebugEnabled()) {
                         log.debug("Need initialize affinity on coordinator [" +
-                            "cache=" + desc.cacheConfiguration().getName() +
+                            "cacheGrp=" + desc.cacheOrGroupName() +
                             "prevAff=" + prev.topologyVersion() + ']');
                     }
 
@@ -1191,9 +1224,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                     futs.add(affFut);
                 }
                 else
-                    cache = new CacheHolder1(cacheCtx, null);
+                    grpHolder = new CacheGroupHolder1(grp, null);
 
-                CacheHolder old = caches.put(cache.cacheId(), cache);
+                CacheGroupHolder old = grpHolders.put(grpHolder.groupId(), grpHolder);
 
                 assert old == null : old;
             }
@@ -1219,38 +1252,36 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @return Cache holder.
      * @throws IgniteCheckedException If failed.
      */
-    private CacheHolder cache(GridDhtPartitionsExchangeFuture fut, DynamicCacheDescriptor desc)
+    private CacheGroupHolder groupHolder(GridDhtPartitionsExchangeFuture fut, final CacheGroupDescriptor desc)
         throws IgniteCheckedException {
         assert lateAffAssign;
 
-        final Integer cacheId = desc.cacheId();
-
-        CacheHolder cache = caches.get(cacheId);
+        CacheGroupHolder cacheGrp = grpHolders.get(desc.groupId());
 
-        if (cache != null)
-            return cache;
+        if (cacheGrp != null)
+            return cacheGrp;
 
-        GridCacheContext cacheCtx = cctx.cacheContext(desc.cacheId());
+        final CacheGroupContext grp = cctx.cache().cacheGroup(desc.groupId());
 
-        if (cacheCtx == null) {
-            cctx.io().addHandler(cacheId, GridDhtAffinityAssignmentResponse.class,
+        if (grp == null) {
+            cctx.io().addCacheGroupHandler(desc.groupId(), GridDhtAffinityAssignmentResponse.class,
                 new IgniteBiInClosure<UUID, GridDhtAffinityAssignmentResponse>() {
                     @Override public void apply(UUID nodeId, GridDhtAffinityAssignmentResponse res) {
-                        processAffinityAssignmentResponse(nodeId, res);
+                        processAffinityAssignmentResponse(desc.groupId(), nodeId, res);
                     }
                 }
             );
 
-            cache = CacheHolder2.create(cctx, desc, fut, null);
+            cacheGrp = CacheGroupHolder2.create(cctx, desc, fut, null);
         }
         else
-            cache = new CacheHolder1(cacheCtx, null);
+            cacheGrp = new CacheGroupHolder1(grp, null);
 
-        CacheHolder old = caches.put(cache.cacheId(), cache);
+        CacheGroupHolder old = grpHolders.put(desc.groupId(), cacheGrp);
 
         assert old == null : old;
 
-        return cache;
+        return cacheGrp;
     }
 
     /**
@@ -1261,18 +1292,20 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      */
     @Nullable private WaitRebalanceInfo initAffinityOnNodeJoin(final GridDhtPartitionsExchangeFuture fut, boolean crd)
         throws IgniteCheckedException {
+        assert lateAffAssign;
+
         AffinityTopologyVersion topVer = fut.topologyVersion();
 
         final Map<Object, List<List<ClusterNode>>> affCache = new HashMap<>();
 
         if (!crd) {
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (cacheCtx.isLocal())
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (grp.isLocal())
                     continue;
 
-                boolean latePrimary = cacheCtx.rebalanceEnabled();
+                boolean latePrimary = grp.rebalanceEnabled();
 
-                initAffinityOnNodeJoin(fut, cacheCtx.affinity().affinityCache(), null, latePrimary, affCache);
+                initAffinityOnNodeJoin(fut, grp.affinity(), null, latePrimary, affCache);
             }
 
             return null;
@@ -1280,9 +1313,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         else {
             final WaitRebalanceInfo waitRebalanceInfo = new WaitRebalanceInfo(topVer);
 
-            forAllRegisteredCaches(new IgniteInClosureX<DynamicCacheDescriptor>() {
-                @Override public void applyx(DynamicCacheDescriptor cacheDesc) throws IgniteCheckedException {
-                    CacheHolder cache = cache(fut, cacheDesc);
+            forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
+                @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException {
+                    CacheGroupHolder cache = groupHolder(fut, desc);
 
                     boolean latePrimary = cache.rebalanceEnabled;
 
@@ -1307,14 +1340,15 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         WaitRebalanceInfo rebalanceInfo,
         boolean latePrimary,
         Map<Object, List<List<ClusterNode>>> affCache)
-        throws IgniteCheckedException {
+        throws IgniteCheckedException
+    {
         assert lateAffAssign;
 
         AffinityTopologyVersion topVer = fut.topologyVersion();
 
         AffinityTopologyVersion affTopVer = aff.lastVersion();
 
-        assert affTopVer.topologyVersion() > 0 : "Affinity is not initialized [cache=" + aff.cacheName() +
+        assert affTopVer.topologyVersion() > 0 : "Affinity is not initialized [grp=" + aff.cacheOrGroupName() +
             ", topVer=" + affTopVer + ", node=" + cctx.localNodeId() + ']';
 
         List<List<ClusterNode>> curAff = aff.assignments(affTopVer);
@@ -1405,7 +1439,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         }
 
         if (rebalance != null)
-            rebalance.add(aff.cacheId(), part, newNodes.get(0).id(), newNodes);
+            rebalance.add(aff.groupId(), part, newNodes.get(0).id(), newNodes);
 
         return nodes0;
     }
@@ -1448,6 +1482,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      */
     private Map<Integer, Map<Integer, List<UUID>>> initAffinityOnNodeLeft0(final GridDhtPartitionsExchangeFuture fut)
         throws IgniteCheckedException {
+        assert lateAffAssign;
+
         final AffinityTopologyVersion topVer = fut.topologyVersion();
 
         final WaitRebalanceInfo waitRebalanceInfo = new WaitRebalanceInfo(topVer);
@@ -1456,24 +1492,24 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         final Map<Integer, Map<Integer, List<UUID>>> assignment = new HashMap<>();
 
-        forAllRegisteredCaches(new IgniteInClosureX<DynamicCacheDescriptor>() {
-            @Override public void applyx(DynamicCacheDescriptor cacheDesc) throws IgniteCheckedException {
-                CacheHolder cache = cache(fut, cacheDesc);
+        forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
+            @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException {
+                CacheGroupHolder grpHolder = groupHolder(fut, desc);
 
-                if (!cache.rebalanceEnabled)
+                if (!grpHolder.rebalanceEnabled)
                     return;
 
-                AffinityTopologyVersion affTopVer = cache.affinity().lastVersion();
+                AffinityTopologyVersion affTopVer = grpHolder.affinity().lastVersion();
 
                 assert affTopVer.topologyVersion() > 0 && !affTopVer.equals(topVer) : "Invalid affinity version " +
-                    "[last=" + affTopVer + ", futVer=" + topVer + ", cache=" + cache.name() + ']';
+                    "[last=" + affTopVer + ", futVer=" + topVer + ", grp=" + desc.cacheOrGroupName() + ']';
 
-                List<List<ClusterNode>> curAssignment = cache.affinity().assignments(affTopVer);
-                List<List<ClusterNode>> newAssignment = cache.affinity().idealAssignment();
+                List<List<ClusterNode>> curAssignment = grpHolder.affinity().assignments(affTopVer);
+                List<List<ClusterNode>> newAssignment = grpHolder.affinity().idealAssignment();
 
                 assert newAssignment != null;
 
-                GridDhtPartitionTopology top = cache.topology(fut);
+                GridDhtPartitionTopology top = grpHolder.topology(fut);
 
                 Map<Integer, List<UUID>> cacheAssignment = null;
 
@@ -1487,7 +1523,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                     List<ClusterNode> newNodes0 = null;
 
                     assert newPrimary == null || aliveNodes.contains(newPrimary) : "Invalid new primary [" +
-                        "cache=" + cache.name() +
+                        "grp=" + desc.cacheOrGroupName() +
                         ", node=" + newPrimary +
                         ", topVer=" + topVer + ']';
 
@@ -1496,7 +1532,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                             GridDhtPartitionState state = top.partitionState(newPrimary.id(), p);
 
                             if (state != GridDhtPartitionState.OWNING) {
-                                newNodes0 = latePrimaryAssignment(cache.affinity(),
+                                newNodes0 = latePrimaryAssignment(grpHolder.affinity(),
                                     p,
                                     curPrimary,
                                     newNodes,
@@ -1511,7 +1547,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                                     ClusterNode curNode = curNodes.get(i);
 
                                     if (top.partitionState(curNode.id(), p) == GridDhtPartitionState.OWNING) {
-                                        newNodes0 = latePrimaryAssignment(cache.affinity(),
+                                        newNodes0 = latePrimaryAssignment(grpHolder.affinity(),
                                             p,
                                             curNode,
                                             newNodes,
@@ -1526,7 +1562,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
                                     for (ClusterNode owner : owners) {
                                         if (aliveNodes.contains(owner)) {
-                                            newNodes0 = latePrimaryAssignment(cache.affinity(),
+                                            newNodes0 = latePrimaryAssignment(grpHolder.affinity(),
                                                 p,
                                                 owner,
                                                 newNodes,
@@ -1549,7 +1585,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                 }
 
                 if (cacheAssignment != null)
-                    assignment.put(cache.cacheId(), cacheAssignment);
+                    assignment.put(grpHolder.groupId(), cacheAssignment);
             }
         });
 
@@ -1562,7 +1598,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
             if (log.isDebugEnabled()) {
                 log.debug("Computed new affinity after node left [topVer=" + topVer +
-                    ", waitCaches=" + (info != null ? cacheNames(info.waitCaches.keySet()) : null) + ']');
+                    ", waitGrps=" + (info != null ? groupNames(info.waitGrps.keySet()) : null) + ']');
             }
         }
 
@@ -1621,7 +1657,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     /**
      *
      */
-    abstract static class CacheHolder {
+    abstract static class CacheGroupHolder {
         /** */
         private final GridAffinityAssignmentCache aff;
 
@@ -1633,7 +1669,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
          * @param aff Affinity cache.
          * @param initAff Existing affinity cache.
          */
-        CacheHolder(boolean rebalanceEnabled,
+        CacheGroupHolder(boolean rebalanceEnabled,
             GridAffinityAssignmentCache aff,
             @Nullable GridAffinityAssignmentCache initAff) {
             this.aff = aff;
@@ -1650,10 +1686,10 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         abstract boolean client();
 
         /**
-         * @return Cache ID.
+         * @return Group ID.
          */
-        int cacheId() {
-            return aff.cacheId();
+        int groupId() {
+            return aff.groupId();
         }
 
         /**
@@ -1664,13 +1700,6 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         }
 
         /**
-         * @return Cache name.
-         */
-        String name() {
-            return aff.cacheName();
-        }
-
-        /**
          * @param fut Exchange future.
          * @return Cache topology.
          */
@@ -1687,20 +1716,20 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     /**
      * Created cache is started on coordinator.
      */
-    private class CacheHolder1 extends CacheHolder {
+    private class CacheGroupHolder1 extends CacheGroupHolder {
         /** */
-        private final GridCacheContext cctx;
+        private final CacheGroupContext grp;
 
         /**
-         * @param cctx Cache context.
+         * @param grp Cache group.
          * @param initAff Current affinity.
          */
-        CacheHolder1(GridCacheContext cctx, @Nullable GridAffinityAssignmentCache initAff) {
-            super(cctx.rebalanceEnabled(), cctx.affinity().affinityCache(), initAff);
+        CacheGroupHolder1(CacheGroupContext grp, @Nullable GridAffinityAssignmentCache initAff) {
+            super(grp.rebalanceEnabled(), grp.affinity(), initAff);
 
-            assert !cctx.isLocal() : cctx.name();
+            assert !grp.isLocal() : grp;
 
-            this.cctx = cctx;
+            this.grp = grp;
         }
 
         /** {@inheritDoc} */
@@ -1709,56 +1738,41 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         }
 
         /** {@inheritDoc} */
-        @Override public int partitions() {
-            return cctx.affinity().partitions();
-        }
-
-        /** {@inheritDoc} */
-        @Override public String name() {
-            return cctx.name();
-        }
-
-        /** {@inheritDoc} */
-        @Override public int cacheId() {
-            return cctx.cacheId();
-        }
-
-        /** {@inheritDoc} */
         @Override public GridDhtPartitionTopology topology(GridDhtPartitionsExchangeFuture fut) {
-            return cctx.topology();
+            return grp.topology();
         }
     }
 
     /**
      * Created if cache is not started on coordinator.
      */
-    private static class CacheHolder2 extends CacheHolder {
+    private static class CacheGroupHolder2 extends CacheGroupHolder {
         /** */
         private final GridCacheSharedContext cctx;
 
         /**
          * @param cctx Context.
-         * @param cacheDesc Cache descriptor.
+         * @param grpDesc Cache group descriptor.
          * @param fut Exchange future.
          * @param initAff Current affinity.
          * @return Cache holder.
          * @throws IgniteCheckedException If failed.
          */
-        static CacheHolder2 create(
+        static CacheGroupHolder2 create(
             GridCacheSharedContext cctx,
-            DynamicCacheDescriptor cacheDesc,
+            CacheGroupDescriptor grpDesc,
             GridDhtPartitionsExchangeFuture fut,
             @Nullable GridAffinityAssignmentCache initAff) throws IgniteCheckedException {
-            assert cacheDesc != null;
+            assert grpDesc != null;
             assert !cctx.kernalContext().clientNode();
 
-            CacheConfiguration<?, ?> ccfg = cacheDesc.cacheConfiguration();
+            CacheConfiguration<?, ?> ccfg = grpDesc.config();
 
-            assert ccfg != null : cacheDesc;
+            assert ccfg != null : grpDesc;
             assert ccfg.getCacheMode() != LOCAL : ccfg.getName();
 
-            assert !cctx.discovery().cacheAffinityNodes(ccfg.getName(),
-                fut.topologyVersion()).contains(cctx.localNode()) : cacheDesc.cacheName();
+            assert !cctx.discovery().cacheGroupAffinityNodes(grpDesc.groupId(),
+                fut.topologyVersion()).contains(cctx.localNode()) : grpDesc.cacheOrGroupName();
 
             AffinityFunction affFunc = cctx.cache().clone(ccfg.getAffinity());
 
@@ -1768,13 +1782,14 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
             U.startLifecycleAware(F.asList(affFunc));
 
             GridAffinityAssignmentCache aff = new GridAffinityAssignmentCache(cctx.kernalContext(),
-                ccfg.getName(),
+                grpDesc.cacheOrGroupName(),
+                grpDesc.groupId(),
                 affFunc,
                 ccfg.getNodeFilter(),
                 ccfg.getBackups(),
                 ccfg.getCacheMode() == LOCAL);
 
-            return new CacheHolder2(ccfg.getRebalanceMode() != NONE, cctx, aff, initAff);
+            return new CacheGroupHolder2(ccfg.getRebalanceMode() != NONE, cctx, aff, initAff);
         }
 
         /**
@@ -1783,7 +1798,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
          * @param aff Affinity.
          * @param initAff Current affinity.
          */
-        CacheHolder2(
+        CacheGroupHolder2(
             boolean rebalanceEnabled,
             GridCacheSharedContext cctx,
             GridAffinityAssignmentCache aff,
@@ -1800,7 +1815,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
         /** {@inheritDoc} */
         @Override public GridDhtPartitionTopology topology(GridDhtPartitionsExchangeFuture fut) {
-            return cctx.exchange().clientTopology(cacheId(), fut);
+            return cctx.exchange().clientTopology(groupId(), fut);
         }
     }
 
@@ -1812,7 +1827,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         private final AffinityTopologyVersion topVer;
 
         /** */
-        private Map<Integer, Map<Integer, UUID>> waitCaches;
+        private Map<Integer, Map<Integer, UUID>> waitGrps;
 
         /** */
         private Map<Integer, Map<Integer, List<ClusterNode>>> assignments;
@@ -1831,9 +1846,9 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
          * @return {@code True} if there are partitions waiting for rebalancing.
          */
         boolean empty() {
-            if (waitCaches != null) {
-                assert !waitCaches.isEmpty();
-                assert waitCaches.size() == assignments.size();
+            if (waitGrps != null) {
+                assert !waitGrps.isEmpty();
+                assert waitGrps.size() == assignments.size();
 
                 return false;
             }
@@ -1842,34 +1857,34 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         }
 
         /**
-         * @param cacheId Cache ID.
+         * @param grpId Group ID.
          * @param part Partition.
          * @param waitNode Node rebalancing data.
          * @param assignment New assignment.
          */
-        void add(Integer cacheId, Integer part, UUID waitNode, List<ClusterNode> assignment) {
+        void add(Integer grpId, Integer part, UUID waitNode, List<ClusterNode> assignment) {
             assert !F.isEmpty(assignment) : assignment;
 
-            if (waitCaches == null) {
-                waitCaches = new HashMap<>();
+            if (waitGrps == null) {
+                waitGrps = new HashMap<>();
                 assignments = new HashMap<>();
                 deploymentIds = new HashMap<>();
             }
 
-            Map<Integer, UUID> cacheWaitParts = waitCaches.get(cacheId);
+            Map<Integer, UUID> cacheWaitParts = waitGrps.get(grpId);
 
             if (cacheWaitParts == null) {
-                waitCaches.put(cacheId, cacheWaitParts = new HashMap<>());
+                waitGrps.put(grpId, cacheWaitParts = new HashMap<>());
 
-                deploymentIds.put(cacheId, registeredCaches.get(cacheId).deploymentId());
+                deploymentIds.put(grpId, registeredGrps.get(grpId).deploymentId());
             }
 
             cacheWaitParts.put(part, waitNode);
 
-            Map<Integer, List<ClusterNode>> cacheAssignment = assignments.get(cacheId);
+            Map<Integer, List<ClusterNode>> cacheAssignment = assignments.get(grpId);
 
             if (cacheAssignment == null)
-                assignments.put(cacheId, cacheAssignment = new HashMap<>());
+                assignments.put(grpId, cacheAssignment = new HashMap<>());
 
             cacheAssignment.put(part, assignment);
         }
@@ -1877,7 +1892,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         /** {@inheritDoc} */
         @Override public String toString() {
             return "WaitRebalanceInfo [topVer=" + topVer +
-                ", caches=" + (waitCaches != null ? waitCaches.keySet() : null) + ']';
+                ", grps=" + (waitGrps != null ? waitGrps.keySet() : null) + ']';
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClientReconnectDiscoveryData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClientReconnectDiscoveryData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClientReconnectDiscoveryData.java
index a30331f..6a6f40d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClientReconnectDiscoveryData.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheClientReconnectDiscoveryData.java
@@ -31,18 +31,31 @@ public class CacheClientReconnectDiscoveryData implements Serializable {
     private static final long serialVersionUID = 0L;
 
     /** */
+    private final Map<Integer, CacheGroupInfo> clientCacheGrps;
+
+    /** */
     private final Map<String, CacheInfo> clientCaches;
 
     /**
      * @param clientCaches Information about caches started on re-joining client node.
+     * @param clientCacheGrps Information about cach groups started on re-joining client node.
      */
-    CacheClientReconnectDiscoveryData(Map<String, CacheInfo> clientCaches) {
+    CacheClientReconnectDiscoveryData(Map<Integer, CacheGroupInfo> clientCacheGrps,
+        Map<String, CacheInfo> clientCaches) {
+        this.clientCacheGrps = clientCacheGrps;
         this.clientCaches = clientCaches;
     }
 
     /**
      * @return Information about caches started on re-joining client node.
      */
+    Map<Integer, CacheGroupInfo> clientCacheGroups() {
+        return clientCacheGrps;
+    }
+
+    /**
+     * @return Information about caches started on re-joining client node.
+     */
     Map<String, CacheInfo> clientCaches() {
         return clientCaches;
     }
@@ -50,6 +63,53 @@ public class CacheClientReconnectDiscoveryData implements Serializable {
     /**
      *
      */
+    static class CacheGroupInfo implements Serializable {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** */
+        private final CacheConfiguration ccfg;
+
+        /** */
+        private final IgniteUuid deploymentId;
+
+        /** Flags added for future usage. */
+        private final long flags;
+
+        /**
+         * @param ccfg Cache group configuration.
+         * @param deploymentId Cache group deployment ID.
+         * @param flags Flags (for future usage).
+         */
+        CacheGroupInfo(CacheConfiguration ccfg,
+            IgniteUuid deploymentId,
+            long flags) {
+            assert ccfg != null;
+            assert deploymentId != null;
+
+            this.ccfg = ccfg;
+            this.deploymentId = deploymentId;
+            this.flags = flags;
+        }
+
+        /**
+         * @return Cache group configuration.
+         */
+        CacheConfiguration config() {
+            return ccfg;
+        }
+
+        /**
+         * @return Cache group deployment ID.
+         */
+        IgniteUuid deploymentId() {
+            return deploymentId;
+        }
+    }
+
+    /**
+     *
+     */
     static class CacheInfo implements Serializable {
         /** */
         private static final long serialVersionUID = 0L;
@@ -67,7 +127,7 @@ public class CacheClientReconnectDiscoveryData implements Serializable {
         private final boolean nearCache;
 
         /** Flags added for future usage. */
-        private final byte flags;
+        private final long flags;
 
         /**
          * @param ccfg Cache configuration.
@@ -80,7 +140,7 @@ public class CacheClientReconnectDiscoveryData implements Serializable {
             CacheType cacheType,
             IgniteUuid deploymentId,
             boolean nearCache,
-            byte flags) {
+            long flags) {
             assert ccfg != null;
             assert cacheType != null;
             assert deploymentId != null;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheData.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheData.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheData.java
index 3e2c259..b728d96 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheData.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheData.java
@@ -35,7 +35,10 @@ public class CacheData implements Serializable {
     private final CacheConfiguration cacheCfg;
 
     /** */
-    private final Integer cacheId;
+    private final int cacheId;
+
+    /** */
+    private final int grpId;
 
     /** */
     private final CacheType cacheType;
@@ -59,11 +62,12 @@ public class CacheData implements Serializable {
     private final boolean template;
 
     /** Flags added for future usage. */
-    private final byte flags;
+    private final long flags;
 
     /**
      * @param cacheCfg Cache configuration.
      * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param cacheType Cache ID.
      * @param deploymentId Cache deployment ID.
      * @param schema Query schema.
@@ -75,6 +79,7 @@ public class CacheData implements Serializable {
      */
     CacheData(CacheConfiguration cacheCfg,
         int cacheId,
+        int grpId,
         CacheType cacheType,
         IgniteUuid deploymentId,
         QuerySchema schema,
@@ -82,14 +87,16 @@ public class CacheData implements Serializable {
         boolean staticCfg,
         boolean sql,
         boolean template,
-        byte flags) {
+        long flags) {
         assert cacheCfg != null;
         assert rcvdFrom != null : cacheCfg.getName();
         assert deploymentId != null : cacheCfg.getName();
         assert template || cacheId != 0 : cacheCfg.getName();
+        assert template || grpId != 0 : cacheCfg.getName();
 
         this.cacheCfg = cacheCfg;
         this.cacheId = cacheId;
+        this.grpId = grpId;
         this.cacheType = cacheType;
         this.deploymentId = deploymentId;
         this.schema = schema;
@@ -101,9 +108,16 @@ public class CacheData implements Serializable {
     }
 
     /**
+     * @return Cache group ID.
+     */
+    public int groupId() {
+        return grpId;
+    }
+
+    /**
      * @return Cache ID.
      */
-    public Integer cacheId() {
+    public int cacheId() {
         return cacheId;
     }
 


[06/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
index cc90be0..a691cbc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTransactionalCache.java
@@ -87,13 +87,13 @@ public class GridNearTransactionalCache<K, V> extends GridNearCacheAdapter<K, V>
     @Override public void start() throws IgniteCheckedException {
         super.start();
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearGetResponse.class, new CI2<UUID, GridNearGetResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearGetResponse.class, new CI2<UUID, GridNearGetResponse>() {
             @Override public void apply(UUID nodeId, GridNearGetResponse res) {
                 processGetResponse(nodeId, res);
             }
         });
 
-        ctx.io().addHandler(ctx.cacheId(), GridNearLockResponse.class, new CI2<UUID, GridNearLockResponse>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridNearLockResponse.class, new CI2<UUID, GridNearLockResponse>() {
             @Override public void apply(UUID nodeId, GridNearLockResponse res) {
                 processLockResponse(nodeId, res);
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishResponse.java
index e3dcbf8..a1a2b57 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxFinishResponse.java
@@ -133,19 +133,19 @@ public class GridNearTxFinishResponse extends GridDistributedTxFinishResponse {
         }
 
         switch (writer.state()) {
-            case 7:
+            case 6:
                 if (!writer.writeByteArray("errBytes", errBytes))
                     return false;
 
                 writer.incrementState();
 
-            case 8:
+            case 7:
                 if (!writer.writeInt("miniId", miniId))
                     return false;
 
                 writer.incrementState();
 
-            case 9:
+            case 8:
                 if (!writer.writeLong("nearThreadId", nearThreadId))
                     return false;
 
@@ -167,7 +167,7 @@ public class GridNearTxFinishResponse extends GridDistributedTxFinishResponse {
             return false;
 
         switch (reader.state()) {
-            case 7:
+            case 6:
                 errBytes = reader.readByteArray("errBytes");
 
                 if (!reader.isLastRead())
@@ -175,7 +175,7 @@ public class GridNearTxFinishResponse extends GridDistributedTxFinishResponse {
 
                 reader.incrementState();
 
-            case 8:
+            case 7:
                 miniId = reader.readInt("miniId");
 
                 if (!reader.isLastRead())
@@ -183,7 +183,7 @@ public class GridNearTxFinishResponse extends GridDistributedTxFinishResponse {
 
                 reader.incrementState();
 
-            case 9:
+            case 8:
                 nearThreadId = reader.readLong("nearThreadId");
 
                 if (!reader.isLastRead())
@@ -203,7 +203,7 @@ public class GridNearTxFinishResponse extends GridDistributedTxFinishResponse {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 10;
+        return 9;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/GridLocalCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/GridLocalCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/GridLocalCache.java
index 8b538ee..69d1260 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/GridLocalCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/GridLocalCache.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryRemovedException;
+import org.apache.ignite.internal.processors.cache.GridCacheLocalConcurrentMap;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntryFactory;
 import org.apache.ignite.internal.processors.cache.GridCacheMvccCandidate;
@@ -65,9 +66,15 @@ public class GridLocalCache<K, V> extends GridCacheAdapter<K, V> {
      * @param ctx Cache registry.
      */
     public GridLocalCache(GridCacheContext<K, V> ctx) {
-        super(ctx, DFLT_START_CACHE_SIZE);
+        super(ctx);
 
-        preldr = new GridCachePreloaderAdapter(ctx);
+        preldr = new GridCachePreloaderAdapter(ctx.group());
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start() throws IgniteCheckedException {
+        if (map == null)
+            map = new GridCacheLocalConcurrentMap(ctx, entryFactory(), DFLT_START_CACHE_SIZE);
     }
 
     /** {@inheritDoc} */
@@ -80,8 +87,10 @@ public class GridLocalCache<K, V> extends GridCacheAdapter<K, V> {
         return preldr;
     }
 
-    /** {@inheritDoc} */
-    @Override protected GridCacheMapEntryFactory entryFactory() {
+    /**
+     * @return Entry factory.
+     */
+    private GridCacheMapEntryFactory entryFactory() {
         return new GridCacheMapEntryFactory() {
             @Override public GridCacheMapEntry create(
                 GridCacheContext ctx,
@@ -97,7 +106,7 @@ public class GridLocalCache<K, V> extends GridCacheAdapter<K, V> {
      * @param key Key of entry.
      * @return Cache entry.
      */
-    @Nullable GridLocalCacheEntry peekExx(KeyCacheObject key) {
+    @Nullable private GridLocalCacheEntry peekExx(KeyCacheObject key) {
         return (GridLocalCacheEntry)peekEx(key);
     }
 
@@ -215,7 +224,7 @@ public class GridLocalCache<K, V> extends GridCacheAdapter<K, V> {
         modes.backup = true;
 
         if (modes.offheap)
-            return ctx.offheap().entriesCount();
+            return ctx.offheap().cacheEntriesCount(ctx.cacheId());
         else if (modes.heap)
             return size();
         else

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
index 56041ee..384cd41 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
@@ -104,7 +104,7 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
     public GridLocalAtomicCache(GridCacheContext<K, V> ctx) {
         super(ctx);
 
-        preldr = new GridCachePreloaderAdapter(ctx);
+        preldr = new GridCachePreloaderAdapter(ctx.group());
     }
 
     /** {@inheritDoc} */
@@ -405,7 +405,7 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
             boolean skipEntry = readNoEntry;
 
             if (readNoEntry) {
-                CacheDataRow row = ctx.offheap().read(cacheKey);
+                CacheDataRow row = ctx.offheap().read(ctx, cacheKey);
 
                 if (row != null) {
                     long expireTime = row.expireTime();

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
index 06a3416..b112e1d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheDistributedQueryManager.java
@@ -104,7 +104,7 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
 
         assert cctx.config().getCacheMode() != LOCAL;
 
-        cctx.io().addHandler(cctx.cacheId(), GridCacheQueryRequest.class, new CI2<UUID, GridCacheQueryRequest>() {
+        cctx.io().addCacheHandler(cctx.cacheId(), GridCacheQueryRequest.class, new CI2<UUID, GridCacheQueryRequest>() {
             @Override public void apply(UUID nodeId, GridCacheQueryRequest req) {
                 processQueryRequest(nodeId, req);
             }
@@ -560,11 +560,11 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
 
             final Object topic = topic(cctx.nodeId(), req.id());
 
-            cctx.io().addOrderedHandler(topic, resHnd);
+            cctx.io().addOrderedCacheHandler(topic, resHnd);
 
             fut.listen(new CI1<IgniteInternalFuture<?>>() {
                 @Override public void apply(IgniteInternalFuture<?> fut) {
-                    cctx.io().removeOrderedHandler(topic);
+                    cctx.io().removeOrderedHandler(false, topic);
                 }
             });
 
@@ -744,11 +744,11 @@ public class GridCacheDistributedQueryManager<K, V> extends GridCacheQueryManage
 
             final Object topic = topic(cctx.nodeId(), req.id());
 
-            cctx.io().addOrderedHandler(topic, resHnd);
+            cctx.io().addOrderedCacheHandler(topic, resHnd);
 
             fut.listen(new CI1<IgniteInternalFuture<?>>() {
                 @Override public void apply(IgniteInternalFuture<?> fut) {
-                    cctx.io().removeOrderedHandler(topic);
+                    cctx.io().removeOrderedHandler(false, topic);
                 }
             });
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
index 07545a5..0c264db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryManager.java
@@ -863,12 +863,12 @@ public abstract class GridCacheQueryManager<K, V> extends GridCacheManagerAdapte
 
                 locPart = locPart0;
 
-                it = cctx.offheap().iterator(part);
+                it = cctx.offheap().cachePartitionIterator(cctx.cacheId(), part);
             }
             else {
                 locPart = null;
 
-                it = cctx.offheap().iterator(true, backups, topVer);
+                it = cctx.offheap().cacheIterator(cctx.cacheId(), true, backups, topVer);
             }
 
             return new PeekValueExpiryAwareIterator(it, plc, topVer, keyValFilter, qry.keepBinary(), locNode) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
index 00ddff8..9dc7817 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryRequest.java
@@ -25,6 +25,7 @@ import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
@@ -47,7 +48,7 @@ import static org.apache.ignite.internal.processors.cache.query.GridCacheQueryTy
 /**
  * Query request.
  */
-public class GridCacheQueryRequest extends GridCacheMessage implements GridCacheDeployable {
+public class GridCacheQueryRequest extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java
index 4d8e658..521aacf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheQueryResponse.java
@@ -29,7 +29,7 @@ import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeployable;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
@@ -46,7 +46,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Query request.
  */
-public class GridCacheQueryResponse extends GridCacheMessage implements GridCacheDeployable {
+public class GridCacheQueryResponse extends GridCacheIdMessage implements GridCacheDeployable {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryBatchAck.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryBatchAck.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryBatchAck.java
index 76147ee..ef0157e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryBatchAck.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryBatchAck.java
@@ -21,7 +21,7 @@ import java.nio.ByteBuffer;
 import java.util.Map;
 import java.util.UUID;
 import org.apache.ignite.internal.GridDirectMap;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheIdMessage;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType;
@@ -31,7 +31,7 @@ import org.apache.ignite.plugin.extensions.communication.MessageWriter;
 /**
  * Batch acknowledgement.
  */
-public class CacheContinuousQueryBatchAck extends GridCacheMessage {
+public class CacheContinuousQueryBatchAck extends GridCacheIdMessage {
     /** */
     private static final long serialVersionUID = 0L;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryEventBuffer.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryEventBuffer.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryEventBuffer.java
index 336f650..7a7c045 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryEventBuffer.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryEventBuffer.java
@@ -155,9 +155,12 @@ public class CacheContinuousQueryEventBuffer {
             batch = initBatch(entry.topologyVersion());
 
             if (batch == null || cntr < batch.startCntr) {
-                if (backup)
+                if (backup) {
                     backupQ.add(entry);
 
+                    return null;
+                }
+
                 return entry;
             }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java
index e5347c8..2b696a5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java
@@ -487,6 +487,70 @@ public class CacheContinuousQueryHandler<K, V> implements GridContinuousHandler
                 onEntryUpdated(evt, primary, false, null);
             }
 
+            @Override public CounterSkipContext skipUpdateCounter(final GridCacheContext cctx,
+                @Nullable CounterSkipContext skipCtx,
+                int part,
+                long cntr,
+                AffinityTopologyVersion topVer,
+                boolean primary) {
+                if (skipCtx == null)
+                    skipCtx = new CounterSkipContext(part, cntr, topVer);
+
+                if (loc) {
+                    assert !locCache;
+
+                    final Collection<CacheEntryEvent<? extends K, ? extends V>> evts = handleEvent(ctx, skipCtx.entry());
+
+                    if (!evts.isEmpty()) {
+                        if (asyncCb) {
+                            ctx.asyncCallbackPool().execute(new Runnable() {
+                                @Override public void run() {
+                                    locLsnr.onUpdated(evts);
+                                }
+                            }, part);
+                        }
+                        else
+                            skipCtx.addProcessClosure(new Runnable() {
+                                @Override public void run() {
+                                    locLsnr.onUpdated(evts);
+                                }
+                            });
+                    }
+
+                    return skipCtx;
+                }
+
+                CacheContinuousQueryEventBuffer buf = partitionBuffer(cctx, part);
+
+                final Object entryOrList = buf.processEntry(skipCtx.entry(), !primary);
+
+                if (entryOrList != null) {
+                    skipCtx.addProcessClosure(new Runnable() {
+                        @Override public void run() {
+                            try {
+                                ctx.continuous().addNotification(nodeId,
+                                    routineId,
+                                    entryOrList,
+                                    topic,
+                                    false,
+                                    true);
+                            }
+                            catch (ClusterTopologyCheckedException ex) {
+                                if (log.isDebugEnabled())
+                                    log.debug("Failed to send event notification to node, node left cluster " +
+                                        "[node=" + nodeId + ", err=" + ex + ']');
+                            }
+                            catch (IgniteCheckedException ex) {
+                                U.error(ctx.log(CU.CONTINUOUS_QRY_LOG_CATEGORY),
+                                    "Failed to send event notification to node: " + nodeId, ex);
+                            }
+                        }
+                    });
+                }
+
+                return skipCtx;
+            }
+
             @Override public void onPartitionEvicted(int part) {
                 entryBufs.remove(part);
             }
@@ -1011,7 +1075,7 @@ public class CacheContinuousQueryHandler<K, V> implements GridContinuousHandler
                         t.get1());
 
                     for (AffinityTopologyVersion topVer : t.get2()) {
-                        for (ClusterNode node : ctx.discovery().cacheAffinityNodes(cctx.name(), topVer)) {
+                        for (ClusterNode node : ctx.discovery().cacheGroupAffinityNodes(cctx.groupId(), topVer)) {
                             if (!node.isLocal()) {
                                 try {
                                     cctx.io().send(node, msg, GridIoPolicy.SYSTEM_POOL);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryListener.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryListener.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryListener.java
index 84b22f9..7da657f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryListener.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryListener.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.cache.query.continuous;
 import java.util.Map;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicAbstractUpdateFuture;
 import org.jetbrains.annotations.Nullable;
 
@@ -76,6 +77,25 @@ public interface CacheContinuousQueryListener<K, V> {
     public void skipUpdateEvent(CacheContinuousQueryEvent<K, V> evt, AffinityTopologyVersion topVer, boolean primary);
 
     /**
+     * For cache updates in shared cache group need notify others caches CQ listeners
+     * that generated counter should be skipped.
+     *
+     * @param cctx Cache context.
+     * @param skipCtx Context.
+     * @param part Partition.
+     * @param cntr Counter to skip.
+     * @param topVer Topology version.
+     * @return Context.
+     */
+    @Nullable public CounterSkipContext skipUpdateCounter(
+        GridCacheContext cctx,
+        @Nullable CounterSkipContext skipCtx,
+        int part,
+        long cntr,
+        AffinityTopologyVersion topVer,
+        boolean primary);
+
+    /**
      * @param part Partition.
      */
     public void onPartitionEvicted(int part);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
index 1a655e9..f264056 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryManager.java
@@ -127,7 +127,7 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
         topicPrefix = "CONTINUOUS_QUERY" + (cctx.name() == null ? "" : "_" + cctx.name());
 
         if (cctx.affinityNode()) {
-            cctx.io().addHandler(cctx.cacheId(), CacheContinuousQueryBatchAck.class,
+            cctx.io().addCacheHandler(cctx.cacheId(), CacheContinuousQueryBatchAck.class,
                 new CI2<UUID, CacheContinuousQueryBatchAck>() {
                     @Override public void apply(UUID uuid, CacheContinuousQueryBatchAck msg) {
                         CacheContinuousQueryListener lsnr = lsnrs.get(msg.routineId());
@@ -175,7 +175,7 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
      * @param primary Primary.
      * @param topVer Topology version.
      */
-    public void skipUpdateEvent(Map<UUID, CacheContinuousQueryListener> lsnrs,
+    private void skipUpdateEvent(Map<UUID, CacheContinuousQueryListener> lsnrs,
         KeyCacheObject key,
         int partId,
         long updCntr,
@@ -204,6 +204,24 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
     }
 
     /**
+     * @param skipCtx Context.
+     * @param part Partition number.
+     * @param cntr Update counter.
+     * @param topVer Topology version.
+     * @return Context.
+     */
+    @Nullable public CounterSkipContext skipUpdateCounter(@Nullable CounterSkipContext skipCtx,
+        int part,
+        long cntr,
+        AffinityTopologyVersion topVer,
+        boolean primary) {
+        for (CacheContinuousQueryListener lsnr : lsnrs.values())
+            skipCtx = lsnr.skipUpdateCounter(cctx, skipCtx, part, cntr, topVer, primary);
+
+        return skipCtx;
+    }
+
+    /**
      * @param internal Internal entry flag (internal key or not user cache).
      * @param preload Whether update happened during preloading.
      * @return Registered listeners.
@@ -633,7 +651,7 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
         hnd.localCache(cctx.isLocal());
 
         IgnitePredicate<ClusterNode> pred = (loc || cctx.config().getCacheMode() == CacheMode.LOCAL) ?
-            F.nodeForNodeId(cctx.localNodeId()) : cctx.config().getNodeFilter();
+            F.nodeForNodeId(cctx.localNodeId()) : cctx.group().nodeFilter();
 
         assert pred != null : cctx.config();
 
@@ -658,7 +676,10 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
         }
 
         if (notifyExisting) {
-            final Iterator<CacheDataRow> it = cctx.offheap().iterator(true, true, AffinityTopologyVersion.NONE);
+            final Iterator<CacheDataRow> it = cctx.offheap().cacheIterator(cctx.cacheId(),
+                true,
+                true,
+                AffinityTopologyVersion.NONE);
 
             locLsnr.onUpdated(new Iterable<CacheEntryEvent>() {
                 @Override public Iterator<CacheEntryEvent> iterator() {
@@ -807,16 +828,24 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
                 intLsnrCnt.incrementAndGet();
         }
         else {
-            added = lsnrs.putIfAbsent(lsnrId, lsnr) == null;
+            synchronized (this) {
+                if (lsnrCnt.get() == 0) {
+                    if (cctx.group().sharedGroup() && !cctx.isLocal())
+                        cctx.group().addCacheWithContinuousQuery(cctx);
+                }
 
-            if (added) {
-                lsnrCnt.incrementAndGet();
+                added = lsnrs.putIfAbsent(lsnrId, lsnr) == null;
 
-                lsnr.onExecution();
+                if (added)
+                    lsnrCnt.incrementAndGet();
             }
+
+            if (added)
+                lsnr.onExecution();
         }
 
-        return added ? GridContinuousHandler.RegisterStatus.REGISTERED : GridContinuousHandler.RegisterStatus.NOT_REGISTERED;
+        return added ? GridContinuousHandler.RegisterStatus.REGISTERED :
+            GridContinuousHandler.RegisterStatus.NOT_REGISTERED;
     }
 
     /**
@@ -834,11 +863,17 @@ public class CacheContinuousQueryManager extends GridCacheManagerAdapter {
             }
         }
         else {
-            if ((lsnr = lsnrs.remove(id)) != null) {
-                lsnrCnt.decrementAndGet();
+            synchronized (this) {
+                if ((lsnr = lsnrs.remove(id)) != null) {
+                    int cnt = lsnrCnt.decrementAndGet();
 
-                lsnr.onUnregister();
+                    if (cctx.group().sharedGroup() && cnt == 0 && !cctx.isLocal())
+                        cctx.group().removeCacheWithContinuousQuery(cctx);
+                }
             }
+
+            if (lsnr != null)
+                lsnr.onUnregister();
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CounterSkipContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CounterSkipContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CounterSkipContext.java
new file mode 100644
index 0000000..23702bc
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CounterSkipContext.java
@@ -0,0 +1,78 @@
+/*
+ * 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.query.continuous;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ *
+ */
+public class CounterSkipContext {
+    /** */
+    private final CacheContinuousQueryEntry entry;
+
+    /** */
+    private List<Runnable> procC;
+
+    /**
+     * @param part Partition.
+     * @param cntr Filtered counter.
+     * @param topVer Topology version.
+     */
+    CounterSkipContext(int part, long cntr, AffinityTopologyVersion topVer) {
+        entry = new CacheContinuousQueryEntry(0,
+            null,
+            null,
+            null,
+            null,
+            false,
+            part,
+            cntr,
+            topVer,
+            (byte)0);
+
+        entry.markFiltered();
+    }
+
+    /**
+     * @return Entry for filtered counter.
+     */
+    CacheContinuousQueryEntry entry() {
+        return entry;
+    }
+
+    /**
+     * @return Entries
+     */
+    @Nullable public List<Runnable> processClosures() {
+        return procC;
+    }
+
+    /**
+     * @param c Closure send
+     */
+    void addProcessClosure(Runnable c) {
+        if (procC == null)
+            procC = new ArrayList<>();
+
+        procC.add(c);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
index 5cba0cf..96af425 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxAdapter.java
@@ -1480,7 +1480,7 @@ public abstract class IgniteTxAdapter extends GridMetadataAwareAdapter implement
             if (modified)
                 cacheVal = cacheCtx.toCacheObject(cacheCtx.unwrapTemporary(val));
 
-            GridCacheOperation op = modified ? (val == null ? DELETE : UPDATE) : NOOP;
+            GridCacheOperation op = modified ? (cacheVal == null ? DELETE : UPDATE) : NOOP;
 
             if (op == NOOP) {
                 ExpiryPolicy expiry = cacheCtx.expiryForTxEntry(txEntry);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxEntry.java
index 163ed99..30aa335 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxEntry.java
@@ -565,7 +565,9 @@ public class IgniteTxEntry implements GridPeerDeployAware, Message {
      */
     public void cached(GridCacheEntryEx entry) {
         assert entry == null || entry.context() == ctx : "Invalid entry assigned to tx entry [txEntry=" + this +
-            ", entry=" + entry + ", ctxNear=" + ctx.isNear() + ", ctxDht=" + ctx.isDht() + ']';
+            ", entry=" + entry +
+            ", ctxNear=" + ctx.isNear() +
+            ", ctxDht=" + ctx.isDht() + ']';
 
         this.entry = entry;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
index a591517..ba3b2b6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxHandler.java
@@ -136,68 +136,68 @@ public class IgniteTxHandler {
         txPrepareMsgLog = ctx.logger(CU.TX_MSG_PREPARE_LOG_CATEGORY);
         txFinishMsgLog = ctx.logger(CU.TX_MSG_FINISH_LOG_CATEGORY);
 
-        ctx.io().addHandler(0, GridNearTxPrepareRequest.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridNearTxPrepareRequest.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processNearTxPrepareRequest(nodeId, (GridNearTxPrepareRequest)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridNearTxPrepareResponse.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridNearTxPrepareResponse.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processNearTxPrepareResponse(nodeId, (GridNearTxPrepareResponse)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridNearTxFinishRequest.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridNearTxFinishRequest.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processNearTxFinishRequest(nodeId, (GridNearTxFinishRequest)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridNearTxFinishResponse.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridNearTxFinishResponse.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processNearTxFinishResponse(nodeId, (GridNearTxFinishResponse)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridDhtTxPrepareRequest.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridDhtTxPrepareRequest.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processDhtTxPrepareRequest(nodeId, (GridDhtTxPrepareRequest)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridDhtTxPrepareResponse.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridDhtTxPrepareResponse.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processDhtTxPrepareResponse(nodeId, (GridDhtTxPrepareResponse)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridDhtTxFinishRequest.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridDhtTxFinishRequest.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processDhtTxFinishRequest(nodeId, (GridDhtTxFinishRequest)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridDhtTxOnePhaseCommitAckRequest.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridDhtTxOnePhaseCommitAckRequest.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processDhtTxOnePhaseCommitAckRequest(nodeId, (GridDhtTxOnePhaseCommitAckRequest)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridDhtTxFinishResponse.class, new CI2<UUID, GridCacheMessage>() {
+        ctx.io().addCacheHandler(0, GridDhtTxFinishResponse.class, new CI2<UUID, GridCacheMessage>() {
             @Override public void apply(UUID nodeId, GridCacheMessage msg) {
                 processDhtTxFinishResponse(nodeId, (GridDhtTxFinishResponse)msg);
             }
         });
 
-        ctx.io().addHandler(0, GridCacheTxRecoveryRequest.class,
+        ctx.io().addCacheHandler(0, GridCacheTxRecoveryRequest.class,
             new CI2<UUID, GridCacheTxRecoveryRequest>() {
                 @Override public void apply(UUID nodeId, GridCacheTxRecoveryRequest req) {
                     processCheckPreparedTxRequest(nodeId, req);
                 }
             });
 
-        ctx.io().addHandler(0, GridCacheTxRecoveryResponse.class,
+        ctx.io().addCacheHandler(0, GridCacheTxRecoveryResponse.class,
             new CI2<UUID, GridCacheTxRecoveryResponse>() {
                 @Override public void apply(UUID nodeId, GridCacheTxRecoveryResponse res) {
                     processCheckPreparedTxResponse(nodeId, res);
@@ -506,8 +506,8 @@ public class IgniteTxHandler {
         for (IgniteTxEntry e : F.concat(false, req.reads(), req.writes())) {
             GridCacheContext ctx = e.context();
 
-            Collection<ClusterNode> cacheNodes0 = ctx.discovery().cacheAffinityNodes(ctx.cacheId(), expVer);
-            Collection<ClusterNode> cacheNodes1 = ctx.discovery().cacheAffinityNodes(ctx.cacheId(), curVer);
+            Collection<ClusterNode> cacheNodes0 = ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), expVer);
+            Collection<ClusterNode> cacheNodes1 = ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), curVer);
 
             if (!cacheNodes0.equals(cacheNodes1) || ctx.affinity().affinityTopologyVersion().compareTo(curVer) < 0)
                 return true;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
index 5a708d7..52a0f56 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
@@ -660,6 +660,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
                                             txEntry.updateCounter())));
 
                                     if (op == CREATE || op == UPDATE) {
+                                        assert val != null : txEntry;
+
                                         GridCacheUpdateTxResult updRes = cached.innerSet(
                                             this,
                                             eventNodeId(),

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksRequest.java
index d1d6afd..94fe005 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksRequest.java
@@ -70,6 +70,16 @@ public class TxLocksRequest extends GridCacheMessage {
         this.txKeys = txKeys;
     }
 
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
     /**
      * @return Future ID.
      */
@@ -139,13 +149,13 @@ public class TxLocksRequest extends GridCacheMessage {
         }
 
         switch (writer.state()) {
-            case 3:
+            case 2:
                 if (!writer.writeLong("futId", futId))
                     return false;
 
                 writer.incrementState();
 
-            case 4:
+            case 3:
                 if (!writer.writeObjectArray("txKeysArr", txKeysArr, MessageCollectionItemType.MSG))
                     return false;
 
@@ -167,7 +177,7 @@ public class TxLocksRequest extends GridCacheMessage {
             return false;
 
         switch (reader.state()) {
-            case 3:
+            case 2:
                 futId = reader.readLong("futId");
 
                 if (!reader.isLastRead())
@@ -175,7 +185,7 @@ public class TxLocksRequest extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 4:
+            case 3:
                 txKeysArr = reader.readObjectArray("txKeysArr", MessageCollectionItemType.MSG, IgniteTxKey.class);
 
                 if (!reader.isLastRead())
@@ -195,7 +205,7 @@ public class TxLocksRequest extends GridCacheMessage {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 5;
+        return 4;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksResponse.java
index 7856eaa..a5c8f09 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxLocksResponse.java
@@ -73,6 +73,16 @@ public class TxLocksResponse extends GridCacheMessage {
         // No-op.
     }
 
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
     /**
      * @return Future ID.
      */
@@ -229,25 +239,25 @@ public class TxLocksResponse extends GridCacheMessage {
         }
 
         switch (writer.state()) {
-            case 3:
+            case 2:
                 if (!writer.writeLong("futId", futId))
                     return false;
 
                 writer.incrementState();
 
-            case 4:
+            case 3:
                 if (!writer.writeObjectArray("locksArr", locksArr, MessageCollectionItemType.MSG))
                     return false;
 
                 writer.incrementState();
 
-            case 5:
+            case 4:
                 if (!writer.writeObjectArray("nearTxKeysArr", nearTxKeysArr, MessageCollectionItemType.MSG))
                     return false;
 
                 writer.incrementState();
 
-            case 6:
+            case 5:
                 if (!writer.writeObjectArray("txKeysArr", txKeysArr, MessageCollectionItemType.MSG))
                     return false;
 
@@ -269,7 +279,7 @@ public class TxLocksResponse extends GridCacheMessage {
             return false;
 
         switch (reader.state()) {
-            case 3:
+            case 2:
                 futId = reader.readLong("futId");
 
                 if (!reader.isLastRead())
@@ -277,7 +287,7 @@ public class TxLocksResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 4:
+            case 3:
                 locksArr = reader.readObjectArray("locksArr", MessageCollectionItemType.MSG, TxLockList.class);
 
                 if (!reader.isLastRead())
@@ -285,7 +295,7 @@ public class TxLocksResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 5:
+            case 4:
                 nearTxKeysArr = reader.readObjectArray("nearTxKeysArr", MessageCollectionItemType.MSG, IgniteTxKey.class);
 
                 if (!reader.isLastRead())
@@ -293,7 +303,7 @@ public class TxLocksResponse extends GridCacheMessage {
 
                 reader.incrementState();
 
-            case 6:
+            case 5:
                 txKeysArr = reader.readObjectArray("txKeysArr", MessageCollectionItemType.MSG, IgniteTxKey.class);
 
                 if (!reader.isLastRead())
@@ -313,7 +323,7 @@ public class TxLocksResponse extends GridCacheMessage {
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 7;
+        return 6;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
index 05179af..730fab5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
@@ -134,7 +134,7 @@ public class GridClusterStateProcessor extends GridProcessorAdapter {
         cacheProc = ctx.cache();
         sharedCtx = cacheProc.context();
 
-        sharedCtx.io().addHandler(0,
+        sharedCtx.io().addCacheHandler(0,
             GridChangeGlobalStateMessageResponse.class,
             new CI2<UUID, GridChangeGlobalStateMessageResponse>() {
                 @Override public void apply(UUID nodeId, GridChangeGlobalStateMessageResponse msg) {
@@ -194,7 +194,7 @@ public class GridClusterStateProcessor extends GridProcessorAdapter {
     @Override public void stop(boolean cancel) throws IgniteCheckedException {
         super.stop(cancel);
 
-        sharedCtx.io().removeHandler(0, GridChangeGlobalStateMessageResponse.class);
+        sharedCtx.io().removeHandler(false, 0, GridChangeGlobalStateMessageResponse.class);
         ctx.event().removeLocalEventListener(lsr, EVT_NODE_LEFT, EVT_NODE_FAILED);
 
         IgniteCheckedException stopErr = new IgniteInterruptedCheckedException(
@@ -452,16 +452,17 @@ public class GridClusterStateProcessor extends GridProcessorAdapter {
 
                 sharedCtx.database().initDataBase();
 
+                // TODO IGNITE-5075 group descriptors.
                 for (CacheConfiguration cfg : cfgs) {
                     if (CU.isSystemCache(cfg.getName()))
                         if (pageStore != null)
-                            pageStore.initializeForCache(cfg);
+                            pageStore.initializeForCache(ctx.cache().cacheDescriptors().get(cfg.getName()).groupDescriptor(), cfg);
                 }
 
                 for (CacheConfiguration cfg : cfgs) {
                     if (!CU.isSystemCache(cfg.getName()))
                         if (pageStore != null)
-                            pageStore.initializeForCache(cfg);
+                            pageStore.initializeForCache(ctx.cache().cacheDescriptors().get(cfg.getName()).groupDescriptor(), cfg);
                 }
 
                 sharedCtx.database().onActivate(ctx);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 3ff4f93..faf8e1d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -1507,7 +1507,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
      * @param desc Type descriptor.
      * @return Future that will be completed when rebuilding of all indexes is finished.
      */
-    private IgniteInternalFuture<Object> rebuildIndexesFromHash(@Nullable final String cacheName,
+private IgniteInternalFuture<Object> rebuildIndexesFromHash(@Nullable final String cacheName,
         @Nullable final QueryTypeDescriptorImpl desc) {
         if (idx == null)
             return new GridFinishedFuture<>(new IgniteCheckedException("Indexing is disabled."));
@@ -1530,7 +1530,7 @@ public class GridQueryProcessor extends GridProcessorAdapter {
                     fut.onDone(e);
                 }
                 catch (Throwable e) {
-                    U.error(log, "Failed to rebuild indexes for type [cacheName=" + cacheName +
+                    U.error(log, "Failed to rebuild indexes for type [cache=" + cacheName +
                         ", name=" + desc.name() + ']', e);
 
                     fut.onDone(e);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index 51648e5..0eac972 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -17,6 +17,19 @@
 
 package org.apache.ignite.internal.processors.query;
 
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cache.QueryEntity;
@@ -30,32 +43,18 @@ import org.apache.ignite.internal.processors.cache.CacheObjectContext;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDefaultAffinityKeyMapper;
 import org.apache.ignite.internal.processors.cache.binary.CacheObjectBinaryProcessorImpl;
-import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
 import org.apache.ignite.internal.processors.query.property.QueryBinaryProperty;
 import org.apache.ignite.internal.processors.query.property.QueryClassProperty;
 import org.apache.ignite.internal.processors.query.property.QueryFieldAccessor;
 import org.apache.ignite.internal.processors.query.property.QueryMethodsAccessor;
 import org.apache.ignite.internal.processors.query.property.QueryPropertyAccessor;
 import org.apache.ignite.internal.processors.query.property.QueryReadOnlyMethodsAccessor;
+import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-import java.sql.Time;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_INDEXING_DISCOVERY_HISTORY_SIZE;
 import static org.apache.ignite.IgniteSystemProperties.getInteger;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCachePartitionsTask.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCachePartitionsTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCachePartitionsTask.java
index 69188c5..af65de0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCachePartitionsTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/cache/VisorCachePartitionsTask.java
@@ -124,7 +124,7 @@ public class VisorCachePartitionsTask extends VisorMultiNodeTask<VisorCacheParti
                     for (GridDhtLocalPartition part : locParts) {
                         int p = part.id();
 
-                        long sz = part.dataStore().size();
+                        long sz = part.dataStore().cacheSize(ca.context().cacheId());
 
                         // Pass NONE as topology version in order not to wait for topology version.
                         if (part.primary(AffinityTopologyVersion.NONE))

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAtomicSingleMessageCountSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAtomicSingleMessageCountSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAtomicSingleMessageCountSelfTest.java
index 5bd53bb..fdc98ff 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAtomicSingleMessageCountSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAtomicSingleMessageCountSelfTest.java
@@ -204,7 +204,7 @@ public class CacheAtomicSingleMessageCountSelfTest extends GridCommonAbstractTes
             throws IgniteSpiException {
 
             if (((GridIoMessage)msg).message() instanceof GridCacheMessage) {
-                int msgCacheId = ((GridCacheMessage)((GridIoMessage)msg).message()).cacheId();
+                int msgCacheId = ((GridCacheIdMessage)((GridIoMessage)msg).message()).cacheId();
 
                 if (filterCacheId == null || filterCacheId == msgCacheId) {
                     AtomicInteger cntr = cntMap.get(((GridIoMessage)msg).message().getClass());

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDeferredDeleteQueueTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDeferredDeleteQueueTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDeferredDeleteQueueTest.java
index 65037d8..18a35c6 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDeferredDeleteQueueTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDeferredDeleteQueueTest.java
@@ -116,7 +116,7 @@ public class CacheDeferredDeleteQueueTest extends GridCommonAbstractTest {
                         for (GridDhtLocalPartition p : top.currentLocalPartitions()) {
                             Collection<Object> rmvQueue = GridTestUtils.getFieldValue(p, "rmvQueue");
 
-                            if (!rmvQueue.isEmpty() || p.dataStore().size() != 0)
+                            if (!rmvQueue.isEmpty() || p.dataStore().fullSize() != 0)
                                 return false;
                         }
                     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDhtLocalPartitionAfterRemoveSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDhtLocalPartitionAfterRemoveSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDhtLocalPartitionAfterRemoveSelfTest.java
index c060eb3..7263656 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDhtLocalPartitionAfterRemoveSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheDhtLocalPartitionAfterRemoveSelfTest.java
@@ -76,7 +76,7 @@ public class CacheDhtLocalPartitionAfterRemoveSelfTest extends GridCommonAbstrac
             cache = grid(g).cache(DEFAULT_CACHE_NAME);
 
             for (GridDhtLocalPartition p : dht(cache).topology().localPartitions()) {
-                int size = p.dataStore().size();
+                int size = p.dataStore().fullSize();
 
                 assertTrue("Unexpected size: " + size, size <= 32);
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java
index 99e6afb..9f4fc80 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheExchangeMessageDuplicatedStateTest.java
@@ -285,30 +285,33 @@ public class CacheExchangeMessageDuplicatedStateTest extends GridCommonAbstractT
         Map<Integer, Integer> dupPartsData,
         GridDhtPartitionsFullMessage msg)
     {
-        Integer cacheId;
-        Integer dupCacheId;
+        int cache1Grp = groupIdForCache(ignite(0), cache1);
+        int cache2Grp = groupIdForCache(ignite(0), cache2);
 
-        if (dupPartsData.containsKey(CU.cacheId(cache1))) {
-            cacheId = CU.cacheId(cache1);
-            dupCacheId = CU.cacheId(cache2);
+        Integer grpId;
+        Integer dupGrpId;
+
+        if (dupPartsData.containsKey(cache1Grp)) {
+            grpId = cache1Grp;
+            dupGrpId = cache2Grp;
         }
         else {
-            cacheId = CU.cacheId(cache2);
-            dupCacheId = CU.cacheId(cache1);
+            grpId = cache2Grp;
+            dupGrpId = cache1Grp;
         }
 
-        assertTrue(dupPartsData.containsKey(cacheId));
-        assertEquals(dupCacheId, dupPartsData.get(cacheId));
-        assertFalse(dupPartsData.containsKey(dupCacheId));
+        assertTrue(dupPartsData.containsKey(grpId));
+        assertEquals(dupGrpId, dupPartsData.get(grpId));
+        assertFalse(dupPartsData.containsKey(dupGrpId));
 
         Map<Integer, GridDhtPartitionFullMap> parts = msg.partitions();
 
-        GridDhtPartitionFullMap emptyFullMap = parts.get(cacheId);
+        GridDhtPartitionFullMap emptyFullMap = parts.get(grpId);
 
         for (GridDhtPartitionMap map : emptyFullMap.values())
             assertEquals(0, map.map().size());
 
-        GridDhtPartitionFullMap fullMap = parts.get(dupCacheId);
+        GridDhtPartitionFullMap fullMap = parts.get(dupGrpId);
 
         for (GridDhtPartitionMap map : fullMap.values())
             assertFalse(map.map().isEmpty());
@@ -325,29 +328,32 @@ public class CacheExchangeMessageDuplicatedStateTest extends GridCommonAbstractT
         Map<Integer, Integer> dupPartsData,
         GridDhtPartitionsSingleMessage msg)
     {
-        Integer cacheId;
-        Integer dupCacheId;
+        int cache1Grp = groupIdForCache(ignite(0), cache1);
+        int cache2Grp = groupIdForCache(ignite(0), cache2);
+
+        Integer grpId;
+        Integer dupGrpId;
 
-        if (dupPartsData.containsKey(CU.cacheId(cache1))) {
-            cacheId = CU.cacheId(cache1);
-            dupCacheId = CU.cacheId(cache2);
+        if (dupPartsData.containsKey(cache1Grp)) {
+            grpId = cache1Grp;
+            dupGrpId = cache2Grp;
         }
         else {
-            cacheId = CU.cacheId(cache2);
-            dupCacheId = CU.cacheId(cache1);
+            grpId = cache2Grp;
+            dupGrpId = cache1Grp;
         }
 
-        assertTrue(dupPartsData.containsKey(cacheId));
-        assertEquals(dupCacheId, dupPartsData.get(cacheId));
-        assertFalse(dupPartsData.containsKey(dupCacheId));
+        assertTrue(dupPartsData.containsKey(grpId));
+        assertEquals(dupGrpId, dupPartsData.get(grpId));
+        assertFalse(dupPartsData.containsKey(dupGrpId));
 
         Map<Integer, GridDhtPartitionMap> parts = msg.partitions();
 
-        GridDhtPartitionMap emptyMap = parts.get(cacheId);
+        GridDhtPartitionMap emptyMap = parts.get(grpId);
 
         assertEquals(0, emptyMap.map().size());
 
-        GridDhtPartitionMap map = parts.get(dupCacheId);
+        GridDhtPartitionMap map = parts.get(dupGrpId);
 
         assertFalse(map.map().isEmpty());
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java
new file mode 100644
index 0000000..c15fa5f
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheGroupsMetricsRebalanceTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMetrics;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CacheRebalanceMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.events.Event;
+import org.apache.ignite.events.EventType;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class CacheGroupsMetricsRebalanceTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String CACHE1 = "cache1";
+
+    /** */
+    private static final String CACHE2 = "cache2";
+
+    /** */
+    private static final String GROUP = "group1";
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
+
+        CacheConfiguration cfg1 = new CacheConfiguration()
+            .setName(CACHE1)
+            .setGroupName(GROUP)
+            .setCacheMode(CacheMode.PARTITIONED)
+            .setAtomicityMode(CacheAtomicityMode.ATOMIC)
+            .setRebalanceMode(CacheRebalanceMode.ASYNC)
+            .setStatisticsEnabled(true);
+
+        CacheConfiguration cfg2 = new CacheConfiguration(cfg1)
+            .setName(CACHE2);
+
+        cfg.setCacheConfiguration(cfg1, cfg2);
+
+        return cfg;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testRebalance() throws Exception {
+        Ignite ignite = startGrids(4);
+
+        IgniteCache<Object, Object> cache1 = ignite.cache(CACHE1);
+        IgniteCache<Object, Object> cache2 = ignite.cache(CACHE2);
+
+        for (int i = 0; i < 10000; i++) {
+            cache1.put(i, CACHE1 + "-" + i);
+
+            if (i % 2 == 0)
+                cache2.put(i, CACHE2 + "-" + i);
+        }
+
+        final CountDownLatch l1 = new CountDownLatch(1);
+        final CountDownLatch l2 = new CountDownLatch(1);
+
+        startGrid(4).events().localListen(new IgnitePredicate<Event>() {
+            @Override public boolean apply(Event evt) {
+                l1.countDown();
+
+                try {
+                    assertTrue(l2.await(5, TimeUnit.SECONDS));
+                }
+                catch (InterruptedException e) {
+                    throw new AssertionError();
+                }
+
+                return false;
+            }
+        }, EventType.EVT_CACHE_REBALANCE_STOPPED);
+
+        assertTrue(l1.await(5, TimeUnit.SECONDS));
+
+        ignite = ignite(4);
+
+        CacheMetrics metrics1 = ignite.cache(CACHE1).localMetrics();
+        CacheMetrics metrics2 = ignite.cache(CACHE2).localMetrics();
+
+        l2.countDown();
+
+        long rate1 = metrics1.getRebalancingKeysRate();
+        long rate2 = metrics2.getRebalancingKeysRate();
+
+        assertTrue(rate1 > 0);
+        assertTrue(rate2 > 0);
+
+        // rate1 has to be roughly twice more than rate2.
+        double ratio = ((double)rate2 / rate1) * 100;
+
+        log.info("Ratio: " + ratio);
+
+        assertTrue(ratio > 40 && ratio < 60);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheOffheapMapEntrySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheOffheapMapEntrySelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheOffheapMapEntrySelfTest.java
index 1886c76..bdc5507 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheOffheapMapEntrySelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheOffheapMapEntrySelfTest.java
@@ -22,8 +22,7 @@ import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.internal.IgniteKernal;
-import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicCacheEntry;
-import org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtColocatedCacheEntry;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheEntry;
 import org.apache.ignite.internal.processors.cache.local.GridLocalCacheEntry;
 
@@ -97,9 +96,9 @@ public class CacheOffheapMapEntrySelfTest extends GridCacheAbstractSelfTest {
 
         checkCacheMapEntry(TRANSACTIONAL, PARTITIONED, GridNearCacheEntry.class);
 
-        checkCacheMapEntry(ATOMIC, REPLICATED, GridDhtAtomicCacheEntry.class);
+        checkCacheMapEntry(ATOMIC, REPLICATED, GridDhtCacheEntry.class);
 
-        checkCacheMapEntry(TRANSACTIONAL, REPLICATED, GridDhtColocatedCacheEntry.class);
+        checkCacheMapEntry(TRANSACTIONAL, REPLICATED, GridDhtCacheEntry.class);
     }
 
     /**
@@ -135,7 +134,7 @@ public class CacheOffheapMapEntrySelfTest extends GridCacheAbstractSelfTest {
 
             assertNotNull(entry);
 
-            assertEquals(entry.getClass(), entryCls);
+            assertEquals(entryCls, entry.getClass());
         }
         finally {
             jcache.destroy();

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheConditionalDeploymentSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheConditionalDeploymentSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheConditionalDeploymentSelfTest.java
index eefdf9d..b0d9f0e 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheConditionalDeploymentSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheConditionalDeploymentSelfTest.java
@@ -167,10 +167,16 @@ public class GridCacheConditionalDeploymentSelfTest extends GridCommonAbstractTe
         }
     }
 
+    /**
+     * @return Cache context.
+     */
     protected GridCacheContext cacheContext() {
         return ((IgniteCacheProxy)grid(0).cache(DEFAULT_CACHE_NAME)).context();
     }
 
+    /**
+     * @return IO manager.
+     */
     protected GridCacheIoManager cacheIoManager() {
         return grid(0).context().cache().context().io();
     }
@@ -182,10 +188,22 @@ public class GridCacheConditionalDeploymentSelfTest extends GridCommonAbstractTe
         /** */
         public static final short DIRECT_TYPE = 302;
 
+        /** {@inheritDoc} */
+        @Override public int handlerId() {
+            return 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean cacheGroupMessage() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
         @Override public short directType() {
             return DIRECT_TYPE;
         }
 
+        /** {@inheritDoc} */
         @Override public byte fieldsCount() {
             return 3;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLeakTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLeakTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLeakTest.java
index a13ad64..cff9745 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLeakTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLeakTest.java
@@ -24,6 +24,7 @@ import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
@@ -128,7 +129,7 @@ public class GridCacheLeakTest extends GridCommonAbstractTest {
                         GridCacheConcurrentMap map = ((IgniteKernal)grid(g)).internalCache(CACHE_NAME).map();
 
                         info("Map size for cache [g=" + g + ", size=" + map.internalSize() +
-                            ", pubSize=" + map.publicSize() + ']');
+                            ", pubSize=" + map.publicSize(CU.cacheId(CACHE_NAME)) + ']');
 
                         assertTrue("Wrong map size: " + map.internalSize(), map.internalSize() <= 8192);
                     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheOrderedPreloadingSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheOrderedPreloadingSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheOrderedPreloadingSelfTest.java
index 7562fe5..bc4f2cb 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheOrderedPreloadingSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheOrderedPreloadingSelfTest.java
@@ -68,8 +68,10 @@ public class GridCacheOrderedPreloadingSelfTest extends GridCommonAbstractTest {
     /** Caches rebalance finish times. */
     private ConcurrentHashMap8<Integer, ConcurrentHashMap8<String, Long>> times;
 
+    /** {@inheritDoc} */
     @Override protected void beforeTest() throws Exception {
         super.beforeTestsStarted();
+
         times = new ConcurrentHashMap8<>();
 
         for (int i = 0; i < GRID_CNT; i++)
@@ -93,8 +95,8 @@ public class GridCacheOrderedPreloadingSelfTest extends GridCommonAbstractTest {
         Map<IgnitePredicate<? extends Event>, int[]> listeners = new HashMap<>();
 
         listeners.put(new IgnitePredicate<CacheRebalancingEvent>() {
-            @Override public boolean apply(CacheRebalancingEvent event) {
-                times.get(gridIdx(event)).putIfAbsent(event.cacheName(), event.timestamp());
+            @Override public boolean apply(CacheRebalancingEvent evt) {
+                times.get(gridIdx(evt)).putIfAbsent(evt.cacheName(), evt.timestamp());
                 return true;
             }
         }, new int[]{EventType.EVT_CACHE_REBALANCE_STOPPED});
@@ -194,7 +196,11 @@ public class GridCacheOrderedPreloadingSelfTest extends GridCommonAbstractTest {
         }
     }
 
-    private int gridIdx(Event event) {
-        return getTestIgniteInstanceIndex((String)event.node().attributes().get(GRID_NAME_ATTR));
+    /**
+     * @param evt Event.
+     * @return Index event node.
+     */
+    private int gridIdx(Event evt) {
+        return getTestIgniteInstanceIndex((String)evt.node().attributes().get(GRID_NAME_ATTR));
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManagerSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManagerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManagerSelfTest.java
index 18c0b32..52f19b7 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManagerSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheTtlManagerSelfTest.java
@@ -112,7 +112,8 @@ public class GridCacheTtlManagerSelfTest extends GridCommonAbstractTest {
                     assertNull(g.cache(DEFAULT_CACHE_NAME).get(key));
 
                     if (!g.internalCache(DEFAULT_CACHE_NAME).context().deferredDelete())
-                        assertNull(g.internalCache(DEFAULT_CACHE_NAME).map().getEntry(g.internalCache(DEFAULT_CACHE_NAME).context().toCacheKeyObject(key)));
+                        assertNull(g.internalCache(DEFAULT_CACHE_NAME).map().getEntry(g.internalCache(DEFAULT_CACHE_NAME).context(),
+                            g.internalCache(DEFAULT_CACHE_NAME).context().toCacheKeyObject(key)));
                 }
             });
         }


[15/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeActions.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeActions.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeActions.java
index dc22669..8282b0c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeActions.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ExchangeActions.java
@@ -34,6 +34,12 @@ import org.jetbrains.annotations.Nullable;
  */
 public class ExchangeActions {
     /** */
+    private List<CacheGroupDescriptor> cacheGrpsToStart;
+
+    /** */
+    private List<CacheGroupDescriptor> cacheGrpsToStop;
+
+    /** */
     private Map<String, ActionData> cachesToStart;
 
     /** */
@@ -57,6 +63,8 @@ public class ExchangeActions {
     boolean clientOnlyExchange() {
         return F.isEmpty(cachesToStart) &&
             F.isEmpty(cachesToStop) &&
+            F.isEmpty(cacheGrpsToStart) &&
+            F.isEmpty(cacheGrpsToStop) &&
             F.isEmpty(cachesToResetLostParts);
     }
 
@@ -64,8 +72,8 @@ public class ExchangeActions {
      * @param nodeId Local node ID.
      * @return Close cache requests.
      */
-    List<DynamicCacheChangeRequest> closeRequests(UUID nodeId) {
-        List<DynamicCacheChangeRequest> res = null;
+    List<ActionData> closeRequests(UUID nodeId) {
+        List<ActionData> res = null;
 
         if (cachesToClose != null) {
             for (ActionData req : cachesToClose.values()) {
@@ -73,12 +81,12 @@ public class ExchangeActions {
                     if (res == null)
                         res = new ArrayList<>(cachesToClose.size());
 
-                    res.add(req.req);
+                    res.add(req);
                 }
             }
         }
 
-        return res != null ? res : Collections.<DynamicCacheChangeRequest>emptyList();
+        return res != null ? res : Collections.<ActionData>emptyList();
     }
 
     /**
@@ -289,6 +297,74 @@ public class ExchangeActions {
     }
 
     /**
+     * @param grpDesc Group descriptor.
+     */
+    void addCacheGroupToStart(CacheGroupDescriptor grpDesc) {
+        assert grpDesc != null;
+
+        if (cacheGrpsToStart == null)
+            cacheGrpsToStart = new ArrayList<>();
+
+        cacheGrpsToStart.add(grpDesc);
+    }
+
+    /**
+     * @return Cache groups to start.
+     */
+    public List<CacheGroupDescriptor> cacheGroupsToStart() {
+        return cacheGrpsToStart != null ? cacheGrpsToStart : Collections.<CacheGroupDescriptor>emptyList();
+    }
+
+    /**
+     * @param grpId Group ID.
+     * @return {@code True} if given cache group starting.
+     */
+    public boolean cacheGroupStarting(int grpId) {
+        if (cacheGrpsToStart != null) {
+            for (CacheGroupDescriptor grp : cacheGrpsToStart) {
+                if (grp.groupId() == grpId)
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @param grpDesc Group descriptor.
+     */
+    public void addCacheGroupToStop(CacheGroupDescriptor grpDesc) {
+        assert grpDesc != null;
+
+        if (cacheGrpsToStop == null)
+            cacheGrpsToStop = new ArrayList<>();
+
+        cacheGrpsToStop.add(grpDesc);
+    }
+
+    /**
+     * @return Cache groups to start.
+     */
+    public List<CacheGroupDescriptor> cacheGroupsToStop() {
+        return cacheGrpsToStop != null ? cacheGrpsToStop : Collections.<CacheGroupDescriptor>emptyList();
+    }
+
+    /**
+     * @param grpId Group ID.
+     * @return {@code True} if given cache group stopping.
+     */
+    public boolean cacheGroupStopping(int grpId) {
+        if (cacheGrpsToStop != null) {
+            for (CacheGroupDescriptor grp : cacheGrpsToStop) {
+                if (grp.groupId() == grpId)
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * @return {@code True} if there are no cache change actions.
      */
     public boolean empty() {
@@ -296,6 +372,8 @@ public class ExchangeActions {
             F.isEmpty(clientCachesToStart) &&
             F.isEmpty(cachesToStop) &&
             F.isEmpty(cachesToClose) &&
+            F.isEmpty(cacheGrpsToStart) &&
+            F.isEmpty(cacheGrpsToStop) &&
             F.isEmpty(cachesToResetLostParts);
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
index a84d093..2ee6c33 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAdapter.java
@@ -303,15 +303,6 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
      */
     @SuppressWarnings("OverriddenMethodCallDuringObjectConstruction")
     protected GridCacheAdapter(GridCacheContext<K, V> ctx) {
-        this(ctx, DFLT_START_CACHE_SIZE);
-    }
-
-    /**
-     * @param ctx Cache context.
-     * @param startSize Start size.
-     */
-    @SuppressWarnings("OverriddenMethodCallDuringObjectConstruction")
-    protected GridCacheAdapter(GridCacheContext<K, V> ctx, int startSize) {
         this(ctx, null);
     }
 
@@ -390,7 +381,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
      * @param e Map entry.
      */
     public void incrementSize(GridCacheMapEntry e) {
-        map.incrementPublicSize(e);
+        map.incrementPublicSize(null, e);
     }
 
     /**
@@ -398,7 +389,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
      * @param e Map entry.
      */
     public void decrementSize(GridCacheMapEntry e) {
-        map.decrementPublicSize(e);
+        map.decrementPublicSize(null, e);
     }
 
     /**
@@ -550,21 +541,12 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
     }
 
     /**
-     * @return Entry factory.
-     */
-    protected abstract GridCacheMapEntryFactory entryFactory();
-
-    /**
      * Starts this cache. Child classes should override this method
      * to provide custom start-up behavior.
      *
      * @throws IgniteCheckedException If start failed.
      */
-    public void start() throws IgniteCheckedException {
-        if (map == null) {
-            map = new GridCacheLocalConcurrentMap(ctx, entryFactory(), DFLT_START_CACHE_SIZE);
-        }
-    }
+    public abstract void start() throws IgniteCheckedException;
 
     /**
      * Startup info.
@@ -718,7 +700,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
                 IgniteCacheOffheapManager offheapMgr = ctx.isNear() ? ctx.near().dht().context().offheap() : ctx.offheap();
 
-                its.add(offheapMgr.<K, V>entriesIterator(modes.primary, modes.backup, topVer, ctx.keepBinary()));
+                its.add(offheapMgr.<K, V>cacheEntriesIterator(ctx, modes.primary, modes.backup, topVer, ctx.keepBinary()));
             }
         }
         else if (modes.heap) {
@@ -945,7 +927,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
      * @return Entry (never {@code null}).
      */
     public GridCacheEntryEx entryEx(KeyCacheObject key, AffinityTopologyVersion topVer) {
-        GridCacheEntryEx e = map.putEntryIfObsoleteOrAbsent(topVer, key, true, false);
+        GridCacheEntryEx e = map.putEntryIfObsoleteOrAbsent(ctx, topVer, key, true, false);
 
         assert e != null;
 
@@ -961,10 +943,11 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
      */
     @Nullable private GridCacheEntryEx entry0(KeyCacheObject key, AffinityTopologyVersion topVer, boolean create,
         boolean touch) {
-        GridCacheMapEntry cur = map.getEntry(key);
+        GridCacheMapEntry cur = map.getEntry(ctx, key);
 
         if (cur == null || cur.obsolete()) {
             cur = map.putEntryIfObsoleteOrAbsent(
+                ctx,
                 topVer,
                 key,
                 create, touch);
@@ -984,7 +967,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
      * @return Set of internal cached entry representations.
      */
     public final Iterable<? extends GridCacheEntryEx> allEntries() {
-        return map.entries();
+        return map.entries(ctx.cacheId());
     }
 
     /** {@inheritDoc} */
@@ -994,7 +977,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
     /** {@inheritDoc} */
     @Override public final Set<K> keySet() {
-        return new KeySet(map.entrySet());
+        return new KeySet(map.entrySet(ctx.cacheId()));
     }
 
     /**
@@ -1004,7 +987,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
     public final void removeIfObsolete(KeyCacheObject key) {
         assert key != null;
 
-        GridCacheMapEntry entry = map.getEntry(key);
+        GridCacheMapEntry entry = map.getEntry(ctx, key);
 
         if (entry != null && entry.obsolete())
             removeEntry(entry);
@@ -1913,7 +1896,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
                             boolean skipEntry = readNoEntry;
 
                             if (readNoEntry) {
-                                CacheDataRow row = ctx.offheap().read(key);
+                                CacheDataRow row = ctx.offheap().read(ctx, key);
 
                                 if (row != null) {
                                     long expireTime = row.expireTime();
@@ -2908,7 +2891,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
         List<K> keys = new ArrayList<>(Math.min(REMOVE_ALL_KEYS_BATCH, size()));
 
         do {
-            for (Iterator<CacheDataRow> it = ctx.offheap().iterator(true, true, null);
+            for (Iterator<CacheDataRow> it = ctx.offheap().cacheIterator(ctx.cacheId(), true, true, null);
                 it.hasNext() && keys.size() < REMOVE_ALL_KEYS_BATCH; )
                 keys.add((K)it.next().key());
 
@@ -3711,7 +3694,8 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
         try {
             return ctx.kernalContext().task().execute(
                 new SizeTask(ctx.name(), ctx.affinity().affinityTopologyVersion(), peekModes), null);
-        } catch (ClusterGroupEmptyException e) {
+        }
+        catch (ClusterGroupEmptyException e) {
             return new GridFinishedFuture<>(0);
         }
     }
@@ -3773,12 +3757,12 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
     /** {@inheritDoc} */
     @Override public int size() {
-        return map.publicSize();
+        return map.publicSize(ctx.cacheId());
     }
 
     /** {@inheritDoc} */
     @Override public long sizeLong() {
-        return map.publicSize();
+        return map.publicSize(ctx.cacheId());
     }
 
     /** {@inheritDoc} */
@@ -3788,12 +3772,12 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
     /** {@inheritDoc} */
     @Override public int primarySize() {
-        return map.publicSize();
+        return map.publicSize(ctx.cacheId());
     }
 
     /** {@inheritDoc} */
     @Override public long primarySizeLong() {
-        return map.publicSize();
+        return map.publicSize(ctx.cacheId());
     }
 
     /** {@inheritDoc} */
@@ -3910,7 +3894,10 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
         try {
             IgniteCacheOffheapManager mgr = ctx.offheap();
 
-            return mgr != null ? mgr.entriesCount(false, true, ctx.affinity().affinityTopologyVersion()) : -1;
+            return mgr != null ? mgr.cacheEntriesCount(ctx.cacheId(),
+                false,
+                true,
+                ctx.affinity().affinityTopologyVersion()) : -1;
         }
         catch (IgniteCheckedException ignore) {
             return 0;
@@ -4432,7 +4419,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
     public Set<Cache.Entry<K, V>> entrySet(@Nullable CacheEntryPredicate... filter) {
         boolean keepBinary = ctx.keepBinary();
 
-        return new EntrySet(map.entrySet(filter), keepBinary);
+        return new EntrySet(map.entrySet(ctx.cacheId(), filter), keepBinary);
     }
 
     /**
@@ -6504,7 +6491,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
         /** {@inheritDoc} */
         @Override public boolean contains(Object o) {
-            GridCacheMapEntry entry = map.getEntry(ctx.toCacheKeyObject(o));
+            GridCacheMapEntry entry = map.getEntry(ctx, ctx.toCacheKeyObject(o));
 
             return entry != null && internalSet.contains(entry);
         }
@@ -6594,7 +6581,7 @@ public abstract class GridCacheAdapter<K, V> implements IgniteInternalCache<K, V
 
         /** {@inheritDoc} */
         @Override public boolean contains(Object o) {
-            GridCacheMapEntry entry = map.getEntry(ctx.toCacheKeyObject(o));
+            GridCacheMapEntry entry = map.getEntry(ctx, ctx.toCacheKeyObject(o));
 
             return entry != null && internalSet.contains(entry);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
index 2bb6f6c..702b848 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAffinityManager.java
@@ -28,14 +28,12 @@ import org.apache.ignite.binary.BinaryObject;
 import org.apache.ignite.cache.affinity.AffinityFunction;
 import org.apache.ignite.cache.affinity.AffinityKeyMapper;
 import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.internal.IgniteClientDisconnectedCheckedException;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.affinity.GridAffinityAssignmentCache;
 import org.apache.ignite.internal.util.future.GridFinishedFuture;
 import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.lang.IgniteFuture;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -63,12 +61,7 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
         affFunction = cctx.config().getAffinity();
         affMapper = cctx.config().getAffinityMapper();
 
-        aff = new GridAffinityAssignmentCache(cctx.kernalContext(),
-            cctx.name(),
-            affFunction,
-            cctx.config().getNodeFilter(),
-            cctx.config().getBackups(),
-            cctx.isLocal());
+        aff = cctx.group().affinity();
     }
 
     /** {@inheritDoc} */
@@ -79,42 +72,6 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
     }
 
     /** {@inheritDoc} */
-    @Override protected void onKernalStop0(boolean cancel) {
-        cancelFutures();
-    }
-
-    /**
-     *
-     */
-    public void cancelFutures() {
-        if (!starting.get())
-            // Ignoring attempt to stop manager that has never been started.
-            return;
-
-        IgniteCheckedException err =
-            new IgniteCheckedException("Failed to wait for topology update, cache (or node) is stopping.");
-
-        if (aff != null)
-            aff.cancelFutures(err);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onDisconnected(IgniteFuture reconnectFut) {
-        IgniteCheckedException err = new IgniteClientDisconnectedCheckedException(reconnectFut,
-            "Failed to wait for topology update, client disconnected.");
-
-        if (aff != null)
-            aff.cancelFutures(err);
-    }
-
-    /**
-     *
-     */
-    public void onReconnected() {
-        aff.onReconnected();
-    }
-
-    /** {@inheritDoc} */
     @Override protected void stop0(boolean cancel, boolean destroy) {
         aff = null;
     }
@@ -443,23 +400,6 @@ public class GridCacheAffinityManager extends GridCacheManagerAdapter {
     }
 
     /**
-     * Dumps debug information.
-     */
-    public void dumpDebugInfo() {
-        GridAffinityAssignmentCache aff0 = aff;
-
-        if (aff0 != null)
-            aff0.dumpDebugInfo();
-    }
-
-    /**
-     * @return Affinity cache.
-     */
-    public GridAffinityAssignmentCache affinityCache() {
-        return aff;
-    }
-
-    /**
      * @param part Partition.
      * @param startVer Start version.
      * @param endVer End version.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java
index 4af2518..7d97159 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheAttributes.java
@@ -52,6 +52,7 @@ public class GridCacheAttributes implements Serializable {
 
     /**
      * @param cfg Cache configuration.
+     * @param sql SQL flag.
      */
     public GridCacheAttributes(CacheConfiguration cfg, boolean sql) {
         ccfg = cfg;
@@ -60,10 +61,10 @@ public class GridCacheAttributes implements Serializable {
     }
 
     /**
-     * Public no-arg constructor for {@link Externalizable}.
+     * @return Cache group name.
      */
-    public GridCacheAttributes() {
-        // No-op.
+    public String groupName() {
+        return ccfg.getGroupName();
     }
 
     /**
@@ -278,6 +279,20 @@ public class GridCacheAttributes implements Serializable {
     }
 
     /**
+     * @return Node filter class name.
+     */
+    String nodeFilterClassName() {
+        return className(ccfg.getNodeFilter());
+    }
+
+    /**
+     * @return Topology validator class name.
+     */
+    String topologyValidatorClassName() {
+        return className(ccfg.getTopologyValidator());
+    }
+
+    /**
      * @return SQL flag.
      */
     public boolean sql() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheClearAllRunnable.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheClearAllRunnable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheClearAllRunnable.java
index df19225..d37cecb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheClearAllRunnable.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheClearAllRunnable.java
@@ -82,7 +82,7 @@ public class GridCacheClearAllRunnable<K, V> implements Runnable {
 
         if (!ctx.isNear()) {
             if (id == 0)
-                ctx.offheap().clear(readers);
+                ctx.offheap().clearCache(ctx, readers);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMap.java
index 9f20d64..282faaf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMap.java
@@ -18,7 +18,10 @@
 package org.apache.ignite.internal.processors.cache;
 
 import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.util.typedef.internal.S;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -27,15 +30,17 @@ import org.jetbrains.annotations.Nullable;
 public interface GridCacheConcurrentMap {
     /**
      * Returns the entry associated with the specified key in the
-     * HashMap.  Returns null if the HashMap contains no mapping
+     * HashMap. Returns null if the HashMap contains no mapping
      * for this key.
      *
+     * @param ctx Cache context.
      * @param key Key.
      * @return Entry.
      */
-    @Nullable public GridCacheMapEntry getEntry(KeyCacheObject key);
+    @Nullable public GridCacheMapEntry getEntry(GridCacheContext ctx, KeyCacheObject key);
 
     /**
+     * @param ctx Cache context.
      * @param topVer Topology version.
      * @param key Key.
      * @param create Create flag.
@@ -44,6 +49,7 @@ public interface GridCacheConcurrentMap {
      * couldn't be created.
      */
     @Nullable public GridCacheMapEntry putEntryIfObsoleteOrAbsent(
+        GridCacheContext ctx,
         AffinityTopologyVersion topVer,
         KeyCacheObject key,
         boolean create,
@@ -70,39 +76,66 @@ public interface GridCacheConcurrentMap {
      * It excludes entries that are marked as deleted.
      * It also does not include entries from underlying data store.
      *
+     * @param cacheId Cache ID.
      * @return the number of publicly available key-value mappings in this map.
      */
-    public int publicSize();
+    public int publicSize(int cacheId);
 
     /**
      * Increments public size.
      *
      * @param e Entry that caused public size change.
+     * @param hld Cache map (passed as optimization to avoid cache map lookup for shared groups).
      */
-    public void incrementPublicSize(GridCacheEntryEx e);
+    public void incrementPublicSize(@Nullable CacheMapHolder hld, GridCacheEntryEx e);
 
     /**
      * Decrements public size.
      *
      * @param e Entry that caused public size change.
+     * @param hld Cache map (passed as optimization to avoid cache map lookup for shared groups).
      */
-    public void decrementPublicSize(GridCacheEntryEx e);
+    public void decrementPublicSize(@Nullable CacheMapHolder hld, GridCacheEntryEx e);
 
     /**
+     * @param cacheId Cache ID.
      * @param filter Filter.
      * @return Iterable of the mappings contained in this map, excluding entries in unvisitable state.
      */
-    public Iterable<GridCacheMapEntry> entries(CacheEntryPredicate... filter);
+    public Iterable<GridCacheMapEntry> entries(int cacheId, CacheEntryPredicate... filter);
 
     /**
+     * @param cacheId Cache ID.
      * @param filter Filter.
-     * @return Iterable of the mappings contained in this map, including entries in unvisitable state.
+     * @return Set of the mappings contained in this map.
      */
-    public Iterable<GridCacheMapEntry> allEntries(CacheEntryPredicate... filter);
+    public Set<GridCacheMapEntry> entrySet(int cacheId, CacheEntryPredicate... filter);
 
     /**
-     * @param filter Filter.
-     * @return Set of the mappings contained in this map.
+     *
      */
-    public Set<GridCacheMapEntry> entrySet(CacheEntryPredicate... filter);
+    static class CacheMapHolder {
+        /** */
+        public final GridCacheContext cctx;
+
+        /** */
+        public final AtomicInteger size = new AtomicInteger();
+
+        /** */
+        public final ConcurrentMap<KeyCacheObject, GridCacheMapEntry> map;
+
+        /**
+         * @param cctx Cache context.
+         * @param map Map.
+         */
+        public CacheMapHolder(GridCacheContext cctx, ConcurrentMap<KeyCacheObject, GridCacheMapEntry> map) {
+            this.cctx = cctx;
+            this.map = map;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return S.toString(CacheMapHolder.class, this);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMapImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMapImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMapImpl.java
index b02a2b7..37f2a51 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMapImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheConcurrentMapImpl.java
@@ -20,15 +20,14 @@ package org.apache.ignite.internal.processors.cache;
 
 import java.util.AbstractSet;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.Set;
-import java.util.concurrent.ConcurrentMap;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.lang.IgniteUuid;
 import org.jetbrains.annotations.Nullable;
-import org.jsr166.ConcurrentHashMap8;
 
 import static org.apache.ignite.events.EventType.EVT_CACHE_ENTRY_CREATED;
 import static org.apache.ignite.events.EventType.EVT_CACHE_ENTRY_DESTROYED;
@@ -37,78 +36,49 @@ import static org.apache.ignite.events.EventType.EVT_CACHE_ENTRY_DESTROYED;
  * Implementation of concurrent cache map.
  */
 public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentMap {
-    /** Default load factor. */
-    private static final float DFLT_LOAD_FACTOR = 0.75f;
-
-    /** Default concurrency level. */
-    private static final int DFLT_CONCUR_LEVEL = Runtime.getRuntime().availableProcessors() * 2;
-
-    /** Internal map. */
-    private final ConcurrentMap<KeyCacheObject, GridCacheMapEntry> map;
-
     /** Map entry factory. */
     private final GridCacheMapEntryFactory factory;
 
-    /** Cache context. */
-    private final GridCacheContext ctx;
-
     /**
      * Creates a new, empty map with the specified initial
      * capacity.
      *
-     * @param ctx Cache context.
      * @param factory Entry factory.
-     * @param initialCapacity the initial capacity. The implementation
-     *      performs internal sizing to accommodate this many elements.
 
      * @throws IllegalArgumentException if the initial capacity is
      *      negative.
      */
-    public GridCacheConcurrentMapImpl(GridCacheContext ctx, GridCacheMapEntryFactory factory, int initialCapacity) {
-        this(ctx, factory, initialCapacity, DFLT_LOAD_FACTOR, DFLT_CONCUR_LEVEL);
+    public GridCacheConcurrentMapImpl(GridCacheMapEntryFactory factory) {
+        this.factory = factory;
     }
 
-    /**
-     * Creates a new, empty map with the specified initial
-     * capacity, load factor and concurrency level.
-     *
-     * @param ctx Cache context.
-     * @param factory Entry factory.
-     * @param initialCapacity the initial capacity. The implementation
-     *      performs internal sizing to accommodate this many elements.
-     * @param loadFactor  the load factor threshold, used to control resizing.
-     *      Resizing may be performed when the average number of elements per
-     *      bin exceeds this threshold.
-     * @param concurrencyLevel the estimated number of concurrently
-     *      updating threads. The implementation performs internal sizing
-     *      to try to accommodate this many threads.
-     * @throws IllegalArgumentException if the initial capacity is
-     *      negative or the load factor or concurrencyLevel are
-     *      non-positive.
-     */
-    public GridCacheConcurrentMapImpl(
-        GridCacheContext ctx,
-        GridCacheMapEntryFactory factory,
-        int initialCapacity,
-        float loadFactor,
-        int concurrencyLevel
-    ) {
-        this.ctx = ctx;
-        this.factory = factory;
+    /** {@inheritDoc} */
+    @Nullable @Override public GridCacheMapEntry getEntry(GridCacheContext ctx, KeyCacheObject key) {
+        CacheMapHolder hld = entriesMapIfExists(ctx.cacheIdBoxed());
 
-        map = new ConcurrentHashMap8<>(initialCapacity, loadFactor, concurrencyLevel);
+        return hld != null ? hld.map.get(key) : null;
     }
 
     /** {@inheritDoc} */
-    @Nullable @Override public GridCacheMapEntry getEntry(KeyCacheObject key) {
-        return map.get(key);
+    @Nullable @Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(
+        GridCacheContext ctx,
+        final AffinityTopologyVersion topVer,
+        KeyCacheObject key,
+        final boolean create,
+        final boolean touch) {
+        return putEntryIfObsoleteOrAbsent(null, ctx, topVer, key, create, touch);
     }
 
-    /** {@inheritDoc} */
-    @Nullable @Override public GridCacheMapEntry putEntryIfObsoleteOrAbsent(final AffinityTopologyVersion topVer,
+    protected final GridCacheMapEntry putEntryIfObsoleteOrAbsent(
+        @Nullable CacheMapHolder hld,
+        GridCacheContext ctx,
+        final AffinityTopologyVersion topVer,
         KeyCacheObject key,
         final boolean create,
         final boolean touch) {
+        if (hld == null)
+            hld = entriesMapIfExists(ctx.cacheIdBoxed());
+
         GridCacheMapEntry cur = null;
         GridCacheMapEntry created = null;
         GridCacheMapEntry created0 = null;
@@ -120,7 +90,7 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
 
         try {
             while (!done) {
-                GridCacheMapEntry entry = map.get(key);
+                GridCacheMapEntry entry = hld != null ? hld.map.get(key) : null;
                 created = null;
                 doomed = null;
 
@@ -134,12 +104,18 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
                                 reserved = true;
                             }
 
+                            if (hld == null) {
+                                hld = entriesMap(ctx);
+
+                                assert hld != null;
+                            }
+
                             created0 = factory.create(ctx, topVer, key);
                         }
 
                         cur = created = created0;
 
-                        done = map.putIfAbsent(created.key(), created) == null;
+                        done = hld.map.putIfAbsent(created.key(), created) == null;
                     }
                     else
                         done = true;
@@ -162,10 +138,10 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
 
                             cur = created = created0;
 
-                            done = map.replace(entry.key(), doomed, created);
+                            done = hld.map.replace(entry.key(), doomed, created);
                         }
                         else
-                            done = map.remove(entry.key(), doomed);
+                            done = hld.map.remove(entry.key(), doomed);
                     }
                     else {
                         cur = entry;
@@ -231,18 +207,31 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
         }
         finally {
             if (reserved)
-                release(sizeChange, cur);
+                release(sizeChange, hld, cur);
             else {
                 if (sizeChange != 0) {
                     assert sizeChange == -1;
+                    assert doomed != null;
 
-                    decrementPublicSize(cur);
+                    decrementPublicSize(hld, doomed);
                 }
             }
         }
     }
 
     /**
+     * @param cctx Cache context.
+     * @return Map for given cache ID.
+     */
+    @Nullable protected abstract CacheMapHolder entriesMap(GridCacheContext cctx);
+
+    /**
+     * @param cacheId Cache ID.
+     * @return Map for given cache ID.
+     */
+    @Nullable protected abstract CacheMapHolder entriesMapIfExists(Integer cacheId);
+
+    /**
      *
      */
     protected boolean reserve() {
@@ -258,63 +247,75 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
 
     /**
      * @param sizeChange Size delta.
+     * @param hld Map holder.
      * @param e Map entry.
      */
-    protected void release(int sizeChange, GridCacheEntryEx e) {
+    protected void release(int sizeChange, CacheMapHolder hld, GridCacheEntryEx e) {
         if (sizeChange == 1)
-            incrementPublicSize(e);
+            incrementPublicSize(hld, e);
         else if (sizeChange == -1)
-            decrementPublicSize(e);
+            decrementPublicSize(hld, e);
     }
 
     /** {@inheritDoc} */
     @Override public boolean removeEntry(final GridCacheEntryEx entry) {
-        boolean removed = map.remove(entry.key(), entry);
+        GridCacheContext ctx = entry.context();
+
+        CacheMapHolder hld = entriesMapIfExists(ctx.cacheIdBoxed());
+
+        boolean rmv = hld != null && hld.map.remove(entry.key(), entry);
 
-        if (removed) {
-            if (ctx.events().isRecordable(EVT_CACHE_ENTRY_DESTROYED))
+        if (rmv) {
+            if (ctx.events().isRecordable(EVT_CACHE_ENTRY_DESTROYED)) {
                 // Event notification.
-                ctx.events().addEvent(entry.partition(), entry.key(), ctx.localNodeId(), (IgniteUuid)null, null,
-                    EVT_CACHE_ENTRY_DESTROYED, null, false, null, false, null, null, null, false);
+                ctx.events().addEvent(entry.partition(),
+                    entry.key(),
+                    ctx.localNodeId(),
+                    (IgniteUuid)null,
+                    null,
+                    EVT_CACHE_ENTRY_DESTROYED,
+                    null,
+                    false,
+                    null,
+                    false,
+                    null,
+                    null,
+                    null,
+                    false);
+            }
 
             synchronized (entry) {
                 if (!entry.deleted())
-                    decrementPublicSize(entry);
+                    decrementPublicSize(hld, entry);
             }
         }
 
-        return removed;
+        return rmv;
     }
 
     /** {@inheritDoc} */
-    @Override public int internalSize() {
-        return map.size();
-    }
+    @Override public Collection<GridCacheMapEntry> entries(int cacheId, final CacheEntryPredicate... filter) {
+        CacheMapHolder hld = entriesMapIfExists(cacheId);
+
+        if (hld == null)
+            return Collections.emptyList();
 
-    /** {@inheritDoc} */
-    @Override public Collection<GridCacheMapEntry> entries(final CacheEntryPredicate... filter) {
         final IgnitePredicate<GridCacheMapEntry> p = new IgnitePredicate<GridCacheMapEntry>() {
             @Override public boolean apply(GridCacheMapEntry entry) {
                 return entry.visitable(filter);
             }
         };
 
-        return F.viewReadOnly(map.values(), F.<GridCacheMapEntry>identity(), p);
+        return F.viewReadOnly(hld.map.values(), F.<GridCacheMapEntry>identity(), p);
     }
 
     /** {@inheritDoc} */
-    @Override public Collection<GridCacheMapEntry> allEntries(final CacheEntryPredicate... filter) {
-        final IgnitePredicate<GridCacheMapEntry> p = new IgnitePredicate<GridCacheMapEntry>() {
-            @Override public boolean apply(GridCacheMapEntry entry) {
-                return F.isAll(entry, filter);
-            }
-        };
+    @Override public Set<GridCacheMapEntry> entrySet(int cacheId, final CacheEntryPredicate... filter) {
+        final CacheMapHolder hld = entriesMapIfExists(cacheId);
 
-        return F.viewReadOnly(map.values(), F.<GridCacheMapEntry>identity(), p);
-    }
+        if (hld == null)
+            return Collections.emptySet();
 
-    /** {@inheritDoc} */
-    @Override public Set<GridCacheMapEntry> entrySet(final CacheEntryPredicate... filter) {
         final IgnitePredicate<GridCacheMapEntry> p = new IgnitePredicate<GridCacheMapEntry>() {
             @Override public boolean apply(GridCacheMapEntry entry) {
                 return entry.visitable(filter);
@@ -323,7 +324,7 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
 
         return new AbstractSet<GridCacheMapEntry>() {
             @Override public Iterator<GridCacheMapEntry> iterator() {
-                return F.iterator0(map.values(), true, p);
+                return F.iterator0(hld.map.values(), true, p);
             }
 
             @Override public int size() {
@@ -336,7 +337,7 @@ public abstract class GridCacheConcurrentMapImpl implements GridCacheConcurrentM
 
                 GridCacheMapEntry entry = (GridCacheMapEntry)o;
 
-                return entry.equals(map.get(entry.key())) && p.apply(entry);
+                return entry.equals(hld.map.get(entry.key())) && p.apply(entry);
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
index 190e952..44cf4e0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
@@ -45,6 +45,7 @@ import org.apache.ignite.cache.affinity.AffinityKeyMapper;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.events.EventType;
 import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteKernal;
@@ -56,8 +57,6 @@ import org.apache.ignite.internal.managers.discovery.GridDiscoveryManager;
 import org.apache.ignite.internal.managers.eventstorage.GridEventStorageManager;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 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.GridDhtCacheAdapter;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry;
@@ -84,7 +83,6 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersionedEnt
 import org.apache.ignite.internal.processors.cacheobject.IgniteCacheObjectProcessor;
 import org.apache.ignite.internal.processors.closure.GridClosureProcessor;
 import org.apache.ignite.internal.processors.plugin.CachePluginManager;
-import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
 import org.apache.ignite.internal.util.F0;
 import org.apache.ignite.internal.util.lang.GridFunc;
@@ -106,9 +104,10 @@ import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
 import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
-import static org.apache.ignite.cache.CacheRebalanceMode.NONE;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.PRIMARY_SYNC;
+import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_STARTED;
+import static org.apache.ignite.events.EventType.EVT_CACHE_REBALANCE_STOPPED;
 import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
 
 /**
@@ -135,14 +134,8 @@ public class GridCacheContext<K, V> implements Externalizable {
     /** Cache shared context. */
     private GridCacheSharedContext<K, V> sharedCtx;
 
-    /** Memory policy. */
-    private MemoryPolicy memPlc;
-
-    /** FreeList instance this cache is associated with. */
-    private FreeList freeList;
-
-    /** ReuseList instance this cache is associated with */
-    private ReuseList reuseList;
+    /** Cache group. */
+    private CacheGroupContext grp;
 
     /** Logger. */
     private IgniteLogger log;
@@ -177,9 +170,6 @@ public class GridCacheContext<K, V> implements Externalizable {
     /** Replication manager. */
     private GridCacheDrManager drMgr;
 
-    /** */
-    private IgniteCacheOffheapManager offheapMgr;
-
     /** Conflict resolver manager. */
     private CacheConflictResolutionManager rslvrMgr;
 
@@ -210,6 +200,9 @@ public class GridCacheContext<K, V> implements Externalizable {
     /** Cache ID. */
     private int cacheId;
 
+    /** Cache ID. */
+    private Integer cacheIdBoxed;
+
     /** Cache type. */
     private CacheType cacheType;
 
@@ -237,18 +230,12 @@ public class GridCacheContext<K, V> implements Externalizable {
     /** Topology version when cache was started on local node. */
     private AffinityTopologyVersion locStartTopVer;
 
-    /** */
-    private UUID rcvdFrom;
-
     /** Dynamic cache deployment ID. */
     private IgniteUuid dynamicDeploymentId;
 
     /** Updates allowed flag. */
     private boolean updatesAllowed;
 
-    /** Flag indicating that this cache is in a recovery mode. */
-    private boolean needsRecovery;
-
     /** Deployment enabled flag for this specific cache */
     private boolean depEnabled;
 
@@ -258,6 +245,12 @@ public class GridCacheContext<K, V> implements Externalizable {
     /** */
     private boolean customAffMapper;
 
+    /** Whether {@link EventType#EVT_CACHE_REBALANCE_STARTED} was sent (used only for REPLICATED cache). */
+    private volatile boolean rebalanceStartedEvtSent;
+
+    /** Whether {@link EventType#EVT_CACHE_REBALANCE_STOPPED} was sent (used only for REPLICATED cache). */
+    private volatile boolean rebalanceStoppedEvtSent;
+
     /**
      * Empty constructor required for {@link Externalizable}.
      */
@@ -269,9 +262,9 @@ public class GridCacheContext<K, V> implements Externalizable {
      * @param ctx Kernal context.
      * @param sharedCtx Cache shared context.
      * @param cacheCfg Cache configuration.
+     * @param grp Cache group.
      * @param cacheType Cache type.
-     * @param memPlc MemoryPolicy instance.
-     * @param freeList FreeList instance.
+     * @param locStartTopVer Topology version when cache was started on local node.
      * @param affNode {@code True} if local node is affinity node.
      * @param updatesAllowed Updates allowed flag.
      * @param evtMgr Cache event manager.
@@ -291,14 +284,11 @@ public class GridCacheContext<K, V> implements Externalizable {
         GridKernalContext ctx,
         GridCacheSharedContext sharedCtx,
         CacheConfiguration cacheCfg,
+        CacheGroupContext grp,
         CacheType cacheType,
         AffinityTopologyVersion locStartTopVer,
-        UUID rcvdFrom,
         boolean affNode,
         boolean updatesAllowed,
-        MemoryPolicy memPlc,
-        FreeList freeList,
-        ReuseList reuseList,
 
         /*
          * Managers in starting order!
@@ -313,7 +303,6 @@ public class GridCacheContext<K, V> implements Externalizable {
         CacheDataStructuresManager dataStructuresMgr,
         GridCacheTtlManager ttlMgr,
         GridCacheDrManager drMgr,
-        IgniteCacheOffheapManager offheapMgr,
         CacheConflictResolutionManager<K, V> rslvrMgr,
         CachePluginManager pluginMgr,
         GridCacheAffinityManager affMgr
@@ -323,6 +312,7 @@ public class GridCacheContext<K, V> implements Externalizable {
         assert cacheCfg != null;
         assert locStartTopVer != null : cacheCfg.getName();
 
+        assert grp != null;
         assert evtMgr != null;
         assert storeMgr != null;
         assert evictMgr != null;
@@ -333,22 +323,17 @@ public class GridCacheContext<K, V> implements Externalizable {
         assert ttlMgr != null;
         assert rslvrMgr != null;
         assert pluginMgr != null;
-        assert offheapMgr != null;
 
         this.ctx = ctx;
         this.sharedCtx = sharedCtx;
         this.cacheCfg = cacheCfg;
+        this.grp = grp;
         this.cacheType = cacheType;
         this.locStartTopVer = locStartTopVer;
-        this.rcvdFrom = rcvdFrom;
         this.affNode = affNode;
         this.updatesAllowed = updatesAllowed;
         this.depEnabled = ctx.deploy().enabled() && !cacheObjects().isBinaryEnabled(cacheCfg);
 
-        this.memPlc = memPlc;
-        this.freeList = freeList;
-        this.reuseList = reuseList;
-
         /*
          * Managers in starting order!
          * ===========================
@@ -361,7 +346,6 @@ public class GridCacheContext<K, V> implements Externalizable {
         this.dataStructuresMgr = add(dataStructuresMgr);
         this.ttlMgr = add(ttlMgr);
         this.drMgr = add(drMgr);
-        this.offheapMgr = add(offheapMgr);
         this.rslvrMgr = add(rslvrMgr);
         this.pluginMgr = add(pluginMgr);
         this.affMgr = add(affMgr);
@@ -374,6 +358,8 @@ public class GridCacheContext<K, V> implements Externalizable {
 
         cacheId = CU.cacheId(cacheName);
 
+        cacheIdBoxed = cacheId;
+
         plc = cacheType.ioPolicy();
 
         Factory<ExpiryPolicy> factory = cacheCfg.getExpiryPolicyFactory();
@@ -387,6 +373,20 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * @return Cache group ID.
+     */
+    public int groupId() {
+        return grp.groupId();
+    }
+
+    /**
+     * @return Cache group.
+     */
+    public CacheGroupContext group() {
+        return grp;
+    }
+
+    /**
      * @return {@code True} if custom {@link AffinityKeyMapper} is configured for cache.
      */
     public boolean customAffinityMapper() {
@@ -460,13 +460,6 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
-     * @return Node ID cache was received from.
-     */
-    public UUID receivedFrom() {
-        return rcvdFrom;
-    }
-
-    /**
      * @return Topology version when cache was started on local node.
      */
     public AffinityTopologyVersion startTopologyVersion() {
@@ -525,6 +518,13 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * @return Cache ID.
+     */
+    public Integer cacheIdBoxed() {
+        return cacheIdBoxed;
+    }
+
+    /**
      * @return {@code True} if should use system transactions which are isolated from user transactions.
      */
     public boolean systemTx() {
@@ -721,21 +721,7 @@ public class GridCacheContext<K, V> implements Externalizable {
      * @return Memory policy.
      */
     public MemoryPolicy memoryPolicy() {
-        return memPlc;
-    }
-
-    /**
-     * @return Free List.
-     */
-    public FreeList freeList() {
-        return freeList;
-    }
-
-    /**
-     * @return Reuse List.
-     */
-    public ReuseList reuseList() {
-        return reuseList;
+        return grp.memoryPolicy();
     }
 
     /**
@@ -775,7 +761,7 @@ public class GridCacheContext<K, V> implements Externalizable {
      * @return {@code True} if rebalance is enabled.
      */
     public boolean rebalanceEnabled() {
-        return cacheCfg.getRebalanceMode() != NONE;
+        return grp.rebalanceEnabled();
     }
 
     /**
@@ -854,6 +840,18 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * @return DHT cache.
+     */
+    public GridDhtCacheAdapter dhtCache() {
+        GridCacheAdapter<K, V> cache = this.cache;
+
+        if (cache == null)
+            throw new IllegalStateException("Cache stopped: " + cacheName);
+
+        return isNear() ? ((GridNearCacheAdapter<K, V>)cache).dht() : dht();
+    }
+
+    /**
      * @return Topology version future.
      */
     public GridDhtTopologyFuture topologyVersionFuture() {
@@ -1073,7 +1071,7 @@ public class GridCacheContext<K, V> implements Externalizable {
      * @return Offheap manager.
      */
     public IgniteCacheOffheapManager offheap() {
-        return offheapMgr;
+        return grp.offheap();
     }
 
     /**
@@ -1945,20 +1943,6 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
-     * @return Current cache state. Must only be modified during exchange.
-     */
-    public boolean needsRecovery() {
-        return needsRecovery;
-    }
-
-    /**
-     * @param needsRecovery Needs recovery flag.
-     */
-    public void needsRecovery(boolean needsRecovery) {
-        this.needsRecovery = needsRecovery;
-    }
-
-    /**
      * Nulling references to potentially leak-prone objects.
      */
     public void cleanup() {
@@ -2027,7 +2011,6 @@ public class GridCacheContext<K, V> implements Externalizable {
             topology().partitionState(localNodeId(), part) == OWNING :
             "result = " + result + ", persistenceEnabled = " + ctx.cache().context().database().persistenceEnabled() +
                 ", partitionState = " + topology().partitionState(localNodeId(), part);
-        ;
 
         return result;
     }
@@ -2045,13 +2028,6 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
-     * @return {@code True} if fast eviction is allowed.
-     */
-    public boolean allowFastEviction() {
-        return shared().database().persistenceEnabled() && !QueryUtils.isEnabled(cacheCfg);
-    }
-
-    /**
      * @param part Partition.
      * @param affNodes Affinity nodes.
      * @param topVer Topology version.
@@ -2066,6 +2042,35 @@ public class GridCacheContext<K, V> implements Externalizable {
             || (top.partitionState(localNodeId(), part) == OWNING);
     }
 
+    /**
+     * @param type Event type.
+     * @return {@code True} if event should be recorded.
+     */
+    public boolean recordEvent(int type) {
+        if (isReplicated()) {
+            if (type == EVT_CACHE_REBALANCE_STARTED) {
+                if (!rebalanceStartedEvtSent) {
+                    rebalanceStartedEvtSent = true;
+
+                    return true;
+                }
+                else
+                    return false;
+            }
+            else if (type == EVT_CACHE_REBALANCE_STOPPED) {
+                if (!rebalanceStoppedEvtSent) {
+                    rebalanceStoppedEvtSent = true;
+
+                    return true;
+                }
+                else
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
     /** {@inheritDoc} */
     @Override public void writeExternal(ObjectOutput out) throws IOException {
         U.writeString(out, igniteInstanceName());

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
index c0e1c55..7371153 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEntryInfo.java
@@ -317,17 +317,17 @@ public class GridCacheEntryInfo implements Message {
     }
 
     /**
+     * @param ctx Cache object context.
      * @return Marshalled size.
+     * @throws IgniteCheckedException If failed.
      */
-    public int marshalledSize(GridCacheContext ctx) throws IgniteCheckedException {
+    public int marshalledSize(CacheObjectContext ctx) throws IgniteCheckedException {
         int size = 0;
 
-        CacheObjectContext cacheObjCtx = ctx.cacheObjectContext();
-
         if (val != null)
-            size += val.valueBytes(cacheObjCtx).length;
+            size += val.valueBytes(ctx).length;
 
-        size += key.valueBytes(cacheObjCtx).length;
+        size += key.valueBytes(ctx).length;
 
         return SIZE_OVERHEAD + size;
     }
@@ -337,12 +337,20 @@ public class GridCacheEntryInfo implements Message {
      * @throws IgniteCheckedException In case of error.
      */
     public void marshal(GridCacheContext ctx) throws IgniteCheckedException {
+        marshal(ctx.cacheObjectContext());
+    }
+
+    /**
+     * @param ctx Cache context.
+     * @throws IgniteCheckedException In case of error.
+     */
+    public void marshal(CacheObjectContext ctx) throws IgniteCheckedException {
         assert key != null;
 
-        key.prepareMarshal(ctx.cacheObjectContext());
+        key.prepareMarshal(ctx);
 
         if (val != null)
-            val.prepareMarshal(ctx.cacheObjectContext());
+            val.prepareMarshal(ctx);
 
         if (expireTime == 0)
             expireTime = -1;
@@ -362,10 +370,21 @@ public class GridCacheEntryInfo implements Message {
      * @throws IgniteCheckedException If unmarshalling failed.
      */
     public void unmarshal(GridCacheContext ctx, ClassLoader clsLdr) throws IgniteCheckedException {
-        key.finishUnmarshal(ctx.cacheObjectContext(), clsLdr);
+        unmarshal(ctx.cacheObjectContext(), clsLdr);
+    }
+
+    /**
+     * Unmarshalls entry.
+     *
+     * @param ctx Cache context.
+     * @param clsLdr Class loader.
+     * @throws IgniteCheckedException If unmarshalling failed.
+     */
+    public void unmarshal(CacheObjectContext ctx, ClassLoader clsLdr) throws IgniteCheckedException {
+        key.finishUnmarshal(ctx, clsLdr);
 
         if (val != null)
-            val.finishUnmarshal(ctx.cacheObjectContext(), clsLdr);
+            val.finishUnmarshal(ctx, clsLdr);
 
         long remaining = expireTime;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEventManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEventManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEventManager.java
index 93c5950..a967305 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEventManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheEventManager.java
@@ -368,42 +368,6 @@ public class GridCacheEventManager extends GridCacheManagerAdapter {
     }
 
     /**
-     * Adds preloading event.
-     *
-     * @param part Partition.
-     * @param type Event type.
-     * @param discoNode Discovery node.
-     * @param discoType Discovery event type.
-     * @param discoTs Discovery event timestamp.
-     */
-    public void addPreloadEvent(int part, int type, ClusterNode discoNode, int discoType, long discoTs) {
-        assert discoNode != null;
-        assert type > 0;
-        assert discoType > 0;
-        assert discoTs > 0;
-
-        if (!cctx.events().isRecordable(type))
-            LT.warn(log, "Added event without checking if event is recordable: " + U.gridEventName(type));
-
-        cctx.gridEvents().record(new CacheRebalancingEvent(cctx.name(), cctx.localNode(),
-            "Cache rebalancing event.", type, part, discoNode, discoType, discoTs));
-    }
-
-    /**
-     * Adds partition unload event.
-     *
-     * @param part Partition.
-     */
-    public void addUnloadEvent(int part) {
-        if (!cctx.events().isRecordable(EVT_CACHE_REBALANCE_PART_UNLOADED))
-            LT.warn(log, "Added event without checking if event is recordable: " +
-                U.gridEventName(EVT_CACHE_REBALANCE_PART_UNLOADED));
-
-        cctx.gridEvents().record(new CacheRebalancingEvent(cctx.name(), cctx.localNode(),
-            "Cache unloading event.", EVT_CACHE_REBALANCE_PART_UNLOADED, part, null, 0, 0));
-    }
-
-    /**
      * @param type Event type.
      * @return {@code True} if event is recordable.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheGroupIdMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheGroupIdMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheGroupIdMessage.java
new file mode 100644
index 0000000..09c143b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheGroupIdMessage.java
@@ -0,0 +1,110 @@
+/*
+ * 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.nio.ByteBuffer;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Message related to particular cache group.
+ */
+public abstract class GridCacheGroupIdMessage extends GridCacheMessage {
+    /** Cache group ID. */
+    @GridToStringInclude
+    protected int grpId;
+
+    /**
+     * @return Cache group ID.
+     */
+    public int groupId() {
+        return grpId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public final int handlerId() {
+        return grpId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!super.writeTo(buf, writer))
+            return false;
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 2:
+                if (!writer.writeInt("grpId", grpId))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        if (!super.readFrom(buf, reader))
+            return false;
+
+        switch (reader.state()) {
+            case 2:
+                grpId = reader.readInt("grpId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridCacheGroupIdMessage.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridCacheGroupIdMessage.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIdMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIdMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIdMessage.java
new file mode 100644
index 0000000..6c20bdd
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIdMessage.java
@@ -0,0 +1,117 @@
+/*
+ * 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.nio.ByteBuffer;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+
+/**
+ * Message related to particular cache.
+ */
+public abstract class GridCacheIdMessage extends GridCacheMessage {
+    /** Cache ID. */
+    @GridToStringInclude
+    protected int cacheId;
+
+    /**
+     * @return Cache ID.
+     */
+    public int cacheId() {
+        return cacheId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean cacheGroupMessage() {
+        return false;
+    }
+
+    /**
+     * @param cacheId Cache ID.
+     */
+    public void cacheId(int cacheId) {
+        this.cacheId = cacheId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 3;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!super.writeTo(buf, writer))
+            return false;
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 2:
+                if (!writer.writeInt("cacheId", cacheId))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        if (!super.readFrom(buf, reader))
+            return false;
+
+        switch (reader.state()) {
+            case 2:
+                cacheId = reader.readInt("cacheId");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridCacheIdMessage.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int handlerId() {
+        return cacheId;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(GridCacheIdMessage.class, this);
+    }
+}


[10/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
index 10caf07..2ac1ba6 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheAdapter.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.processors.cache.distributed.dht;
 
 import java.io.Externalizable;
-import java.util.AbstractSet;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -26,7 +25,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
-import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentMap;
 import javax.cache.Cache;
@@ -34,13 +32,15 @@ import javax.cache.expiry.ExpiryPolicy;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.events.DiscoveryEvent;
+import org.apache.ignite.events.Event;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.managers.eventstorage.GridLocalEventListener;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CacheOperationContext;
-import org.apache.ignite.internal.processors.cache.CachePeekModes;
 import org.apache.ignite.internal.processors.cache.EntryGetResult;
 import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheClearAllRunnable;
@@ -59,7 +59,9 @@ import org.apache.ignite.internal.processors.cache.distributed.GridCacheTtlUpdat
 import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheAdapter;
 import org.apache.ignite.internal.processors.cache.distributed.GridDistributedCacheEntry;
 import org.apache.ignite.internal.processors.cache.distributed.dht.colocated.GridDhtDetachedCacheEntry;
-import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysFuture;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysRequest;
+import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtForceKeysResponse;
 import org.apache.ignite.internal.processors.cache.distributed.near.CacheVersionedValue;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearCacheAdapter;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
@@ -70,7 +72,6 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.platform.cache.PlatformCacheEntryFilter;
 import org.apache.ignite.internal.util.future.GridCompoundFuture;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
-import org.apache.ignite.internal.util.lang.GridIteratorAdapter;
 import org.apache.ignite.internal.util.typedef.CI1;
 import org.apache.ignite.internal.util.typedef.CI2;
 import org.apache.ignite.internal.util.typedef.CI3;
@@ -78,6 +79,7 @@ import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteBiInClosure;
 import org.apache.ignite.lang.IgniteBiPredicate;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteUuid;
@@ -86,8 +88,11 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
 
+import static org.apache.ignite.events.EventType.EVT_NODE_FAILED;
+import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
 import static org.apache.ignite.internal.processors.dr.GridDrType.DR_LOAD;
 import static org.apache.ignite.internal.processors.dr.GridDrType.DR_NONE;
+import static org.apache.ignite.internal.util.GridConcurrentFactory.newMap;
 
 /**
  * DHT cache adapter.
@@ -96,18 +101,36 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
     /** */
     private static final long serialVersionUID = 0L;
 
-    /** Topology. */
-    private GridDhtPartitionTopologyImpl top;
-
-    /** Preloader. */
-    protected GridCachePreloader preldr;
-
     /** Multi tx future holder. */
     private ThreadLocal<IgniteBiTuple<IgniteUuid, GridDhtTopologyFuture>> multiTxHolder = new ThreadLocal<>();
 
     /** Multi tx futures. */
     private ConcurrentMap<IgniteUuid, MultiUpdateFuture> multiTxFuts = new ConcurrentHashMap8<>();
 
+    /** Force key futures. */
+    private final ConcurrentMap<IgniteUuid, GridDhtForceKeysFuture<?, ?>> forceKeyFuts = newMap();
+
+    /** */
+    private volatile boolean stopping;
+
+    /** Discovery listener. */
+    private final GridLocalEventListener discoLsnr = new GridLocalEventListener() {
+        @Override public void onEvent(Event evt) {
+            DiscoveryEvent e = (DiscoveryEvent)evt;
+
+            ClusterNode loc = ctx.localNode();
+
+            assert e.type() == EVT_NODE_LEFT || e.type() == EVT_NODE_FAILED : e;
+
+            final ClusterNode n = e.eventNode();
+
+            assert !loc.id().equals(n.id());
+
+            for (GridDhtForceKeysFuture<?, ?> f : forceKeyFuts.values())
+                f.onDiscoveryEvent(e);
+        }
+    };
+
     /**
      * Empty constructor required for {@link Externalizable}.
      */
@@ -116,6 +139,176 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
     }
 
     /**
+     * Adds future to future map.
+     *
+     * @param fut Future to add.
+     * @return {@code False} if node cache is stopping and future was completed with error.
+     */
+    public boolean addFuture(GridDhtForceKeysFuture<?, ?> fut) {
+        forceKeyFuts.put(fut.futureId(), fut);
+
+        if (stopping) {
+            fut.onDone(stopError());
+
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Removes future from future map.
+     *
+     * @param fut Future to remove.
+     */
+    public void removeFuture(GridDhtForceKeysFuture<?, ?> fut) {
+        forceKeyFuts.remove(fut.futureId(), fut);
+    }
+
+    /**
+     * @param node Node.
+     * @param msg Message.
+     */
+    protected final void processForceKeyResponse(ClusterNode node, GridDhtForceKeysResponse msg) {
+        GridDhtForceKeysFuture<?, ?> f = forceKeyFuts.get(msg.futureId());
+
+        if (f != null)
+            f.onResult(msg);
+        else if (log.isDebugEnabled())
+            log.debug("Receive force key response for unknown future (is it duplicate?) [nodeId=" + node.id() +
+                ", res=" + msg + ']');
+    }
+    /**
+     * @param node Node originated request.
+     * @param msg Force keys message.
+     */
+    protected final void processForceKeysRequest(final ClusterNode node, final GridDhtForceKeysRequest msg) {
+        IgniteInternalFuture<?> fut = ctx.mvcc().finishKeys(msg.keys(), msg.cacheId(), msg.topologyVersion());
+
+        if (fut.isDone())
+            processForceKeysRequest0(node, msg);
+        else
+            fut.listen(new CI1<IgniteInternalFuture<?>>() {
+                @Override public void apply(IgniteInternalFuture<?> t) {
+                    processForceKeysRequest0(node, msg);
+                }
+            });
+    }
+
+    /**
+     * @param node Node originated request.
+     * @param msg Force keys message.
+     */
+    private void processForceKeysRequest0(ClusterNode node, GridDhtForceKeysRequest msg) {
+        try {
+            ClusterNode loc = ctx.localNode();
+
+            GridDhtForceKeysResponse res = new GridDhtForceKeysResponse(
+                ctx.cacheId(),
+                msg.futureId(),
+                msg.miniId(),
+                ctx.deploymentEnabled());
+
+            GridDhtPartitionTopology top = ctx.topology();
+
+            for (KeyCacheObject k : msg.keys()) {
+                int p = ctx.affinity().partition(k);
+
+                GridDhtLocalPartition locPart = top.localPartition(p, AffinityTopologyVersion.NONE, false);
+
+                // If this node is no longer an owner.
+                if (locPart == null && !top.owners(p).contains(loc)) {
+                    res.addMissed(k);
+
+                    continue;
+                }
+
+                GridCacheEntryEx entry;
+
+                while (true) {
+                    try {
+                        entry = ctx.dht().entryEx(k);
+
+                        entry.unswap();
+
+                        GridCacheEntryInfo info = entry.info();
+
+                        if (info == null) {
+                            assert entry.obsolete() : entry;
+
+                            continue;
+                        }
+
+                        if (!info.isNew())
+                            res.addInfo(info);
+
+                        ctx.evicts().touch(entry, msg.topologyVersion());
+
+                        break;
+                    }
+                    catch (GridCacheEntryRemovedException ignore) {
+                        if (log.isDebugEnabled())
+                            log.debug("Got removed entry: " + k);
+                    }
+                    catch (GridDhtInvalidPartitionException ignore) {
+                        if (log.isDebugEnabled())
+                            log.debug("Local node is no longer an owner: " + p);
+
+                        res.addMissed(k);
+
+                        break;
+                    }
+                }
+            }
+
+            if (log.isDebugEnabled())
+                log.debug("Sending force key response [node=" + node.id() + ", res=" + res + ']');
+
+            ctx.io().send(node, res, ctx.ioPolicy());
+        }
+        catch (ClusterTopologyCheckedException ignore) {
+            if (log.isDebugEnabled())
+                log.debug("Received force key request form failed node (will ignore) [nodeId=" + node.id() +
+                    ", req=" + msg + ']');
+        }
+        catch (IgniteCheckedException e) {
+            U.error(log, "Failed to reply to force key request [nodeId=" + node.id() + ", req=" + msg + ']', e);
+        }
+    }
+
+    /**
+     *
+     */
+    public void dumpDebugInfo() {
+        if (!forceKeyFuts.isEmpty()) {
+            U.warn(log, "Pending force key futures [cache=" + ctx.name() + "]:");
+
+            for (GridDhtForceKeysFuture fut : forceKeyFuts.values())
+                U.warn(log, ">>> " + fut);
+        }
+    }
+
+    @Override public void onKernalStop() {
+        super.onKernalStop();
+
+        stopping = true;
+
+        IgniteCheckedException err = stopError();
+
+        for (GridDhtForceKeysFuture fut : forceKeyFuts.values())
+            fut.onDone(err);
+
+        ctx.gridEvents().removeLocalEventListener(discoLsnr);
+    }
+
+    /**
+     * @return Node stop exception.
+     */
+    private IgniteCheckedException stopError() {
+        return new NodeStoppingException("Operation has been cancelled (cache or node is stopping).");
+    }
+
+    /**
      * @param nodeId Sender node ID.
      * @param res Near get response.
      */
@@ -160,7 +353,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
      * @param ctx Context.
      */
     protected GridDhtCacheAdapter(GridCacheContext<K, V> ctx) {
-        this(ctx, new GridCachePartitionedConcurrentMap(ctx));
+        this(ctx, new GridCachePartitionedConcurrentMap(ctx.group()));
     }
 
     /**
@@ -174,83 +367,21 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
     }
 
     /** {@inheritDoc} */
-    @Override protected void init() {
-        super.init();
-
-        top = new GridDhtPartitionTopologyImpl(ctx, entryFactory());
-    }
-
-    /** {@inheritDoc} */
     @Override public void start() throws IgniteCheckedException {
-        super.start();
-
-        ctx.io().addHandler(ctx.cacheId(), GridCacheTtlUpdateRequest.class, new CI2<UUID, GridCacheTtlUpdateRequest>() {
+        ctx.io().addCacheHandler(ctx.cacheId(), GridCacheTtlUpdateRequest.class, new CI2<UUID, GridCacheTtlUpdateRequest>() {
             @Override public void apply(UUID nodeId, GridCacheTtlUpdateRequest req) {
                 processTtlUpdateRequest(req);
             }
         });
-    }
-
-    /** {@inheritDoc} */
-    @Override public void stop() {
-        super.stop();
-
-        if (preldr != null)
-            preldr.stop();
-
-        // Clean up to help GC.
-        preldr = null;
-        top = null;
-    }
 
-    /** {@inheritDoc} */
-    @Override public void onReconnected() {
-        super.onReconnected();
-
-        ctx.affinity().onReconnected();
-
-        top.onReconnected();
-
-        if (preldr != null)
-            preldr.onReconnected();
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onKernalStart() throws IgniteCheckedException {
-        super.onKernalStart();
-
-        if (preldr != null)
-            preldr.onKernalStart();
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onKernalStop() {
-        super.onKernalStop();
-
-        if (preldr != null)
-            preldr.onKernalStop();
+        ctx.gridEvents().addLocalEventListener(discoLsnr, EVT_NODE_LEFT, EVT_NODE_FAILED);
     }
 
     /** {@inheritDoc} */
     @Override public void printMemoryStats() {
         super.printMemoryStats();
 
-        top.printMemoryStats(1024);
-    }
-
-    /**
-     * @return Cache map entry factory.
-     */
-    @Override protected GridCacheMapEntryFactory entryFactory() {
-        return new GridCacheMapEntryFactory() {
-            @Override public GridCacheMapEntry create(
-                GridCacheContext ctx,
-                AffinityTopologyVersion topVer,
-                KeyCacheObject key
-            ) {
-                return new GridDhtCacheEntry(ctx, topVer, key);
-            }
-        };
+        ctx.group().topology().printMemoryStats(1024);
     }
 
     /**
@@ -262,21 +393,12 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
      * @return Partition topology.
      */
     public GridDhtPartitionTopology topology() {
-        return top;
+        return ctx.group().topology();
     }
 
     /** {@inheritDoc} */
     @Override public GridCachePreloader preloader() {
-        return preldr;
-    }
-
-    /**
-     * @return DHT preloader.
-     */
-    public GridDhtPreloader dhtPreloader() {
-        assert preldr instanceof GridDhtPreloader;
-
-        return (GridDhtPreloader)preldr;
+        return ctx.group().preloader();
     }
 
     /**
@@ -300,6 +422,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
         if (tup != null)
             throw new IgniteCheckedException("Nested multi-update locks are not supported");
 
+        GridDhtPartitionTopology top = ctx.group().topology();
+
         top.readLock();
 
         GridDhtTopologyFuture topFut;
@@ -342,7 +466,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
         if (tup == null)
             throw new IgniteCheckedException("Multi-update was not started or released twice.");
 
-        top.readLock();
+        ctx.group().topology().readLock();
 
         try {
             IgniteUuid lockId = tup.get1();
@@ -355,7 +479,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
             multiFut.onDone(lockId);
         }
         finally {
-            top.readUnlock();
+            ctx.group().topology().readUnlock();
         }
     }
 
@@ -516,7 +640,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
             return;
 
         try {
-            GridDhtLocalPartition part = top.localPartition(ctx.affinity().partition(key),
+            GridDhtLocalPartition part = ctx.group().topology().localPartition(ctx.affinity().partition(key),
                 AffinityTopologyVersion.NONE, true);
 
             // Reserve to make sure that partition does not get unloaded.
@@ -576,7 +700,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
         long sum = 0;
 
         for (GridDhtLocalPartition p : topology().currentLocalPartitions())
-            sum += p.dataStore().size();
+            sum += p.dataStore().cacheSize(ctx.cacheId());
 
         return sum;
     }
@@ -594,7 +718,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
 
         for (GridDhtLocalPartition p : topology().currentLocalPartitions()) {
             if (p.primary(topVer))
-                sum += p.dataStore().size();
+                sum += p.dataStore().cacheSize(ctx.cacheId());
         }
 
         return sum;
@@ -809,7 +933,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
 
                         res = new GridNearSingleGetResponse(ctx.cacheId(),
                             req.futureId(),
-                            req.topologyVersion(),
+                            null,
                             res0,
                             false,
                             req.addDeploymentInfo());
@@ -818,9 +942,9 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
                             res.setContainsValue();
                     }
                     else {
-                        AffinityTopologyVersion topVer = ctx.shared().exchange().readyAffinityVersion();
+                        AffinityTopologyVersion topVer = ctx.shared().exchange().lastTopologyFuture().topologyVersion();
 
-                        assert topVer.compareTo(req.topologyVersion()) >= 0 : "Wrong ready topology version for " +
+                        assert topVer.compareTo(req.topologyVersion()) > 0 : "Wrong ready topology version for " +
                             "invalid partitions response [topVer=" + topVer + ", req=" + req + ']';
 
                         res = new GridNearSingleGetResponse(ctx.cacheId(),
@@ -908,9 +1032,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
                 }
 
                 if (!F.isEmpty(fut.invalidPartitions()))
-                    res.invalidPartitions(fut.invalidPartitions(), ctx.shared().exchange().readyAffinityVersion());
-                else
-                    res.invalidPartitions(fut.invalidPartitions(), req.topologyVersion());
+                    res.invalidPartitions(fut.invalidPartitions(), ctx.shared().exchange().lastTopologyFuture().topologyVersion());
 
                 try {
                     ctx.io().send(nodeId, res, ctx.ioPolicy());
@@ -1096,7 +1218,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
             false);
 
         if (part != null)
-            part.onDeferredDelete(entry.key(), ver);
+            part.onDeferredDelete(entry.context().cacheId(), entry.key(), ver);
     }
 
     /**
@@ -1108,8 +1230,8 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
         if (expVer.equals(curVer))
             return false;
 
-        Collection<ClusterNode> cacheNodes0 = ctx.discovery().cacheAffinityNodes(ctx.cacheId(), expVer);
-        Collection<ClusterNode> cacheNodes1 = ctx.discovery().cacheAffinityNodes(ctx.cacheId(), curVer);
+        Collection<ClusterNode> cacheNodes0 = ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), expVer);
+        Collection<ClusterNode> cacheNodes1 = ctx.discovery().cacheGroupAffinityNodes(ctx.groupId(), curVer);
 
         if (!cacheNodes0.equals(cacheNodes1) || ctx.affinity().affinityTopologyVersion().compareTo(curVer) < 0)
             return true;
@@ -1147,7 +1269,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
      * @param topVer Specified affinity topology version.
      * @return Local entries iterator.
      */
-    public Iterator<Cache.Entry<K, V>> localEntriesIterator(final boolean primary,
+    private Iterator<Cache.Entry<K, V>> localEntriesIterator(final boolean primary,
         final boolean backup,
         final boolean keepBinary,
         final AffinityTopologyVersion topVer) {
@@ -1161,7 +1283,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
      * @param topVer Specified affinity topology version.
      * @return Local entries iterator.
      */
-    public Iterator<? extends GridCacheEntryEx> localEntriesIteratorEx(final boolean primary,
+    private Iterator<? extends GridCacheEntryEx> localEntriesIteratorEx(final boolean primary,
         final boolean backup,
         final AffinityTopologyVersion topVer) {
         assert primary || backup;
@@ -1208,7 +1330,7 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
                                 GridDhtLocalPartition part = partIt.next();
 
                                 if (primary == part.primary(topVer)) {
-                                    curIt = part.entries().iterator();
+                                    curIt = part.entries(ctx.cacheId()).iterator();
 
                                     break;
                                 }
@@ -1253,4 +1375,35 @@ public abstract class GridDhtCacheAdapter<K, V> extends GridDistributedCacheAdap
             return topVer;
         }
     }
+
+    /**
+     *
+     */
+    protected abstract class MessageHandler<M> implements IgniteBiInClosure<UUID, M> {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** {@inheritDoc} */
+        @Override public void apply(UUID nodeId, M msg) {
+            ClusterNode node = ctx.node(nodeId);
+
+            if (node == null) {
+                if (log.isDebugEnabled())
+                    log.debug("Received message from failed node [node=" + nodeId + ", msg=" + msg + ']');
+
+                return;
+            }
+
+            if (log.isDebugEnabled())
+                log.debug("Received message from node [node=" + nodeId + ", msg=" + msg + ']');
+
+            onMessage(node, msg);
+        }
+
+        /**
+         * @param node Node.
+         * @param msg Message.
+         */
+        protected abstract void onMessage(ClusterNode node, M msg);
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
index 8c0b0c2..ebb2cfc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtCacheEntry.java
@@ -93,8 +93,10 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
     }
 
     /** {@inheritDoc} */
-    @Override protected long nextPartCounter() {
-        return locPart.nextUpdateCounter();
+    @Override protected long nextPartitionCounter(AffinityTopologyVersion topVer,
+        boolean primary,
+        @Nullable Long primaryCntr) {
+        return locPart.nextUpdateCounter(cctx.cacheId(), topVer, primary, primaryCntr);
     }
 
     /** {@inheritDoc} */
@@ -139,7 +141,7 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
         assert !Thread.holdsLock(this);
 
         // Remove this entry from partition mapping.
-        cctx.dht().topology().onRemoved(this);
+        cctx.topology().onRemoved(this);
     }
 
     /**
@@ -715,8 +717,8 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
     /**
      * @return Cache name.
      */
-    protected String cacheName() {
-        return cctx.dht().near().name();
+    protected final String cacheName() {
+        return cctx.name();
     }
 
     /** {@inheritDoc} */
@@ -726,12 +728,12 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
 
     /** {@inheritDoc} */
     @Override protected void incrementMapPublicSize() {
-        locPart.incrementPublicSize(this);
+        locPart.incrementPublicSize(null, this);
     }
 
     /** {@inheritDoc} */
     @Override protected void decrementMapPublicSize() {
-        locPart.decrementPublicSize(this);
+        locPart.decrementPublicSize(null, this);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
index 458bc4a..49922fe 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetFuture.java
@@ -166,7 +166,7 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
      * Initializes future.
      */
     void init() {
-        GridDhtFuture<Object> fut = cctx.dht().dhtPreloader().request(keys.keySet(), topVer);
+        GridDhtFuture<Object> fut = cctx.group().preloader().request(cctx, keys.keySet(), topVer);
 
         if (fut != null) {
             if (!F.isEmpty(fut.invalidPartitions())) {
@@ -292,9 +292,11 @@ public final class GridDhtGetFuture<K, V> extends GridCompoundIdentityFuture<Col
      */
     private boolean map(KeyCacheObject key) {
         try {
+            int keyPart = cctx.affinity().partition(key);
+
             GridDhtLocalPartition part = topVer.topologyVersion() > 0 ?
-                cache().topology().localPartition(cctx.affinity().partition(key), topVer, true) :
-                cache().topology().localPartition(key, false);
+                cache().topology().localPartition(keyPart, topVer, true) :
+                cache().topology().localPartition(keyPart);
 
             if (part == null)
                 return false;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
index 9a7cfdc..1a81f6e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtGetSingleFuture.java
@@ -87,8 +87,8 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
     /** Topology version .*/
     private AffinityTopologyVersion topVer;
 
-    /** Retries because ownership changed. */
-    private Collection<Integer> retries;
+    /** Retry because ownership changed. */
+    private Integer retry;
 
     /** Subject ID. */
     private UUID subjId;
@@ -194,17 +194,21 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
      *
      */
     private void map() {
-        if (cctx.dht().dhtPreloader().needForceKeys()) {
-            GridDhtFuture<Object> fut = cctx.dht().dhtPreloader().request(
+        if (cctx.group().preloader().needForceKeys()) {
+            GridDhtFuture<Object> fut = cctx.group().preloader().request(
+                cctx,
                 Collections.singleton(key),
                 topVer);
 
             if (fut != null) {
                 if (!F.isEmpty(fut.invalidPartitions())) {
-                    if (retries == null)
-                        retries = new HashSet<>();
+                    assert fut.invalidPartitions().size() == 1 : fut.invalidPartitions();
 
-                    retries.addAll(fut.invalidPartitions());
+                    retry = F.first(fut.invalidPartitions());
+
+                    onDone((GridCacheEntryInfo)null);
+
+                    return;
                 }
 
                 fut.listen(
@@ -239,17 +243,14 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
      *
      */
     private void map0() {
-        // Assign keys to primary nodes.
-        int part = cctx.affinity().partition(key);
+        assert retry == null : retry;
 
-        if (retries == null || !retries.contains(part)) {
-            if (!map(key)) {
-                retries = Collections.singleton(part);
+        if (!map(key)) {
+            retry = cctx.affinity().partition(key);
 
-                onDone((GridCacheEntryInfo)null);
+            onDone((GridCacheEntryInfo)null);
 
-                return;
-            }
+            return;
         }
 
         getAsync();
@@ -257,7 +258,7 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
 
     /** {@inheritDoc} */
     @Override public Collection<Integer> invalidPartitions() {
-        return retries == null ? Collections.<Integer>emptyList() : retries;
+        return retry == null ? Collections.<Integer>emptyList() : Collections.singletonList(retry);
     }
 
     /**
@@ -266,9 +267,11 @@ public final class GridDhtGetSingleFuture<K, V> extends GridFutureAdapter<GridCa
      */
     private boolean map(KeyCacheObject key) {
         try {
+            int keyPart = cctx.affinity().partition(key);
+
             GridDhtLocalPartition part = topVer.topologyVersion() > 0 ?
-                cache().topology().localPartition(cctx.affinity().partition(key), topVer, true) :
-                cache().topology().localPartition(key, false);
+                cache().topology().localPartition(keyPart, topVer, true) :
+                cache().topology().localPartition(keyPart);
 
             if (part == null)
                 return false;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
index a35c168..a53e864 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLocalPartition.java
@@ -19,7 +19,10 @@ package org.apache.ignite.internal.processors.cache.distributed.dht;
 
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.ConcurrentNavigableMap;
 import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -30,31 +33,33 @@ import java.util.concurrent.locks.ReentrantLock;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PartitionMetaStateRecord;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheConcurrentMapImpl;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntryFactory;
+import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.database.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader;
 import org.apache.ignite.internal.processors.cache.extras.GridCacheObsoleteEntryExtras;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
-import org.apache.ignite.internal.processors.query.QueryUtils;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
 import org.apache.ignite.internal.util.lang.GridIterator;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
-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.IgniteUuid;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.jsr166.ConcurrentHashMap8;
 import org.jsr166.ConcurrentLinkedDeque8;
 
 import static org.apache.ignite.IgniteSystemProperties.IGNITE_ATOMIC_CACHE_DELETE_HISTORY_SIZE;
@@ -71,6 +76,17 @@ import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDh
  * Key partition.
  */
 public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements Comparable<GridDhtLocalPartition>, GridReservable {
+    /** */
+    private static final GridCacheMapEntryFactory ENTRY_FACTORY = new GridCacheMapEntryFactory() {
+        @Override public GridCacheMapEntry create(
+            GridCacheContext ctx,
+            AffinityTopologyVersion topVer,
+            KeyCacheObject key
+        ) {
+            return new GridDhtCacheEntry(ctx, topVer, key);
+        }
+    };
+
     /** Maximum size for delete queue. */
     public static final int MAX_DELETE_QUEUE_SIZE = Integer.getInteger(IGNITE_ATOMIC_CACHE_DELETE_HISTORY_SIZE, 200_000);
 
@@ -101,29 +117,48 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     @GridToStringExclude
     private final GridFutureAdapter<?> rent;
 
-    /** Context. */
-    private final GridCacheContext cctx;
+    /** */
+    @GridToStringExclude
+    private final GridCacheSharedContext ctx;
+
+    /** */
+    @GridToStringExclude
+    private final CacheGroupContext grp;
 
     /** Create time. */
     @GridToStringExclude
     private final long createTime = U.currentTimeMillis();
 
     /** Eviction history. */
+    @GridToStringExclude
     private final Map<KeyCacheObject, GridCacheVersion> evictHist = new HashMap<>();
 
     /** Lock. */
+    @GridToStringExclude
     private final ReentrantLock lock = new ReentrantLock();
 
+    /** */
+    @GridToStringExclude
+    private final ConcurrentMap<Integer, CacheMapHolder> cacheMaps;
+
+    /** */
+    @GridToStringExclude
+    private final CacheMapHolder singleCacheEntryMap;
+
     /** Remove queue. */
+    @GridToStringExclude
     private final ConcurrentLinkedDeque8<RemovedEntryHolder> rmvQueue = new ConcurrentLinkedDeque8<>();
 
     /** Group reservations. */
+    @GridToStringExclude
     private final CopyOnWriteArrayList<GridDhtPartitionsReservation> reservations = new CopyOnWriteArrayList<>();
 
     /** */
+    @GridToStringExclude
     private final CacheDataStore store;
 
     /** Partition updates. */
+    @GridToStringExclude
     private final ConcurrentNavigableMap<Long, Boolean> updates = new ConcurrentSkipListMap<>();
 
     /** Last applied update. */
@@ -137,21 +172,30 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     private boolean reload;
 
     /**
-     * @param cctx Context.
+     * @param ctx Context.
+     * @param grp Cache group.
      * @param id Partition ID.
-     * @param entryFactory Entry factory.
      */
-    @SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor") GridDhtLocalPartition(
-        GridCacheContext cctx,
-        int id,
-        GridCacheMapEntryFactory entryFactory
-    ) {
-        super(cctx, entryFactory, Math.max(10, GridCacheAdapter.DFLT_START_CACHE_SIZE / cctx.affinity().partitions()));
+    @SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
+    GridDhtLocalPartition(GridCacheSharedContext ctx,
+        CacheGroupContext grp,
+        int id) {
+        super(ENTRY_FACTORY);
 
         this.id = id;
-        this.cctx = cctx;
+        this.ctx = ctx;
+        this.grp = grp;
 
-        log = U.logger(cctx.kernalContext(), logRef, this);
+        log = U.logger(ctx.kernalContext(), logRef, this);
+
+        if (grp.sharedGroup()) {
+            singleCacheEntryMap = null;
+            cacheMaps = new ConcurrentHashMap<>();
+        }
+        else {
+            singleCacheEntryMap = new CacheMapHolder(grp.singleCacheContext(), createEntriesMap());
+            cacheMaps = null;
+        }
 
         rent = new GridFutureAdapter<Object>() {
             @Override public String toString() {
@@ -159,15 +203,15 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
             }
         };
 
-        int delQueueSize = CU.isSystemCache(cctx.name()) ? 100 :
-            Math.max(MAX_DELETE_QUEUE_SIZE / cctx.affinity().partitions(), 20);
+        int delQueueSize = grp.systemCache() ? 100 :
+            Math.max(MAX_DELETE_QUEUE_SIZE / grp.affinity().partitions(), 20);
 
         rmvQueueMaxSize = U.ceilPow2(delQueueSize);
 
         rmvdEntryTtl = Long.getLong(IGNITE_CACHE_REMOVED_ENTRIES_TTL, 10_000);
 
         try {
-            store = cctx.offheap().createCacheDataStore(id);
+            store = grp.offheap().createCacheDataStore(id);
         }
         catch (IgniteCheckedException e) {
             // TODO ignite-db
@@ -176,6 +220,62 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
+     * @return Entries map.
+     */
+    private ConcurrentMap<KeyCacheObject, GridCacheMapEntry> createEntriesMap() {
+        return new ConcurrentHashMap8<>(Math.max(10, GridCacheAdapter.DFLT_START_CACHE_SIZE / grp.affinity().partitions()),
+            0.75f,
+            Runtime.getRuntime().availableProcessors() * 2);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int internalSize() {
+        if (grp.sharedGroup()) {
+            int size = 0;
+
+            for (CacheMapHolder hld : cacheMaps.values())
+                size += hld.map.size();
+
+            return size;
+        }
+
+        return singleCacheEntryMap.map.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected CacheMapHolder entriesMap(GridCacheContext cctx) {
+        if (grp.sharedGroup())
+            return cacheMapHolder(cctx);
+
+        return singleCacheEntryMap;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override protected CacheMapHolder entriesMapIfExists(Integer cacheId) {
+        return grp.sharedGroup() ? cacheMaps.get(cacheId) : singleCacheEntryMap;
+    }
+
+    /**
+     * @param cctx Cache context.
+     * @return Map holder.
+     */
+    private CacheMapHolder cacheMapHolder(GridCacheContext cctx) {
+        assert grp.sharedGroup();
+
+        CacheMapHolder hld = cacheMaps.get(cctx.cacheIdBoxed());
+
+        if (hld != null)
+            return hld;
+
+        CacheMapHolder  old = cacheMaps.putIfAbsent(cctx.cacheIdBoxed(), hld = new CacheMapHolder(cctx, createEntriesMap()));
+
+        if (old != null)
+            hld = old;
+
+        return hld;
+    }
+
+    /**
      * @return Data store.
      */
     public CacheDataStore dataStore() {
@@ -242,10 +342,10 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * @return {@code True} if partition is empty.
      */
     public boolean isEmpty() {
-        if (cctx.allowFastEviction())
+        if (grp.allowFastEviction())
             return internalSize() == 0;
 
-        return store.size() == 0 && internalSize() == 0;
+        return store.fullSize() == 0 && internalSize() == 0;
     }
 
     /**
@@ -309,6 +409,20 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
+     * @param cacheId Cache ID.
+     * @param key Key.
+     * @param ver Version.
+     */
+    private void removeVersionedEntry(int cacheId, KeyCacheObject key, GridCacheVersion ver) {
+        CacheMapHolder hld = grp.sharedGroup() ? cacheMaps.get(cacheId) : singleCacheEntryMap;
+
+        GridCacheMapEntry entry = hld != null ? hld.map.get(key) : null;
+
+        if (entry != null && entry.markObsoleteVersion(ver))
+            removeEntry(entry);
+    }
+
+    /**
      *
      */
     public void cleanupRemoveQueue() {
@@ -316,10 +430,10 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
             RemovedEntryHolder item = rmvQueue.pollFirst();
 
             if (item != null)
-                cctx.dht().removeVersionedEntry(item.key(), item.version());
+                removeVersionedEntry(item.cacheId(), item.key(), item.version());
         }
 
-        if (!cctx.isDrEnabled()) {
+        if (!grp.isDrEnabled()) {
             RemovedEntryHolder item = rmvQueue.peekFirst();
 
             while (item != null && item.expireTime() < U.currentTimeMillis()) {
@@ -328,7 +442,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
                 if (item == null)
                     break;
 
-                cctx.dht().removeVersionedEntry(item.key(), item.version());
+                removeVersionedEntry(item.cacheId(), item.key(), item.version());
 
                 item = rmvQueue.peekFirst();
             }
@@ -336,13 +450,14 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
+     * @param cacheId cacheId Cache ID.
      * @param key Removed key.
      * @param ver Removed version.
      */
-    public void onDeferredDelete(KeyCacheObject key, GridCacheVersion ver) {
+    public void onDeferredDelete(int cacheId, KeyCacheObject key, GridCacheVersion ver) {
         cleanupRemoveQueue();
 
-        rmvQueue.add(new RemovedEntryHolder(key, ver, rmvdEntryTtl));
+        rmvQueue.add(new RemovedEntryHolder(cacheId, key, ver, rmvdEntryTtl));
     }
 
     /**
@@ -434,7 +549,10 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /** {@inheritDoc} */
-    @Override protected void release(int sizeChange, GridCacheEntryEx e) {
+    @Override protected void release(int sizeChange, CacheMapHolder hld, GridCacheEntryEx e) {
+        if (grp.sharedGroup() && sizeChange != 0)
+            hld.size.addAndGet(sizeChange);
+
         release0(sizeChange);
     }
 
@@ -482,16 +600,16 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * @return {@code true} if cas succeeds.
      */
     private boolean casState(long state, GridDhtPartitionState toState) {
-        if (cctx.shared().database().persistenceEnabled()) {
+        if (ctx.database().persistenceEnabled()) {
             synchronized (this) {
                 boolean update = this.state.compareAndSet(state, setPartState(state, toState));
 
                 if (update)
                     try {
-                        cctx.shared().wal().log(new PartitionMetaStateRecord(cctx.cacheId(), id, toState, updateCounter()));
+                        ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, toState, updateCounter()));
                     }
                     catch (IgniteCheckedException e) {
-                        log.error("Error while writing to log", e);
+                        U.error(log, "Error while writing to log", e);
                     }
 
                 return update;
@@ -624,7 +742,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
 
         GridDhtPartitionState partState = getPartState(state);
 
-        if (isEmpty() && !QueryUtils.isEnabled(cctx.config()) && getSize(state) == 0 &&
+        if (isEmpty() && !grp.queriesEnabled() && getSize(state) == 0 &&
             partState == RENTING && getReservations(state) == 0 && !groupReserved() &&
             casState(state, EVICTED)) {
             if (log.isDebugEnabled())
@@ -634,7 +752,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
                 finishDestroy(updateSeq);
         }
         else if (partState == RENTING || shouldBeRenting())
-            cctx.preloader().evictPartitionAsync(this);
+            grp.preloader().evictPartitionAsync(this);
     }
 
     /**
@@ -710,18 +828,13 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
         assert state() == EVICTED : this;
         assert evictGuard.get() == -1;
 
-        if (cctx.isDrEnabled())
-            cctx.dr().partitionEvicted(id);
-
-        cctx.continuousQueries().onPartitionEvicted(id);
-
-        cctx.dataStructures().onPartitionEvicted(id);
+        grp.onPartitionEvicted(id);
 
         destroyCacheDataStore();
 
         rent.onDone();
 
-        ((GridDhtPreloader)cctx.preloader()).onPartitionEvicted(this, updateSeq);
+        ((GridDhtPreloader)grp.preloader()).onPartitionEvicted(this, updateSeq);
 
         clearDeferredDeletes();
     }
@@ -759,7 +872,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      */
     private void destroyCacheDataStore() {
         try {
-            cctx.offheap().destroyCacheDataStore(dataStore());
+            grp.offheap().destroyCacheDataStore(dataStore());
         }
         catch (IgniteCheckedException e) {
             log.error("Unable to destroy cache data store on partition eviction [id=" + id + "]", e);
@@ -778,7 +891,9 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * @return {@code True} if local node is primary for this partition.
      */
     public boolean primary(AffinityTopologyVersion topVer) {
-        return cctx.affinity().primaryByPartition(cctx.localNode(), id, topVer);
+        List<ClusterNode> nodes = grp.affinity().cachedAffinity(topVer).get(id);
+
+        return !nodes.isEmpty() && ctx.localNode().equals(nodes.get(0));
     }
 
     /**
@@ -786,14 +901,23 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * @return {@code True} if local node is backup for this partition.
      */
     public boolean backup(AffinityTopologyVersion topVer) {
-        return cctx.affinity().backupByPartition(cctx.localNode(), id, topVer);
+        List<ClusterNode> nodes = grp.affinity().cachedAffinity(topVer).get(id);
+
+        return nodes.indexOf(ctx.localNode()) > 0;
     }
 
     /**
+     * @param cacheId ID of cache initiated counter update.
+     * @param topVer Topology version for current operation.
      * @return Next update index.
      */
-    public long nextUpdateCounter() {
-        return store.nextUpdateCounter();
+    long nextUpdateCounter(int cacheId, AffinityTopologyVersion topVer, boolean primary, @Nullable Long primaryCntr) {
+        long nextCntr = store.nextUpdateCounter();
+
+        if (grp.sharedGroup())
+            grp.onPartitionCounterUpdate(cacheId, id, primaryCntr != null ? primaryCntr : nextCntr, topVer, primary);
+
+        return nextCntr;
     }
 
     /**
@@ -830,40 +954,128 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * @throws NodeStoppingException If node stopping.
      */
     public void clearAll() throws NodeStoppingException {
-        GridCacheVersion clearVer = cctx.versions().next();
+        GridCacheVersion clearVer = ctx.versions().next();
+
+        GridCacheObsoleteEntryExtras extras = new GridCacheObsoleteEntryExtras(clearVer);
+
+        boolean rec = grp.eventRecordable(EVT_CACHE_REBALANCE_OBJECT_UNLOADED);
 
-        boolean rec = cctx.events().isRecordable(EVT_CACHE_REBALANCE_OBJECT_UNLOADED);
+        if (grp.sharedGroup()) {
+            for (CacheMapHolder hld : cacheMaps.values())
+                clear(hld.map, extras, rec);
+        }
+        else
+            clear(singleCacheEntryMap.map, extras, rec);
 
-        Iterator<GridCacheMapEntry> it = allEntries().iterator();
+        if (!grp.allowFastEviction()) {
+            CacheMapHolder hld = grp.sharedGroup() ? null : singleCacheEntryMap;
 
-        GridCacheObsoleteEntryExtras extras = new GridCacheObsoleteEntryExtras(clearVer);
+            try {
+                GridIterator<CacheDataRow> it0 = grp.offheap().partitionIterator(id);
+
+                while (it0.hasNext()) {
+                    ctx.database().checkpointReadLock();
+
+                    try {
+                        CacheDataRow row = it0.next();
+
+                        if (grp.sharedGroup() && (hld == null || hld.cctx.cacheId() != row.cacheId())) {
+                            hld = cacheMaps.get(row.cacheId());
+
+                            if (hld == null)
+                                continue;
+                        }
+
+                        assert hld != null;
+
+                        GridCacheMapEntry cached = putEntryIfObsoleteOrAbsent(
+                            hld,
+                            hld.cctx,
+                            grp.affinity().lastVersion(),
+                            row.key(),
+                            true,
+                            false);
+
+                        ctx.database().checkpointReadLock();
+
+                        try {if (cached instanceof GridDhtCacheEntry && ((GridDhtCacheEntry)cached).clearInternal(clearVer, extras)) {
+                            if (rec) {
+                                hld.cctx.events().addEvent(cached.partition(),
+                                    cached.key(),
+                                    ctx.localNodeId(),
+                                    (IgniteUuid)null,
+                                    null,
+                                    EVT_CACHE_REBALANCE_OBJECT_UNLOADED,
+                                    null,
+                                    false,
+                                    cached.rawGet(),
+                                    cached.hasValue(),
+                                    null,
+                                    null,
+                                    null,
+                                    false);}
+                            }
+                        }
+                        finally {
+                            ctx.database().checkpointReadUnlock();
+                        }
+                    }
+                    catch (GridDhtInvalidPartitionException e) {
+                        assert isEmpty() && state() == EVICTED : "Invalid error [e=" + e + ", part=" + this + ']';
+
+                        break; // Partition is already concurrently cleared and evicted.
+                    }
+                    finally {
+                        ctx.database().checkpointReadUnlock();
+                    }
+                }
+            }
+            catch (NodeStoppingException e) {
+                if (log.isDebugEnabled())
+                    log.debug("Failed to get iterator for evicted partition: " + id);
+
+                rent.onDone(e);
+
+                throw e;
+            }
+            catch (IgniteCheckedException e) {
+                U.error(log, "Failed to get iterator for evicted partition: " + id, e);
+            }
+        }
+    }
+
+    /**
+     * @param map Map to clear.
+     * @param extras Obsolete extras.
+     * @param evt Unload event flag.
+     * @throws NodeStoppingException
+     */
+    private void clear(ConcurrentMap<KeyCacheObject, GridCacheMapEntry> map,
+        GridCacheObsoleteEntryExtras extras,
+        boolean evt) throws NodeStoppingException {
+        Iterator<GridCacheMapEntry> it = map.values().iterator();
 
         while (it.hasNext()) {
             GridCacheMapEntry cached = null;
 
-            cctx.shared().database().checkpointReadLock();
+            ctx.database().checkpointReadLock();
 
             try {
                 cached = it.next();
 
-                if (cached instanceof GridDhtCacheEntry && ((GridDhtCacheEntry)cached).clearInternal(clearVer, extras)) {
+                if (cached instanceof GridDhtCacheEntry && ((GridDhtCacheEntry)cached).clearInternal(extras.obsoleteVersion(), extras)) {
                     removeEntry(cached);
 
                     if (!cached.isInternal()) {
-                        if (rec) {
-                            cctx.events().addEvent(cached.partition(),
+                        if (evt) {
+                            grp.addCacheEvent(cached.partition(),
                                 cached.key(),
-                                cctx.localNodeId(),
-                                (IgniteUuid)null,
-                                null,
+                                ctx.localNodeId(),
                                 EVT_CACHE_REBALANCE_OBJECT_UNLOADED,
                                 null,
                                 false,
                                 cached.rawGet(),
                                 cached.hasValue(),
-                                null,
-                                null,
-                                null,
                                 false);
                         }
                     }
@@ -886,66 +1098,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
                 U.error(log, "Failed to clear cache entry for evicted partition: " + cached, e);
             }
             finally {
-                cctx.shared().database().checkpointReadUnlock();
-            }
-        }
-
-        if (!cctx.allowFastEviction()) {
-            try {
-                GridIterator<CacheDataRow> it0 = cctx.offheap().iterator(id);
-
-                while (it0.hasNext()) {
-                    try {
-                        CacheDataRow row = it0.next();
-
-                        GridCacheMapEntry cached = putEntryIfObsoleteOrAbsent(cctx.affinity().affinityTopologyVersion(),
-                            row.key(),
-                            true,
-                            false);
-
-                        cctx.shared().database().checkpointReadLock();
-
-                        try {
-                            if (cached instanceof GridDhtCacheEntry && ((GridDhtCacheEntry)cached).clearInternal(clearVer, extras)) {
-                                if (rec) {
-                                    cctx.events().addEvent(cached.partition(),
-                                        cached.key(),
-                                        cctx.localNodeId(),
-                                        (IgniteUuid)null,
-                                        null,
-                                        EVT_CACHE_REBALANCE_OBJECT_UNLOADED,
-                                        null,
-                                        false,
-                                        cached.rawGet(),
-                                        cached.hasValue(),
-                                        null,
-                                        null,
-                                        null,
-                                        false);
-                                }
-                            }
-                        }
-                        finally {
-                            cctx.shared().database().checkpointReadUnlock();
-                        }
-                    }
-                    catch (GridDhtInvalidPartitionException e) {
-                        assert isEmpty() && state() == EVICTED : "Invalid error [e=" + e + ", part=" + this + ']';
-
-                        break; // Partition is already concurrently cleared and evicted.
-                    }
-                }
-            }
-            catch (NodeStoppingException e) {
-                if (log.isDebugEnabled())
-                    log.debug("Failed to get iterator for evicted partition: " + id);
-
-                rent.onDone(e);
-
-                throw e;
-            }
-            catch (IgniteCheckedException e) {
-                U.error(log, "Failed to get iterator for evicted partition: " + id, e);
+                ctx.database().checkpointReadUnlock();
             }
         }
     }
@@ -955,7 +1108,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      */
     private void clearDeferredDeletes() {
         for (RemovedEntryHolder e : rmvQueue)
-            cctx.dht().removeVersionedEntry(e.key(), e.version());
+            removeVersionedEntry(e.cacheId(), e.key(), e.version());
     }
 
     /** {@inheritDoc} */
@@ -980,6 +1133,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(GridDhtLocalPartition.class, this,
+            "grp", grp.cacheOrGroupName(),
             "state", state(),
             "reservations", reservations(),
             "empty", isEmpty(),
@@ -987,12 +1141,25 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /** {@inheritDoc} */
-    @Override public int publicSize() {
+    @Override public int publicSize(int cacheId) {
+        if (grp.sharedGroup()) {
+            CacheMapHolder hld = cacheMaps.get(cacheId);
+
+            return hld != null ? hld.size.get() : 0;
+        }
+
         return getSize(state.get());
     }
 
     /** {@inheritDoc} */
-    @Override public void incrementPublicSize(GridCacheEntryEx e) {
+    @Override public void incrementPublicSize(@Nullable CacheMapHolder hld, GridCacheEntryEx e) {
+        if (grp.sharedGroup()) {
+            if (hld == null)
+                hld = cacheMapHolder(e.context());
+
+            hld.size.incrementAndGet();
+        }
+
         while (true) {
             long state = this.state.get();
 
@@ -1002,7 +1169,14 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /** {@inheritDoc} */
-    @Override public void decrementPublicSize(GridCacheEntryEx e) {
+    @Override public void decrementPublicSize(@Nullable CacheMapHolder hld, GridCacheEntryEx e) {
+        if (grp.sharedGroup()) {
+            if (hld == null)
+                hld = cacheMapHolder(e.context());
+
+            hld.size.decrementAndGet();
+        }
+
         while (true) {
             long state = this.state.get();
 
@@ -1014,6 +1188,22 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
+     * @param cacheId Cache ID.
+     */
+    void onCacheStopped(int cacheId) {
+        assert grp.sharedGroup() : grp.cacheOrGroupName();
+
+        for (Iterator<RemovedEntryHolder> it = rmvQueue.iterator(); it.hasNext();) {
+            RemovedEntryHolder e = it.next();
+
+            if (e.cacheId() == cacheId)
+                it.remove();
+        }
+
+        cacheMaps.remove(cacheId);
+    }
+
+    /**
      * @param state Composite state.
      * @return Partition state.
      */
@@ -1068,6 +1258,9 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * Removed entry holder.
      */
     private static class RemovedEntryHolder {
+        /** */
+        private final int cacheId;
+
         /** Cache key */
         private final KeyCacheObject key;
 
@@ -1078,11 +1271,13 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
         private final long expireTime;
 
         /**
+         * @param cacheId Cache ID.
          * @param key Key.
          * @param ver Entry version.
          * @param ttl TTL.
          */
-        private RemovedEntryHolder(KeyCacheObject key, GridCacheVersion ver, long ttl) {
+        private RemovedEntryHolder(int cacheId, KeyCacheObject key, GridCacheVersion ver, long ttl) {
+            this.cacheId = cacheId;
             this.key = key;
             this.ver = ver;
 
@@ -1090,6 +1285,13 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
         }
 
         /**
+         * @return Cache ID.
+         */
+        int cacheId() {
+            return cacheId;
+        }
+
+        /**
          * @return Key.
          */
         KeyCacheObject key() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockResponse.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockResponse.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockResponse.java
index ea6ca06..87abd6c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockResponse.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtLockResponse.java
@@ -174,7 +174,7 @@ public class GridDhtLockResponse extends GridDistributedLockResponse {
         }
 
         if (preloadEntries != null)
-            marshalInfos(preloadEntries, cctx);
+            marshalInfos(preloadEntries, cctx.shared(), cctx.cacheObjectContext());
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
index c6715e5..4e0608d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtPartitionTopology.java
@@ -88,9 +88,9 @@ public interface GridDhtPartitionTopology {
     public boolean stopping();
 
     /**
-     * @return Cache ID.
+     * @return Cache group ID.
      */
-    public int cacheId();
+    public int groupId();
 
     /**
      * Pre-initializes this topology.
@@ -146,13 +146,12 @@ public interface GridDhtPartitionTopology {
     public void releasePartitions(int... parts);
 
     /**
-     * @param key Cache key.
-     * @param create If {@code true}, then partition will be created if it's not there.
+     * @param part Partition number.
      * @return Local partition.
      * @throws GridDhtInvalidPartitionException If partition is evicted or absent and
      *      does not belong to this node.
      */
-    @Nullable public GridDhtLocalPartition localPartition(Object key, boolean create)
+    @Nullable public GridDhtLocalPartition localPartition(int part)
         throws GridDhtInvalidPartitionException;
 
     /**


[14/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java
index bb31645..a251047 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheIoManager.java
@@ -110,16 +110,11 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
     /** Number of retries using to send messages. */
     private int retryCnt;
 
-    /** Indexed class handlers. */
-    private volatile Map<Integer, IgniteBiInClosure[]> idxClsHandlers = new HashMap<>();
+    /** */
+    private final MessageHandlers cacheHandlers = new MessageHandlers();
 
-    /** Handler registry. */
-    private ConcurrentMap<ListenerKey, IgniteBiInClosure<UUID, GridCacheMessage>>
-        clsHandlers = new ConcurrentHashMap8<>();
-
-    /** Ordered handler registry. */
-    private ConcurrentMap<Object, IgniteBiInClosure<UUID, ? extends GridCacheMessage>> orderedHandlers =
-        new ConcurrentHashMap8<>();
+    /** */
+    private final MessageHandlers grpHandlers = new MessageHandlers();
 
     /** Stopping flag. */
     private boolean stopping;
@@ -144,17 +139,19 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
 
             if (cacheMsg.partitionExchangeMessage()) {
                 if (cacheMsg instanceof GridDhtAffinityAssignmentRequest) {
+                    GridDhtAffinityAssignmentRequest msg0 = (GridDhtAffinityAssignmentRequest)cacheMsg;
+
                     assert cacheMsg.topologyVersion() != null : cacheMsg;
 
                     AffinityTopologyVersion startTopVer = new AffinityTopologyVersion(cctx.localNode().order());
 
-                    DynamicCacheDescriptor cacheDesc = cctx.cache().cacheDescriptor(cacheMsg.cacheId());
+                    CacheGroupDescriptor desc = cctx.cache().cacheGroupDescriptors().get(msg0.groupId());
 
-                    if (cacheDesc != null) {
-                        if (cacheDesc.startTopologyVersion() != null)
-                            startTopVer = cacheDesc.startTopologyVersion();
-                        else if (cacheDesc.receivedFromStartVersion() != null)
-                            startTopVer = cacheDesc.receivedFromStartVersion();
+                    if (desc != null) {
+                        if (desc.startTopologyVersion() != null)
+                            startTopVer = desc.startTopologyVersion();
+                        else if (desc.receivedFromStartVersion() != null)
+                            startTopVer = desc.receivedFromStartVersion();
                     }
 
                     // Need to wait for exchange to avoid race between cache start and affinity request.
@@ -165,7 +162,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                             log.debug("Wait for exchange before processing message [msg=" + msg +
                                 ", node=" + nodeId +
                                 ", waitVer=" + startTopVer +
-                                ", cacheDesc=" + cacheDesc + ']');
+                                ", cacheDesc=" + descriptorForMessage(cacheMsg) + ']');
                         }
 
                         fut.listen(new CI1<IgniteInternalFuture<?>>() {
@@ -260,21 +257,31 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
      */
     @SuppressWarnings("unchecked")
     private void handleMessage(UUID nodeId, GridCacheMessage cacheMsg) {
+        handleMessage(nodeId, cacheMsg, cacheMsg.cacheGroupMessage() ? grpHandlers : cacheHandlers);
+    }
+
+    /**
+     * @param nodeId Sender node ID.
+     * @param cacheMsg Message.
+     * @param msgHandlers Message handlers.
+     */
+    @SuppressWarnings("unchecked")
+    private void handleMessage(UUID nodeId, GridCacheMessage cacheMsg, MessageHandlers msgHandlers) {
         int msgIdx = cacheMsg.lookupIndex();
 
         IgniteBiInClosure<UUID, GridCacheMessage> c = null;
 
         if (msgIdx >= 0) {
-            Map<Integer, IgniteBiInClosure[]> idxClsHandlers0 = idxClsHandlers;
+            Map<Integer, IgniteBiInClosure[]> idxClsHandlers0 = msgHandlers.idxClsHandlers;
 
-            IgniteBiInClosure[] cacheClsHandlers = idxClsHandlers0.get(cacheMsg.cacheId());
+            IgniteBiInClosure[] cacheClsHandlers = idxClsHandlers0.get(cacheMsg.handlerId());
 
             if (cacheClsHandlers != null)
                 c = cacheClsHandlers[msgIdx];
         }
 
         if (c == null)
-            c = clsHandlers.get(new ListenerKey(cacheMsg.cacheId(), cacheMsg.getClass()));
+            c = msgHandlers.clsHandlers.get(new ListenerKey(cacheMsg.handlerId(), cacheMsg.getClass()));
 
         if (c == null) {
             IgniteLogger log = cacheMsg.messageLogger(cctx);
@@ -285,12 +292,12 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
 
             msg0.append(", locTopVer=").append(cctx.exchange().readyAffinityVersion()).
                 append(", msgTopVer=").append(cacheMsg.topologyVersion()).
-                append(", cacheDesc=").append(cctx.cache().cacheDescriptor(cacheMsg.cacheId())).
+                append(", desc=").append(descriptorForMessage(cacheMsg)).
                 append(']');
 
             msg0.append(U.nl()).append("Registered listeners:");
 
-            Map<Integer, IgniteBiInClosure[]> idxClsHandlers0 = idxClsHandlers;
+            Map<Integer, IgniteBiInClosure[]> idxClsHandlers0 = msgHandlers.idxClsHandlers;
 
             for (Map.Entry<Integer, IgniteBiInClosure[]> e : idxClsHandlers0.entrySet())
                 msg0.append(U.nl()).append(e.getKey()).append("=").append(Arrays.toString(e.getValue()));
@@ -323,7 +330,10 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
     @Override protected void onKernalStop0(boolean cancel) {
         cctx.gridIO().removeMessageListener(TOPIC_CACHE);
 
-        for (Object ordTopic : orderedHandlers.keySet())
+        for (Object ordTopic : cacheHandlers.orderedHandlers.keySet())
+            cctx.gridIO().removeMessageListener(ordTopic);
+
+        for (Object ordTopic : grpHandlers.orderedHandlers.keySet())
             cctx.gridIO().removeMessageListener(ordTopic);
 
         boolean interrupted = false;
@@ -520,14 +530,17 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
      */
     private void processFailedMessage(UUID nodeId, GridCacheMessage msg, IgniteBiInClosure<UUID, GridCacheMessage> c)
         throws IgniteCheckedException {
-        GridCacheContext ctx = cctx.cacheContext(msg.cacheId());
+        assert msg != null;
+
+        GridCacheContext ctx = msg instanceof GridCacheIdMessage ?
+            cctx.cacheContext(((GridCacheIdMessage)msg).cacheId()) : null;
 
         switch (msg.directType()) {
             case 30: {
                 GridDhtLockRequest req = (GridDhtLockRequest)msg;
 
                 GridDhtLockResponse res = new GridDhtLockResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.version(),
                     req.futureId(),
                     req.miniId(),
@@ -560,17 +573,17 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridDhtAtomicUpdateRequest req = (GridDhtAtomicUpdateRequest)msg;
 
                 GridDhtAtomicUpdateResponse res = new GridDhtAtomicUpdateResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.partition(),
                     req.futureId(),
-                    ctx.deploymentEnabled());
+                    false);
 
                 res.onError(req.classError());
 
                 sendResponseOnFailedMessage(nodeId, res, cctx, ctx.ioPolicy());
 
                 if (req.nearNodeId() != null) {
-                    GridDhtAtomicNearResponse nearRes = new GridDhtAtomicNearResponse(ctx.cacheId(),
+                    GridDhtAtomicNearResponse nearRes = new GridDhtAtomicNearResponse(req.cacheId(),
                         req.partition(),
                         req.nearFutureId(),
                         nodeId,
@@ -588,12 +601,12 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearAtomicFullUpdateRequest req = (GridNearAtomicFullUpdateRequest)msg;
 
                 GridNearAtomicUpdateResponse res = new GridNearAtomicUpdateResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     nodeId,
                     req.futureId(),
                     req.partition(),
                     false,
-                    ctx.deploymentEnabled());
+                    false);
 
                 res.error(req.classError());
 
@@ -606,10 +619,10 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridDhtForceKeysRequest req = (GridDhtForceKeysRequest)msg;
 
                 GridDhtForceKeysResponse res = new GridDhtForceKeysResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.futureId(),
                     req.miniId(),
-                    ctx.deploymentEnabled()
+                    false
                 );
 
                 res.error(req.classError());
@@ -629,7 +642,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearGetRequest req = (GridNearGetRequest)msg;
 
                 GridNearGetResponse res = new GridNearGetResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.futureId(),
                     req.miniId(),
                     req.version(),
@@ -645,7 +658,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
             case 50: {
                 GridNearGetResponse res = (GridNearGetResponse)msg;
 
-                CacheGetFuture fut = (CacheGetFuture)ctx.mvcc().future(res.futureId());
+                CacheGetFuture fut = (CacheGetFuture)cctx.mvcc().future(res.futureId());
 
                 if (fut == null) {
                     if (log.isDebugEnabled())
@@ -665,7 +678,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearLockRequest req = (GridNearLockRequest)msg;
 
                 GridNearLockResponse res = new GridNearLockResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.version(),
                     req.futureId(),
                     req.miniId(),
@@ -673,7 +686,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                     0,
                     req.classError(),
                     null,
-                    ctx.deploymentEnabled());
+                    false);
 
                 sendResponseOnFailedMessage(nodeId, res, cctx, ctx.ioPolicy());
             }
@@ -712,7 +725,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                     cctx.deploymentEnabled());
 
                 cctx.io().sendOrderedMessage(
-                    ctx.node(nodeId),
+                    cctx.node(nodeId),
                     TOPIC_CACHE.topic(QUERY_TOPIC_PREFIX, nodeId, req.id()),
                     res,
                     ctx.ioPolicy(),
@@ -731,7 +744,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearSingleGetRequest req = (GridNearSingleGetRequest)msg;
 
                 GridNearSingleGetResponse res = new GridNearSingleGetResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.futureId(),
                     req.topologyVersion(),
                     null,
@@ -748,7 +761,7 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
             case 117: {
                 GridNearSingleGetResponse res = (GridNearSingleGetResponse)msg;
 
-                GridPartitionedSingleGetFuture fut = (GridPartitionedSingleGetFuture)ctx.mvcc()
+                GridPartitionedSingleGetFuture fut = (GridPartitionedSingleGetFuture)cctx.mvcc()
                     .future(new IgniteUuid(IgniteUuid.VM_ID, res.futureId()));
 
                 if (fut == null) {
@@ -769,12 +782,12 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearAtomicSingleUpdateRequest req = (GridNearAtomicSingleUpdateRequest)msg;
 
                 GridNearAtomicUpdateResponse res = new GridNearAtomicUpdateResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     nodeId,
                     req.futureId(),
                     req.partition(),
                     false,
-                    ctx.deploymentEnabled());
+                    false);
 
                 res.error(req.classError());
 
@@ -787,12 +800,12 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearAtomicSingleUpdateInvokeRequest req = (GridNearAtomicSingleUpdateInvokeRequest)msg;
 
                 GridNearAtomicUpdateResponse res = new GridNearAtomicUpdateResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     nodeId,
                     req.futureId(),
                     req.partition(),
                     false,
-                    ctx.deploymentEnabled());
+                    false);
 
                 res.error(req.classError());
 
@@ -805,12 +818,12 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridNearAtomicSingleUpdateFilterRequest req = (GridNearAtomicSingleUpdateFilterRequest)msg;
 
                 GridNearAtomicUpdateResponse res = new GridNearAtomicUpdateResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     nodeId,
                     req.futureId(),
                     req.partition(),
                     false,
-                    ctx.deploymentEnabled());
+                    false);
 
                 res.error(req.classError());
 
@@ -823,17 +836,17 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 GridDhtAtomicSingleUpdateRequest req = (GridDhtAtomicSingleUpdateRequest)msg;
 
                 GridDhtAtomicUpdateResponse res = new GridDhtAtomicUpdateResponse(
-                    ctx.cacheId(),
+                    req.cacheId(),
                     req.partition(),
                     req.futureId(),
-                    ctx.deploymentEnabled());
+                    false);
 
                 res.onError(req.classError());
 
                 sendResponseOnFailedMessage(nodeId, res, cctx, ctx.ioPolicy());
 
                 if (req.nearNodeId() != null) {
-                    GridDhtAtomicNearResponse nearRes = new GridDhtAtomicNearResponse(ctx.cacheId(),
+                    GridDhtAtomicNearResponse nearRes = new GridDhtAtomicNearResponse(req.cacheId(),
                         req.partition(),
                         req.nearFutureId(),
                         nodeId,
@@ -887,8 +900,8 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
                 if (txState != null)
                     txState.unwindEvicts(cctx);
             }
-            else {
-                GridCacheContext ctx = cctx.cacheContext(msg.cacheId());
+            else if (msg instanceof GridCacheIdMessage) {
+                GridCacheContext ctx = cctx.cacheContext(((GridCacheIdMessage)msg).cacheId());
 
                 if (ctx != null)
                     CU.unwindEvicts(ctx);
@@ -1180,79 +1193,125 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
     }
 
     /**
-     * Adds message handler.
-     *
-     * @param cacheId Cache ID.
+     * @param hndId Message handler ID.
      * @param type Type of message.
      * @param c Handler.
      */
-    @SuppressWarnings({"unchecked"})
-    public void addHandler(
-        int cacheId,
+    public void addCacheHandler(
+        int hndId,
         Class<? extends GridCacheMessage> type,
         IgniteBiInClosure<UUID, ? extends GridCacheMessage> c) {
+        assert !type.isAssignableFrom(GridCacheGroupIdMessage.class) : type;
+
+        addHandler(hndId, type, c, cacheHandlers);
+    }
+
+    /**
+     * @param hndId Message handler ID.
+     * @param type Type of message.
+     * @param c Handler.
+     */
+    public void addCacheGroupHandler(
+        int hndId,
+        Class<? extends GridCacheGroupIdMessage> type,
+        IgniteBiInClosure<UUID, ? extends GridCacheMessage> c) {
+        assert !type.isAssignableFrom(GridCacheIdMessage.class) : type;
+
+        addHandler(hndId, type, c, grpHandlers);
+    }
+
+    /**
+     * @param hndId Message handler ID.
+     * @param type Type of message.
+     * @param c Handler.
+     * @param msgHandlers Message handlers.
+     */
+    @SuppressWarnings({"unchecked"})
+    private void addHandler(
+        int hndId,
+        Class<? extends GridCacheMessage> type,
+        IgniteBiInClosure<UUID, ? extends GridCacheMessage> c,
+        MessageHandlers msgHandlers) {
         int msgIdx = messageIndex(type);
 
         if (msgIdx != -1) {
-            Map<Integer, IgniteBiInClosure[]> idxClsHandlers0 = idxClsHandlers;
+            Map<Integer, IgniteBiInClosure[]> idxClsHandlers0 = msgHandlers.idxClsHandlers;
 
-            IgniteBiInClosure[] cacheClsHandlers = idxClsHandlers0.get(cacheId);
+            IgniteBiInClosure[] cacheClsHandlers = idxClsHandlers0.get(hndId);
 
             if (cacheClsHandlers == null) {
                 cacheClsHandlers = new IgniteBiInClosure[GridCacheMessage.MAX_CACHE_MSG_LOOKUP_INDEX];
 
-                idxClsHandlers0.put(cacheId, cacheClsHandlers);
+                idxClsHandlers0.put(hndId, cacheClsHandlers);
             }
 
             if (cacheClsHandlers[msgIdx] != null)
-                throw new IgniteException("Duplicate cache message ID found [cacheId=" + cacheId +
+                throw new IgniteException("Duplicate cache message ID found [hndId=" + hndId +
                     ", type=" + type + ']');
 
             cacheClsHandlers[msgIdx] = c;
 
-            idxClsHandlers = idxClsHandlers0;
+            msgHandlers.idxClsHandlers = idxClsHandlers0;
 
             return;
         }
         else {
-            ListenerKey key = new ListenerKey(cacheId, type);
+            ListenerKey key = new ListenerKey(hndId, type);
 
-            if (clsHandlers.putIfAbsent(key,
+            if (msgHandlers.clsHandlers.putIfAbsent(key,
                 (IgniteBiInClosure<UUID, GridCacheMessage>)c) != null)
-                assert false : "Handler for class already registered [cacheId=" + cacheId + ", cls=" + type +
-                    ", old=" + clsHandlers.get(key) + ", new=" + c + ']';
+                assert false : "Handler for class already registered [hndId=" + hndId + ", cls=" + type +
+                    ", old=" + msgHandlers.clsHandlers.get(key) + ", new=" + c + ']';
         }
 
         IgniteLogger log0 = log;
 
         if (log0 != null && log0.isTraceEnabled())
             log0.trace(
-                "Registered cache communication handler [cacheId=" + cacheId + ", type=" + type +
+                "Registered cache communication handler [hndId=" + hndId + ", type=" + type +
                     ", msgIdx=" + msgIdx + ", handler=" + c + ']');
     }
 
     /**
      * @param cacheId Cache ID to remove handlers for.
      */
-    public void removeHandlers(int cacheId) {
-        assert cacheId != 0;
+    void removeCacheHandlers(int cacheId) {
+        removeHandlers(cacheHandlers, cacheId);
+    }
+
+    /**
+     * @param grpId Cache group ID to remove handlers for.
+     */
+    void removeCacheGroupHandlers(int grpId) {
+        removeHandlers(grpHandlers, grpId);
+    }
+
+    /**
+     * @param msgHandlers Handlers.
+     * @param hndId ID to remove handlers for.
+     */
+    private void removeHandlers(MessageHandlers msgHandlers, int hndId) {
+        assert hndId != 0;
 
-        idxClsHandlers.remove(cacheId);
+        msgHandlers.idxClsHandlers.remove(hndId);
 
-        for (Iterator<ListenerKey> iter = clsHandlers.keySet().iterator(); iter.hasNext(); ) {
+        for (Iterator<ListenerKey> iter = msgHandlers.clsHandlers.keySet().iterator(); iter.hasNext(); ) {
             ListenerKey key = iter.next();
 
-            if (key.cacheId == cacheId)
+            if (key.hndId == hndId)
                 iter.remove();
         }
     }
 
     /**
-     * @param cacheId Cache ID to remove handlers for.
+     * @param cacheGrp {@code True} if cache group handler, {@code false} if cache handler.
+     * @param hndId Handler ID.
      * @param type Message type.
      */
-    public void removeHandler(int cacheId, Class<? extends GridCacheMessage> type) {
-        clsHandlers.remove(new ListenerKey(cacheId, type));
+    public void removeHandler(boolean cacheGrp, int hndId, Class<? extends GridCacheMessage> type) {
+        MessageHandlers msgHandlers = cacheGrp ? grpHandlers : cacheHandlers;
+
+        msgHandlers.clsHandlers.remove(new ListenerKey(hndId, type));
     }
 
     /**
@@ -1274,16 +1333,35 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
     }
 
     /**
+     * @param topic Topic.
+     * @param c Handler.
+     */
+    public void addOrderedCacheHandler(Object topic, IgniteBiInClosure<UUID, ? extends GridCacheIdMessage> c) {
+        addOrderedHandler(false, topic, c);
+    }
+
+    /**
+     * @param topic Topic.
+     * @param c Handler.
+     */
+    public void addOrderedCacheGroupHandler(Object topic, IgniteBiInClosure<UUID, ? extends GridCacheGroupIdMessage> c) {
+        addOrderedHandler(true, topic, c);
+    }
+
+    /**
      * Adds ordered message handler.
      *
+     * @param cacheGrp {@code True} if cache group message, {@code false} if cache message.
      * @param topic Topic.
      * @param c Handler.
      */
     @SuppressWarnings({"unchecked"})
-    public void addOrderedHandler(Object topic, IgniteBiInClosure<UUID, ? extends GridCacheMessage> c) {
+    private void addOrderedHandler(boolean cacheGrp, Object topic, IgniteBiInClosure<UUID, ? extends GridCacheMessage> c) {
+        MessageHandlers msgHandlers = cacheGrp ? grpHandlers : cacheHandlers;
+
         IgniteLogger log0 = log;
 
-        if (orderedHandlers.putIfAbsent(topic, c) == null) {
+        if (msgHandlers.orderedHandlers.putIfAbsent(topic, c) == null) {
             cctx.gridIO().addMessageListener(topic, new OrderedMessageListener(
                 (IgniteBiInClosure<UUID, GridCacheMessage>)c));
 
@@ -1298,10 +1376,13 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
     /**
      * Removed ordered message handler.
      *
+     * @param cacheGrp {@code True} if cache group message, {@code false} if cache message.
      * @param topic Topic.
      */
-    public void removeOrderedHandler(Object topic) {
-        if (orderedHandlers.remove(topic) != null) {
+    public void removeOrderedHandler(boolean cacheGrp, Object topic) {
+        MessageHandlers msgHandlers = cacheGrp ? grpHandlers : cacheHandlers;
+
+        if (msgHandlers.orderedHandlers.remove(topic) != null) {
             cctx.gridIO().removeMessageListener(topic);
 
             if (log != null && log.isDebugEnabled())
@@ -1352,12 +1433,43 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
         }
     }
 
+    /**
+     * @param msg Message.
+     * @return Cache or group descriptor.
+     */
+    private Object descriptorForMessage(GridCacheMessage msg) {
+        if (msg instanceof GridCacheIdMessage)
+            return cctx.cache().cacheDescriptor(((GridCacheIdMessage)msg).cacheId());
+        else if (msg instanceof GridCacheGroupIdMessage)
+            return cctx.cache().cacheGroupDescriptors().get(((GridCacheGroupIdMessage)msg).groupId());
+
+        return null;
+    }
+
     /** {@inheritDoc} */
     @Override public void printMemoryStats() {
         X.println(">>> ");
         X.println(">>> Cache IO manager memory stats [igniteInstanceName=" + cctx.igniteInstanceName() + ']');
-        X.println(">>>   clsHandlersSize: " + clsHandlers.size());
-        X.println(">>>   orderedHandlersSize: " + orderedHandlers.size());
+        X.println(">>>   cacheClsHandlersSize: " + cacheHandlers.clsHandlers.size());
+        X.println(">>>   cacheOrderedHandlersSize: " + cacheHandlers.orderedHandlers.size());
+        X.println(">>>   cacheGrpClsHandlersSize: " + grpHandlers.clsHandlers.size());
+        X.println(">>>   cacheGrpOrderedHandlersSize: " + grpHandlers.orderedHandlers.size());
+    }
+
+    /**
+     *
+     */
+    static class MessageHandlers {
+        /** Indexed class handlers. */
+        volatile Map<Integer, IgniteBiInClosure[]> idxClsHandlers = new HashMap<>();
+
+        /** Handler registry. */
+        ConcurrentMap<ListenerKey, IgniteBiInClosure<UUID, GridCacheMessage>>
+            clsHandlers = new ConcurrentHashMap8<>();
+
+        /** Ordered handler registry. */
+        ConcurrentMap<Object, IgniteBiInClosure<UUID, ? extends GridCacheMessage>> orderedHandlers =
+            new ConcurrentHashMap8<>();
     }
 
     /**
@@ -1391,17 +1503,17 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
      */
     private static class ListenerKey {
         /** Cache ID. */
-        private int cacheId;
+        private int hndId;
 
         /** Message class. */
         private Class<? extends GridCacheMessage> msgCls;
 
         /**
-         * @param cacheId Cache ID.
+         * @param hndId Handler ID.
          * @param msgCls Message class.
          */
-        private ListenerKey(int cacheId, Class<? extends GridCacheMessage> msgCls) {
-            this.cacheId = cacheId;
+        private ListenerKey(int hndId, Class<? extends GridCacheMessage> msgCls) {
+            this.hndId = hndId;
             this.msgCls = msgCls;
         }
 
@@ -1415,12 +1527,12 @@ public class GridCacheIoManager extends GridCacheSharedManagerAdapter {
 
             ListenerKey that = (ListenerKey)o;
 
-            return cacheId == that.cacheId && msgCls.equals(that.msgCls);
+            return hndId == that.hndId && msgCls.equals(that.msgCls);
         }
 
         /** {@inheritDoc} */
         @Override public int hashCode() {
-            int res = cacheId;
+            int res = hndId;
 
             res = 31 * res + msgCls.hashCode();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheLocalConcurrentMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheLocalConcurrentMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheLocalConcurrentMap.java
index db99272..63cfe1f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheLocalConcurrentMap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheLocalConcurrentMap.java
@@ -18,37 +18,65 @@
 
 package org.apache.ignite.internal.processors.cache;
 
-import java.util.concurrent.atomic.AtomicInteger;
+import org.jetbrains.annotations.Nullable;
+import org.jsr166.ConcurrentHashMap8;
 
 /**
  * GridCacheConcurrentMap implementation for local and near caches.
  */
 public class GridCacheLocalConcurrentMap extends GridCacheConcurrentMapImpl {
     /** */
-    private final AtomicInteger pubSize = new AtomicInteger();
+    private final int cacheId;
 
-    public GridCacheLocalConcurrentMap(GridCacheContext ctx,
-        GridCacheMapEntryFactory factory, int initialCapacity) {
-        super(ctx, factory, initialCapacity);
+    /** */
+    private final CacheMapHolder entryMap;
+
+    /**
+     * @param cctx Cache context.
+     * @param factory Entry factory.
+     * @param initCap Initial capacity.
+     */
+    public GridCacheLocalConcurrentMap(GridCacheContext cctx, GridCacheMapEntryFactory factory, int initCap) {
+        super(factory);
+
+        this.cacheId = cctx.cacheId();
+        this.entryMap = new CacheMapHolder(cctx,
+            new ConcurrentHashMap8<KeyCacheObject, GridCacheMapEntry>(initCap, 0.75f, Runtime.getRuntime().availableProcessors() * 2));
     }
 
-    public GridCacheLocalConcurrentMap(GridCacheContext ctx,
-        GridCacheMapEntryFactory factory, int initialCapacity, float loadFactor, int concurrencyLevel) {
-        super(ctx, factory, initialCapacity, loadFactor, concurrencyLevel);
+    /** {@inheritDoc} */
+    @Override public int internalSize() {
+        return entryMap.map.size();
     }
 
     /** {@inheritDoc} */
-    @Override public int publicSize() {
-        return pubSize.get();
+    @Nullable @Override protected CacheMapHolder entriesMap(GridCacheContext cctx) {
+        return entryMap;
     }
 
     /** {@inheritDoc} */
-    @Override public void incrementPublicSize(GridCacheEntryEx e) {
-        pubSize.incrementAndGet();
+    @Nullable @Override protected CacheMapHolder entriesMapIfExists(Integer cacheId) {
+        return entryMap;
     }
 
     /** {@inheritDoc} */
-    @Override public void decrementPublicSize(GridCacheEntryEx e) {
-        pubSize.decrementAndGet();
+    @Override public int publicSize(int cacheId) {
+        assert this.cacheId == cacheId;
+
+        return entryMap.size.get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void incrementPublicSize(CacheMapHolder hld, GridCacheEntryEx e) {
+        assert cacheId == e.context().cacheId();
+
+        entryMap.size.incrementAndGet();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void decrementPublicSize(CacheMapHolder hld, GridCacheEntryEx e) {
+        assert cacheId == e.context().cacheId();
+
+        entryMap.size.decrementAndGet();
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index 5e57ca4..28656fb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -981,7 +981,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
             if (cctx.deferredDelete() && deletedUnlocked() && !isInternal() && !detached())
                 deletedUnlocked(false);
 
-            updateCntr0 = nextPartCounter(topVer);
+            updateCntr0 = nextPartitionCounter(topVer, tx == null || tx.local(), updateCntr);
 
             if (updateCntr != null && updateCntr != 0)
                 updateCntr0 = updateCntr;
@@ -1160,7 +1160,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 }
             }
 
-            updateCntr0 = nextPartCounter(topVer);
+            updateCntr0 = nextPartitionCounter(topVer, tx == null || tx.local(), updateCntr);
 
             if (updateCntr != null && updateCntr != 0)
                 updateCntr0 = updateCntr;
@@ -1562,7 +1562,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 updateMetrics(op, metrics);
 
             if (lsnrCol != null) {
-                long updateCntr = nextPartCounter(AffinityTopologyVersion.NONE);
+                long updateCntr = nextPartitionCounter(AffinityTopologyVersion.NONE, true, null);
 
                 cctx.continuousQueries().onEntryUpdated(
                     lsnrCol,
@@ -1651,6 +1651,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 (op == GridCacheOperation.TRANSFORM || cctx.loadPreviousValue()));
 
             c = new AtomicCacheUpdateClosure(this,
+                topVer,
                 newVer,
                 op,
                 writeObj,
@@ -1677,7 +1678,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 c.call(dataRow);
             }
             else
-                cctx.offheap().invoke(key, localPartition(), c);
+                cctx.offheap().invoke(cctx, key, localPartition(), c);
 
             GridCacheUpdateAtomicResult updateRes = c.updateRes;
 
@@ -1722,7 +1723,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                         else
                             evtVal = (CacheObject)writeObj;
 
-                        long updateCntr0 = nextPartCounter();
+                        long updateCntr0 = nextPartitionCounter(topVer, primary, updateCntr);
 
                         if (updateCntr != null)
                             updateCntr0 = updateCntr;
@@ -2612,7 +2613,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 long updateCntr = 0;
 
                 if (!preload)
-                    updateCntr = nextPartCounter(topVer);
+                    updateCntr = nextPartitionCounter(topVer, true, null);
 
                 if (walEnabled) {
                     cctx.shared().wal().log(new DataRecord(new DataEntry(
@@ -2668,33 +2669,15 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
     }
 
     /**
+     * @param topVer Topology version for current operation.
+     * @param primary Primary node update flag.
+     * @param primaryCntr Counter assigned on primary node.
      * @return Update counter.
      */
-    protected long nextPartCounter() {
+    protected long nextPartitionCounter(AffinityTopologyVersion topVer, boolean primary, @Nullable Long primaryCntr) {
         return 0;
     }
 
-    /**
-     * @param topVer Topology version.
-     * @return Update counter.
-     */
-    private long nextPartCounter(AffinityTopologyVersion topVer) {
-        long updateCntr;
-
-        if (!cctx.isLocal() && !isNear()) {
-            GridDhtLocalPartition locPart = cctx.topology().localPartition(partition(), topVer, false);
-
-            if (locPart == null)
-                return 0;
-
-            updateCntr = locPart.nextUpdateCounter();
-        }
-        else
-            updateCntr = 0;
-
-        return updateCntr;
-    }
-
     /** {@inheritDoc} */
     @Override public synchronized GridCacheVersionedEntryEx versionedEntry(final boolean keepBinary)
         throws IgniteCheckedException, GridCacheEntryRemovedException {
@@ -3192,7 +3175,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         synchronized (this) {
             checkObsolete();
 
-            cctx.offheap().updateIndexes(key, localPartition());
+            if (cctx.queries().enabled())
+                cctx.offheap().updateIndexes(cctx, key, localPartition());
         }
     }
 
@@ -3227,14 +3211,14 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
      * @param oldRow Old row if available.
      * @throws IgniteCheckedException If update failed.
      */
-    protected void storeValue(@Nullable CacheObject val,
+    protected void storeValue(CacheObject val,
         long expireTime,
         GridCacheVersion ver,
         @Nullable CacheDataRow oldRow) throws IgniteCheckedException {
         assert Thread.holdsLock(this);
         assert val != null : "null values in update for key: " + key;
 
-        cctx.offheap().invoke(key,  localPartition(), new UpdateClosure(this, val, ver, expireTime));
+        cctx.offheap().invoke(cctx, key,  localPartition(), new UpdateClosure(this, val, ver, expireTime));
     }
 
     /**
@@ -3276,7 +3260,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
     protected void removeValue() throws IgniteCheckedException {
         assert Thread.holdsLock(this);
 
-        cctx.offheap().remove(key, partition(), localPartition());
+        cctx.offheap().remove(cctx, key, partition(), localPartition());
     }
 
     /** {@inheritDoc} */
@@ -3606,7 +3590,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         GridDhtLocalPartition locPart = localPartition();
 
         if (locPart != null)
-            locPart.incrementPublicSize(this);
+            locPart.incrementPublicSize(null, this);
         else
             cctx.incrementPublicSize(this);
     }
@@ -3618,7 +3602,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         GridDhtLocalPartition locPart = localPartition();
 
         if (locPart != null)
-            locPart.decrementPublicSize(this);
+            locPart.decrementPublicSize(null, this);
         else
             cctx.decrementPublicSize(this);
     }
@@ -3930,7 +3914,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
             if (oldRow != null)
                 oldRow.key(entry.key);
 
-            newRow = entry.cctx.offheap().dataStore(entry.localPartition()).createRow(entry.key,
+            newRow = entry.cctx.offheap().dataStore(entry.localPartition()).createRow(
+                entry.cctx,
+                entry.key,
                 val,
                 ver,
                 expireTime,
@@ -3964,6 +3950,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         private final GridCacheMapEntry entry;
 
         /** */
+        private final AffinityTopologyVersion topVer;
+
+        /** */
         private GridCacheVersion newVer;
 
         /** */
@@ -4026,7 +4015,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
         /** */
         private CacheDataRow oldRow;
 
-        AtomicCacheUpdateClosure(GridCacheMapEntry entry,
+        AtomicCacheUpdateClosure(
+            GridCacheMapEntry entry,
+            AffinityTopologyVersion topVer,
             GridCacheVersion newVer,
             GridCacheOperation op,
             Object writeObj,
@@ -4047,6 +4038,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
             assert op == UPDATE || op == DELETE || op == TRANSFORM : op;
 
             this.entry = entry;
+            this.topVer = topVer;
             this.newVer = newVer;
             this.op = op;
             this.writeObj = writeObj;
@@ -4282,7 +4274,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
             }
 
             if (needUpdate) {
-                newRow = entry.localPartition().dataStore().createRow(entry.key,
+                newRow = entry.localPartition().dataStore().createRow(
+                    entry.cctx,
+                    entry.key,
                     storeLoadedVal,
                     newVer,
                     entry.expireTimeExtras(),
@@ -4413,7 +4407,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                     ", locNodeId=" + cctx.localNodeId() + ']';
             }
 
-            long updateCntr0 = entry.nextPartCounter();
+            long updateCntr0 = entry.nextPartitionCounter(topVer, primary, updateCntr);
 
             if (updateCntr != null)
                 updateCntr0 = updateCntr;
@@ -4421,7 +4415,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
             entry.logUpdate(op, updated, newVer, newExpireTime, updateCntr0);
 
             if (!entry.isNear()) {
-                newRow = entry.localPartition().dataStore().createRow(entry.key,
+                newRow = entry.localPartition().dataStore().createRow(
+                    entry.cctx,
+                    entry.key,
                     updated,
                     newVer,
                     newExpireTime,
@@ -4495,7 +4491,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 // Must persist inside synchronization in non-tx mode.
                 cctx.store().remove(null, entry.key);
 
-            long updateCntr0 = entry.nextPartCounter();
+            long updateCntr0 = entry.nextPartitionCounter(topVer, primary, updateCntr);
 
             if (updateCntr != null)
                 updateCntr0 = updateCntr;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMessage.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMessage.java
index 4de465c..11916e9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMessage.java
@@ -84,9 +84,15 @@ public abstract class GridCacheMessage implements Message {
     @GridDirectTransient
     private boolean skipPrepare;
 
-    /** Cache ID. */
-    @GridToStringInclude
-    protected int cacheId;
+    /**
+     * @return ID to distinguish message handlers for the same messages but for different caches/cache groups.
+     */
+    public abstract int handlerId();
+
+    /**
+     * @return {@code True} if cache group message.
+     */
+    public abstract boolean cacheGroupMessage();
 
     /**
      * @return Error, if any.
@@ -170,20 +176,6 @@ public abstract class GridCacheMessage implements Message {
     }
 
     /**
-     * @return Cache ID.
-     */
-    public int cacheId() {
-        return cacheId;
-    }
-
-    /**
-     * @param cacheId Cache ID.
-     */
-    public void cacheId(int cacheId) {
-        this.cacheId = cacheId;
-    }
-
-    /**
      * Gets topology version or -1 in case of topology version is not required for this message.
      *
      * @return Topology version.
@@ -205,6 +197,15 @@ public abstract class GridCacheMessage implements Message {
      * @throws IgniteCheckedException If failed.
      */
     protected final void prepareObject(@Nullable Object o, GridCacheContext ctx) throws IgniteCheckedException {
+        prepareObject(o, ctx.shared());
+    }
+
+    /**
+     * @param o Object to prepare for marshalling.
+     * @param ctx Context.
+     * @throws IgniteCheckedException If failed.
+     */
+    protected final void prepareObject(@Nullable Object o, GridCacheSharedContext ctx) throws IgniteCheckedException {
         assert addDepInfo || forceAddDepInfo;
 
         if (!skipPrepare && o != null) {
@@ -279,24 +280,28 @@ public abstract class GridCacheMessage implements Message {
     /**
      * @param info Entry to marshal.
      * @param ctx Context.
+     * @param cacheObjCtx Cache object context.
      * @throws IgniteCheckedException If failed.
      */
-    protected final void marshalInfo(GridCacheEntryInfo info, GridCacheContext ctx) throws IgniteCheckedException {
+    protected final void marshalInfo(GridCacheEntryInfo info,
+        GridCacheSharedContext ctx,
+        CacheObjectContext cacheObjCtx
+    ) throws IgniteCheckedException {
         assert ctx != null;
 
         if (info != null) {
-            info.marshal(ctx);
+            info.marshal(cacheObjCtx);
 
             if (addDepInfo) {
                 if (info.key() != null)
-                    prepareObject(info.key().value(ctx.cacheObjectContext(), false), ctx);
+                    prepareObject(info.key().value(cacheObjCtx, false), ctx);
 
                 CacheObject val = info.value();
 
                 if (val != null) {
-                    val.finishUnmarshal(ctx.cacheObjectContext(), ctx.deploy().globalLoader());
+                    val.finishUnmarshal(cacheObjCtx, ctx.deploy().globalLoader());
 
-                    prepareObject(CU.value(val, ctx, false), ctx);
+                    prepareObject(val.value(cacheObjCtx, false), ctx);
                 }
             }
         }
@@ -314,7 +319,7 @@ public abstract class GridCacheMessage implements Message {
         assert ctx != null;
 
         if (info != null)
-            info.unmarshal(ctx, ldr);
+            info.unmarshal(ctx.cacheObjectContext(), ldr);
     }
 
     /**
@@ -324,13 +329,14 @@ public abstract class GridCacheMessage implements Message {
      */
     protected final void marshalInfos(
         Iterable<? extends GridCacheEntryInfo> infos,
-        GridCacheContext ctx
+        GridCacheSharedContext ctx,
+        CacheObjectContext cacheObjCtx
     ) throws IgniteCheckedException {
         assert ctx != null;
 
         if (infos != null)
             for (GridCacheEntryInfo e : infos)
-                marshalInfo(e, ctx);
+                marshalInfo(e, ctx, cacheObjCtx);
     }
 
     /**
@@ -369,14 +375,14 @@ public abstract class GridCacheMessage implements Message {
 
                 if (addDepInfo) {
                     if (e.key() != null)
-                        prepareObject(e.key().value(cctx.cacheObjectContext(), false), cctx);
+                        prepareObject(e.key().value(cctx.cacheObjectContext(), false), ctx);
 
                     if (e.value() != null)
-                        prepareObject(e.value().value(cctx.cacheObjectContext(), false), cctx);
+                        prepareObject(e.value().value(cctx.cacheObjectContext(), false), ctx);
 
                     if (e.entryProcessors() != null) {
                         for (T2<EntryProcessor<Object, Object, Object>, Object[]> entProc : e.entryProcessors())
-                            prepareObject(entProc.get1(), cctx);
+                            prepareObject(entProc.get1(), ctx);
                     }
                 }
                 else if (p2pEnabled && e.entryProcessors() != null) {
@@ -384,7 +390,7 @@ public abstract class GridCacheMessage implements Message {
                         forceAddDepInfo = true;
 
                     for (T2<EntryProcessor<Object, Object, Object>, Object[]> entProc : e.entryProcessors())
-                        prepareObject(entProc.get1(), cctx);
+                        prepareObject(entProc.get1(), ctx);
                 }
             }
         }
@@ -435,7 +441,7 @@ public abstract class GridCacheMessage implements Message {
             Object arg = args[i];
 
             if (addDepInfo)
-                prepareObject(arg, ctx);
+                prepareObject(arg, ctx.shared());
 
             argsBytes[i] = arg == null ? null : CU.marshal(ctx, arg);
         }
@@ -487,7 +493,7 @@ public abstract class GridCacheMessage implements Message {
 
         for (Object o : col) {
             if (addDepInfo)
-                prepareObject(o, ctx);
+                prepareObject(o, ctx.shared());
 
             byteCol.add(o == null ? null : CU.marshal(ctx, o));
         }
@@ -522,7 +528,7 @@ public abstract class GridCacheMessage implements Message {
             obj.prepareMarshal(ctx.cacheObjectContext());
 
             if (addDepInfo)
-                prepareObject(obj.value(ctx.cacheObjectContext(), false), ctx);
+                prepareObject(obj.value(ctx.cacheObjectContext(), false), ctx.shared());
         }
     }
 
@@ -541,7 +547,7 @@ public abstract class GridCacheMessage implements Message {
                 obj.prepareMarshal(ctx.cacheObjectContext());
 
                 if (addDepInfo)
-                    prepareObject(obj.value(ctx.cacheObjectContext(), false), ctx);
+                    prepareObject(obj.value(ctx.cacheObjectContext(), false), ctx.shared());
             }
         }
     }
@@ -630,6 +636,11 @@ public abstract class GridCacheMessage implements Message {
     }
 
     /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 2;
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
         writer.setBuffer(buf);
 
@@ -642,18 +653,12 @@ public abstract class GridCacheMessage implements Message {
 
         switch (writer.state()) {
             case 0:
-                if (!writer.writeInt("cacheId", cacheId))
-                    return false;
-
-                writer.incrementState();
-
-            case 1:
                 if (!writer.writeMessage("depInfo", depInfo))
                     return false;
 
                 writer.incrementState();
 
-            case 2:
+            case 1:
                 if (!writer.writeLong("msgId", msgId))
                     return false;
 
@@ -673,14 +678,6 @@ public abstract class GridCacheMessage implements Message {
 
         switch (reader.state()) {
             case 0:
-                cacheId = reader.readInt("cacheId");
-
-                if (!reader.isLastRead())
-                    return false;
-
-                reader.incrementState();
-
-            case 1:
                 depInfo = reader.readMessage("depInfo");
 
                 if (!reader.isLastRead())
@@ -688,7 +685,7 @@ public abstract class GridCacheMessage implements Message {
 
                 reader.incrementState();
 
-            case 2:
+            case 1:
                 msgId = reader.readLong("msgId");
 
                 if (!reader.isLastRead())
@@ -714,6 +711,6 @@ public abstract class GridCacheMessage implements Message {
 
     /** {@inheritDoc} */
     @Override public String toString() {
-        return S.toString(GridCacheMessage.class, this, "cacheId", cacheId);
+        return S.toString(GridCacheMessage.class, this);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
index bdc2216..ae4c164 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
@@ -96,7 +96,6 @@ import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.util.worker.GridWorker;
 import org.apache.ignite.lang.IgniteBiInClosure;
-import org.apache.ignite.lang.IgniteInClosure;
 import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.lang.IgniteUuid;
 import org.apache.ignite.thread.IgniteThread;
@@ -310,21 +309,21 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
         cctx.gridEvents().addDiscoveryEventListener(discoLsnr, EVT_NODE_JOINED, EVT_NODE_LEFT, EVT_NODE_FAILED,
             EVT_DISCOVERY_CUSTOM_EVT);
 
-        cctx.io().addHandler(0, GridDhtPartitionsSingleMessage.class,
+        cctx.io().addCacheHandler(0, GridDhtPartitionsSingleMessage.class,
             new MessageHandler<GridDhtPartitionsSingleMessage>() {
                 @Override public void onMessage(ClusterNode node, GridDhtPartitionsSingleMessage msg) {
                     processSinglePartitionUpdate(node, msg);
                 }
             });
 
-        cctx.io().addHandler(0, GridDhtPartitionsFullMessage.class,
+        cctx.io().addCacheHandler(0, GridDhtPartitionsFullMessage.class,
             new MessageHandler<GridDhtPartitionsFullMessage>() {
                 @Override public void onMessage(ClusterNode node, GridDhtPartitionsFullMessage msg) {
                     processFullPartitionUpdate(node, msg);
                 }
             });
 
-        cctx.io().addHandler(0, GridDhtPartitionsSingleRequest.class,
+        cctx.io().addCacheHandler(0, GridDhtPartitionsSingleRequest.class,
             new MessageHandler<GridDhtPartitionsSingleRequest>() {
                 @Override public void onMessage(ClusterNode node, GridDhtPartitionsSingleRequest msg) {
                     processSinglePartitionRequest(node, msg);
@@ -383,24 +382,28 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
             for (int cnt = 0; cnt < cctx.gridConfig().getRebalanceThreadPoolSize(); cnt++) {
                 final int idx = cnt;
 
-                cctx.io().addOrderedHandler(rebalanceTopic(cnt), new CI2<UUID, GridCacheMessage>() {
-                    @Override public void apply(final UUID id, final GridCacheMessage m) {
+                cctx.io().addOrderedCacheGroupHandler(rebalanceTopic(cnt), new CI2<UUID, GridCacheGroupIdMessage>() {
+                    @Override public void apply(final UUID id, final GridCacheGroupIdMessage m) {
                         if (!enterBusy())
                             return;
 
                         try {
-                            GridCacheContext cacheCtx = cctx.cacheContext(m.cacheId);
-
-                            if (cacheCtx != null) {
-                                if (m instanceof GridDhtPartitionSupplyMessage)
-                                    cacheCtx.preloader().handleSupplyMessage(
-                                        idx, id, (GridDhtPartitionSupplyMessage)m);
-                                else if (m instanceof GridDhtPartitionDemandMessage)
-                                    cacheCtx.preloader().handleDemandMessage(
-                                        idx, id, (GridDhtPartitionDemandMessage)m);
-                                else
-                                    U.error(log, "Unsupported message type: " + m.getClass().getName());
+                            CacheGroupContext grp = cctx.cache().cacheGroup(m.groupId());
+
+                            if (grp != null) {
+                                if (m instanceof GridDhtPartitionSupplyMessage) {
+                                    grp.preloader().handleSupplyMessage(idx, id, (GridDhtPartitionSupplyMessage) m);
+
+                                    return;
+                                }
+                                else if (m instanceof GridDhtPartitionDemandMessage) {
+                                    grp.preloader().handleDemandMessage(idx, id, (GridDhtPartitionDemandMessage) m);
+
+                                    return;
+                                }
                             }
+
+                            U.error(log, "Unsupported message type: " + m.getClass().getName());
                         }
                         finally {
                             leaveBusy();
@@ -418,14 +421,14 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                     try {
                         fut.get();
 
-                        for (GridCacheContext cacheCtx : cctx.cacheContexts())
-                            cacheCtx.preloader().onInitialExchangeComplete(null);
+                        for (CacheGroupContext grp : cctx.cache().cacheGroups())
+                            grp.preloader().onInitialExchangeComplete(null);
 
                         reconnectExchangeFut.onDone();
                     }
                     catch (IgniteCheckedException e) {
-                        for (GridCacheContext cacheCtx : cctx.cacheContexts())
-                            cacheCtx.preloader().onInitialExchangeComplete(e);
+                        for (CacheGroupContext grp : cctx.cache().cacheGroups())
+                            grp.preloader().onInitialExchangeComplete(e);
 
                         reconnectExchangeFut.onDone(e);
                     }
@@ -470,9 +473,9 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
             AffinityTopologyVersion nodeStartVer = new AffinityTopologyVersion(discoEvt.topologyVersion(), 0);
 
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (nodeStartVer.equals(cacheCtx.startTopologyVersion()))
-                    cacheCtx.preloader().onInitialExchangeComplete(null);
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (nodeStartVer.equals(grp.localStartVersion()))
+                    grp.preloader().onInitialExchangeComplete(null);
             }
 
             if (log.isDebugEnabled())
@@ -492,9 +495,9 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
     @Override protected void onKernalStop0(boolean cancel) {
         cctx.gridEvents().removeDiscoveryEventListener(discoLsnr);
 
-        cctx.io().removeHandler(0, GridDhtPartitionsSingleMessage.class);
-        cctx.io().removeHandler(0, GridDhtPartitionsFullMessage.class);
-        cctx.io().removeHandler(0, GridDhtPartitionsSingleRequest.class);
+        cctx.io().removeHandler(false, 0, GridDhtPartitionsSingleMessage.class);
+        cctx.io().removeHandler(false, 0, GridDhtPartitionsFullMessage.class);
+        cctx.io().removeHandler(false, 0, GridDhtPartitionsSingleRequest.class);
 
         stopErr = cctx.kernalContext().clientDisconnected() ?
             new IgniteClientDisconnectedCheckedException(cctx.kernalContext().cluster().clientReconnectFuture(),
@@ -514,7 +517,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
         if (!cctx.kernalContext().clientNode()) {
             for (int cnt = 0; cnt < cctx.gridConfig().getRebalanceThreadPoolSize(); cnt++)
-                cctx.io().removeOrderedHandler(rebalanceTopic(cnt));
+                cctx.io().removeOrderedHandler(true, rebalanceTopic(cnt));
         }
 
         U.cancel(exchWorker);
@@ -549,22 +552,22 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param exchFut Exchange future.
      * @return Topology.
      */
-    public GridDhtPartitionTopology clientTopology(int cacheId, GridDhtPartitionsExchangeFuture exchFut) {
-        GridClientPartitionTopology top = clientTops.get(cacheId);
+    public GridDhtPartitionTopology clientTopology(int grpId, GridDhtPartitionsExchangeFuture exchFut) {
+        GridClientPartitionTopology top = clientTops.get(grpId);
 
         if (top != null)
             return top;
 
         Object affKey = null;
 
-        DynamicCacheDescriptor desc = cctx.cache().cacheDescriptor(cacheId);
+        CacheGroupDescriptor grpDesc = cctx.cache().cacheGroupDescriptors().get(grpId);
 
-        if (desc != null) {
-            CacheConfiguration ccfg = desc.cacheConfiguration();
+        if (grpDesc != null) {
+            CacheConfiguration<?, ?> ccfg = grpDesc.config();
 
             AffinityFunction aff = ccfg.getAffinity();
 
@@ -574,8 +577,8 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                 aff.partitions());
         }
 
-        GridClientPartitionTopology old = clientTops.putIfAbsent(cacheId,
-            top = new GridClientPartitionTopology(cctx, cacheId, exchFut, affKey));
+        GridClientPartitionTopology old = clientTops.putIfAbsent(grpId,
+            top = new GridClientPartitionTopology(cctx, grpId, exchFut, affKey));
 
         return old != null ? old : top;
     }
@@ -588,11 +591,11 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
     }
 
     /**
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @return Client partition topology.
      */
-    public GridClientPartitionTopology clearClientTopology(int cacheId) {
-        return clientTops.remove(cacheId);
+    public GridClientPartitionTopology clearClientTopology(int grpId) {
+        return clientTops.remove(grpId);
     }
 
     /**
@@ -806,18 +809,12 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
         // If this is the oldest node.
         if (oldest.id().equals(cctx.localNodeId())) {
             // Check rebalance state & send CacheAffinityChangeMessage if need.
-            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                if (!cacheCtx.isLocal()) {
-                    if (cacheCtx == null)
-                        continue;
-
-                    GridDhtPartitionTopology top = null;
-
-                    if (!cacheCtx.isLocal())
-                        top = cacheCtx.topology();
+            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                if (!grp.isLocal()) {
+                    GridDhtPartitionTopology top = grp.topology();
 
                     if (top != null)
-                        cctx.affinity().checkRebalanceState(top, cacheCtx.cacheId());
+                        cctx.affinity().checkRebalanceState(top, grp.groupId());
                 }
             }
 
@@ -827,7 +824,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
             AffinityTopologyVersion rmtTopVer =
                 lastFut != null ? lastFut.topologyVersion() : AffinityTopologyVersion.NONE;
 
-            Collection<ClusterNode> rmts = CU.remoteNodes(cctx, AffinityTopologyVersion.NONE);
+            Collection<ClusterNode> rmts = CU.remoteNodes(cctx, rmtTopVer);
 
             if (log.isDebugEnabled())
                 log.debug("Refreshing partitions from oldest node: " + cctx.localNodeId());
@@ -845,9 +842,8 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
     /**
      * @param nodes Nodes.
-     * @return {@code True} if message was sent, {@code false} if node left grid.
      */
-    private boolean sendAllPartitions(Collection<ClusterNode> nodes) {
+    private void sendAllPartitions(Collection<ClusterNode> nodes) {
         GridDhtPartitionsFullMessage m = createPartitionsFullMessage(nodes, null, null, null, null, true);
 
         if (log.isDebugEnabled())
@@ -868,8 +864,6 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                 U.warn(log, "Failed to send partitions full message [node=" + node + ", err=" + e + ']');
             }
         }
-
-        return true;
     }
 
     /**
@@ -896,51 +890,48 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
         final Map<Object, T2<Integer, GridDhtPartitionFullMap>> dupData = new HashMap<>();
 
-        cctx.forAllCaches(new IgniteInClosure<GridCacheContext>() {
-            @Override public void apply(GridCacheContext cacheCtx) {
-                if (!cacheCtx.isLocal()) {
-                    boolean ready;
-
-                    if (exchId != null) {
-                        AffinityTopologyVersion startTopVer = cacheCtx.startTopologyVersion();
-
-                        ready = startTopVer.compareTo(exchId.topologyVersion()) <= 0;
-                    }
-                    else
-                        ready = cacheCtx.started();
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (!grp.isLocal()) {
+                if (exchId != null) {
+                    AffinityTopologyVersion startTopVer = grp.localStartVersion();
 
-                    if (ready) {
-                        GridAffinityAssignmentCache affCache = cacheCtx.affinity().affinityCache();
+                    if (startTopVer.compareTo(exchId.topologyVersion()) > 0)
+                        continue;
+                }
 
-                        GridDhtPartitionFullMap locMap = cacheCtx.topology().partitionMap(true);
+                GridAffinityAssignmentCache affCache = grp.affinity();
 
-                        addFullPartitionsMap(m,
-                            dupData,
-                            compress,
-                            cacheCtx.cacheId(),
-                            locMap,
-                            affCache.similarAffinityKey());
+                GridDhtPartitionFullMap locMap = grp.topology().partitionMap(true);
 
-                        if (exchId != null)
-                            m.addPartitionUpdateCounters(cacheCtx.cacheId(), cacheCtx.topology().updateCounters(true));
-                    }
+                if (locMap != null) {
+                    addFullPartitionsMap(m,
+                        dupData,
+                        compress,
+                        grp.groupId(),
+                        locMap,
+                        affCache.similarAffinityKey());
                 }
+
+                if (exchId != null)
+                    m.addPartitionUpdateCounters(grp.groupId(), grp.topology().updateCounters(true));
             }
-        });
+        }
 
         // It is important that client topologies be added after contexts.
         for (GridClientPartitionTopology top : cctx.exchange().clientTopologies()) {
             GridDhtPartitionFullMap map = top.partitionMap(true);
 
-            addFullPartitionsMap(m,
-                dupData,
-                compress,
-                top.cacheId(),
-                map,
-                top.similarAffinityKey());
+            if (map != null) {
+                addFullPartitionsMap(m,
+                    dupData,
+                    compress,
+                    top.groupId(),
+                    map,
+                    top.similarAffinityKey());
+            }
 
             if (exchId != null)
-                m.addPartitionUpdateCounters(top.cacheId(), top.updateCounters(true));
+                m.addPartitionUpdateCounters(top.groupId(), top.updateCounters(true));
         }
 
         return m;
@@ -950,19 +941,21 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
      * @param m Message.
      * @param dupData Duplicated data map.
      * @param compress {@code True} if need check for duplicated partition state data.
-     * @param cacheId Cache ID.
+     * @param grpId Cache group ID.
      * @param map Map to add.
      * @param affKey Cache affinity key.
      */
     private void addFullPartitionsMap(GridDhtPartitionsFullMessage m,
         Map<Object, T2<Integer, GridDhtPartitionFullMap>> dupData,
         boolean compress,
-        Integer cacheId,
+        Integer grpId,
         GridDhtPartitionFullMap map,
         Object affKey) {
+        assert map != null;
+
         Integer dupDataCache = null;
 
-        if (compress && affKey != null && !m.containsCache(cacheId)) {
+        if (compress && affKey != null && !m.containsGroup(grpId)) {
             T2<Integer, GridDhtPartitionFullMap> state0 = dupData.get(affKey);
 
             if (state0 != null && state0.get2().partitionStateEquals(map)) {
@@ -978,10 +971,10 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                 dupDataCache = state0.get1();
             }
             else
-                dupData.put(affKey, new T2<>(cacheId, map));
+                dupData.put(affKey, new T2<>(grpId, map));
         }
 
-        m.addFullPartitionsMap(cacheId, map, dupDataCache);
+        m.addFullPartitionsMap(grpId, map, dupDataCache);
     }
 
     /**
@@ -1029,24 +1022,24 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
         Map<Object, T2<Integer,Map<Integer, GridDhtPartitionState>>> dupData = new HashMap<>();
 
-        for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-            if (!cacheCtx.isLocal()) {
-                GridDhtPartitionMap locMap = cacheCtx.topology().localPartitionMap();
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (!grp.isLocal()) {
+                GridDhtPartitionMap locMap = grp.topology().localPartitionMap();
 
                 addPartitionMap(m,
                     dupData,
                     true,
-                    cacheCtx.cacheId(),
+                    grp.groupId(),
                     locMap,
-                    cacheCtx.affinity().affinityCache().similarAffinityKey());
+                    grp.affinity().similarAffinityKey());
 
                 if (sndCounters)
-                    m.partitionUpdateCounters(cacheCtx.cacheId(), cacheCtx.topology().updateCounters(true));
+                    m.partitionUpdateCounters(grp.groupId(), grp.topology().updateCounters(true));
             }
         }
 
         for (GridClientPartitionTopology top : clientTops.values()) {
-            if (m.partitions() != null && m.partitions().containsKey(top.cacheId()))
+            if (m.partitions() != null && m.partitions().containsKey(top.groupId()))
                 continue;
 
             GridDhtPartitionMap locMap = top.localPartitionMap();
@@ -1054,12 +1047,12 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
             addPartitionMap(m,
                 dupData,
                 true,
-                top.cacheId(),
+                top.groupId(),
                 locMap,
                 top.similarAffinityKey());
 
             if (sndCounters)
-                m.partitionUpdateCounters(top.cacheId(), top.updateCounters(true));
+                m.partitionUpdateCounters(top.groupId(), top.updateCounters(true));
         }
 
         return m;
@@ -1257,22 +1250,19 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                 boolean updated = false;
 
                 for (Map.Entry<Integer, GridDhtPartitionFullMap> entry : msg.partitions().entrySet()) {
-                    Integer cacheId = entry.getKey();
-
-                    GridCacheContext<K, V> cacheCtx = cctx.cacheContext(cacheId);
+                    Integer grpId = entry.getKey();
 
-                    if (cacheCtx != null && !cacheCtx.started())
-                        continue; // Can safely ignore background exchange.
+                    CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
                     GridDhtPartitionTopology top = null;
 
-                    if (cacheCtx == null)
-                        top = clientTops.get(cacheId);
-                    else if (!cacheCtx.isLocal())
-                        top = cacheCtx.topology();
+                    if (grp == null)
+                        top = clientTops.get(grpId);
+                    else if (!grp.isLocal())
+                        top = grp.topology();
 
                     if (top != null)
-                        updated |= top.update(null, entry.getValue(), null, msg.partsToReload(cctx.localNodeId(), cacheId)) != null;
+                        updated |= top.update(null, entry.getValue(), null, msg.partsToReload(cctx.localNodeId(), grpId)) != null;
                 }
 
                 if (!cctx.kernalContext().clientNode() && updated)
@@ -1280,9 +1270,10 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
                 boolean hasMovingParts = false;
 
-                for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                    if (!cacheCtx.isLocal() && cacheCtx.started() && cacheCtx.topology().hasMovingPartitions()) {
+                for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                    if (!grp.isLocal() && grp.topology().hasMovingPartitions()) {
                         hasMovingParts = true;
+
                         break;
                     }
                 }
@@ -1315,25 +1306,25 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                 boolean updated = false;
 
                 for (Map.Entry<Integer, GridDhtPartitionMap> entry : msg.partitions().entrySet()) {
-                    Integer cacheId = entry.getKey();
+                    Integer grpId = entry.getKey();
 
-                    GridCacheContext<K, V> cacheCtx = cctx.cacheContext(cacheId);
+                    CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-                    if (cacheCtx != null &&
-                        cacheCtx.startTopologyVersion().compareTo(entry.getValue().topologyVersion()) > 0)
+                    if (grp != null &&
+                        grp.localStartVersion().compareTo(entry.getValue().topologyVersion()) > 0)
                         continue;
 
                     GridDhtPartitionTopology top = null;
 
-                    if (cacheCtx == null)
-                        top = clientTops.get(cacheId);
-                    else if (!cacheCtx.isLocal())
-                        top = cacheCtx.topology();
+                    if (grp == null)
+                        top = clientTops.get(grpId);
+                    else if (!grp.isLocal())
+                        top = grp.topology();
 
                     if (top != null) {
                         updated |= top.update(null, entry.getValue()) != null;
 
-                        cctx.affinity().checkRebalanceState(top, cacheId);
+                        cctx.affinity().checkRebalanceState(top, grpId);
                     }
                 }
 
@@ -1422,8 +1413,8 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
         dumpPendingObjects(exchTopVer);
 
-        for (GridCacheContext cacheCtx : cctx.cacheContexts())
-            cacheCtx.preloader().dumpDebugInfo();
+        for (CacheGroupContext grp : cctx.cache().cacheGroups())
+            grp.preloader().dumpDebugInfo();
 
         cctx.affinity().dumpDebugInfo();
 
@@ -1586,21 +1577,19 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
             }
         }
 
-        for (GridCacheContext ctx : cctx.cacheContexts()) {
-            if (ctx.isLocal())
+        for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+            if (grp.isLocal())
                 continue;
 
-            GridCacheContext ctx0 = ctx.isNear() ? ctx.near().dht().context() : ctx;
-
-            GridCachePreloader preloader = ctx0.preloader();
+            GridCachePreloader preloader = grp.preloader();
 
             if (preloader != null)
                 preloader.dumpDebugInfo();
 
-            GridCacheAffinityManager affMgr = ctx0.affinity();
+            GridAffinityAssignmentCache aff = grp.affinity();
 
-            if (affMgr != null)
-                affMgr.dumpDebugInfo();
+            if (aff != null)
+                aff.dumpDebugInfo();
         }
     }
 
@@ -1743,8 +1732,11 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                 try {
                     boolean preloadFinished = true;
 
-                    for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                        preloadFinished &= cacheCtx.preloader() != null && cacheCtx.preloader().syncFuture().isDone();
+                    for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                        if (grp.isLocal())
+                            continue;
+
+                        preloadFinished &= grp.preloader() != null && grp.preloader().syncFuture().isDone();
 
                         if (!preloadFinished)
                             break;
@@ -1851,11 +1843,11 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
                             boolean changed = false;
 
-                            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                                if (cacheCtx.isLocal())
+                            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                                if (grp.isLocal())
                                     continue;
 
-                                changed |= cacheCtx.topology().afterExchange(exchFut);
+                                changed |= grp.topology().afterExchange(exchFut);
                             }
 
                             if (!cctx.kernalContext().clientNode() && changed && !hasPendingExchange())
@@ -1875,16 +1867,16 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                         if (!exchFut.skipPreload() && cctx.kernalContext().state().active()) {
                             assignsMap = new HashMap<>();
 
-                            for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
-                                long delay = cacheCtx.config().getRebalanceDelay();
+                            for (CacheGroupContext grp : cctx.cache().cacheGroups()) {
+                                long delay = grp.config().getRebalanceDelay();
 
                                 GridDhtPreloaderAssignments assigns = null;
 
                                 // Don't delay for dummy reassigns to avoid infinite recursion.
                                 if (delay == 0 || forcePreload)
-                                    assigns = cacheCtx.preloader().assign(exchFut);
+                                    assigns = grp.preloader().assign(exchFut);
 
-                                assignsMap.put(cacheCtx.cacheId(), assigns);
+                                assignsMap.put(grp.groupId(), assigns);
                             }
                         }
                     }
@@ -1899,16 +1891,16 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                         NavigableMap<Integer, List<Integer>> orderMap = new TreeMap<>();
 
                         for (Map.Entry<Integer, GridDhtPreloaderAssignments> e : assignsMap.entrySet()) {
-                            int cacheId = e.getKey();
+                            int grpId = e.getKey();
 
-                            GridCacheContext<K, V> cacheCtx = cctx.cacheContext(cacheId);
+                            CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-                            int order = cacheCtx.config().getRebalanceOrder();
+                            int order = grp.config().getRebalanceOrder();
 
                             if (orderMap.get(order) == null)
                                 orderMap.put(order, new ArrayList<Integer>(size));
 
-                            orderMap.get(order).add(cacheId);
+                            orderMap.get(order).add(grpId);
                         }
 
                         Runnable r = null;
@@ -1918,35 +1910,27 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                         boolean assignsCancelled = false;
 
                         for (Integer order : orderMap.descendingKeySet()) {
-                            for (Integer cacheId : orderMap.get(order)) {
-                                GridCacheContext<K, V> cacheCtx = cctx.cacheContext(cacheId);
+                            for (Integer grpId : orderMap.get(order)) {
+                                CacheGroupContext grp = cctx.cache().cacheGroup(grpId);
 
-                                GridDhtPreloaderAssignments assigns = assignsMap.get(cacheId);
+                                GridDhtPreloaderAssignments assigns = assignsMap.get(grpId);
 
                                 if (assigns != null)
                                     assignsCancelled |= assigns.cancelled();
 
-                                List<String> waitList = new ArrayList<>(size - 1);
-
-                                for (List<Integer> cIds : orderMap.headMap(order).values()) {
-                                    for (Integer cId : cIds)
-                                        waitList.add(cctx.cacheContext(cId).name());
-                                }
-
                                 // Cancels previous rebalance future (in case it's not done yet).
                                 // Sends previous rebalance stopped event (if necessary).
                                 // Creates new rebalance future.
                                 // Sends current rebalance started event (if necessary).
                                 // Finishes cache sync future (on empty assignments).
-                                Runnable cur = cacheCtx.preloader().addAssignments(assigns,
+                                Runnable cur = grp.preloader().addAssignments(assigns,
                                     forcePreload,
-                                    waitList,
                                     cnt,
                                     r,
                                     exchFut.forcedRebalanceFuture());
 
                                 if (cur != null) {
-                                    rebList.add(U.maskName(cacheCtx.name()));
+                                    rebList.add(grp.cacheOrGroupName());
 
                                     r = cur;
                                 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloader.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloader.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloader.java
index 5ae68e8..0ac0272 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloader.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloader.java
@@ -22,6 +22,7 @@ import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtFuture;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicAbstractUpdateRequest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionDemandMessage;
@@ -45,18 +46,6 @@ public interface GridCachePreloader {
     public void start() throws IgniteCheckedException;
 
     /**
-     * Stops preloading.
-     */
-    public void stop();
-
-    /**
-     * Kernal start callback.
-     *
-     * @throws IgniteCheckedException If failed.
-     */
-    public void onKernalStart() throws IgniteCheckedException;
-
-    /**
      * Kernal stop callback.
      */
     public void onKernalStop();
@@ -90,7 +79,6 @@ public interface GridCachePreloader {
      */
     public Runnable addAssignments(GridDhtPreloaderAssignments assignments,
         boolean forcePreload,
-        Collection<String> caches,
         int cnt,
         Runnable next,
         @Nullable GridFutureAdapter<Boolean> forcedRebFut);
@@ -136,20 +124,25 @@ public interface GridCachePreloader {
     /**
      * Requests that preloader sends the request for the key.
      *
+     * @param cctx Cache context.
      * @param keys Keys to request.
      * @param topVer Topology version, {@code -1} if not required.
      * @return Future to complete when all keys are preloaded.
      */
-    public IgniteInternalFuture<Object> request(Collection<KeyCacheObject> keys, AffinityTopologyVersion topVer);
+    public GridDhtFuture<Object> request(GridCacheContext cctx,
+        Collection<KeyCacheObject> keys,
+        AffinityTopologyVersion topVer);
 
     /**
      * Requests that preloader sends the request for the key.
      *
+     * @param cctx Cache context.
      * @param req Message with keys to request.
      * @param topVer Topology version, {@code -1} if not required.
      * @return Future to complete when all keys are preloaded.
      */
-    public IgniteInternalFuture<Object> request(GridNearAtomicAbstractUpdateRequest req,
+    public GridDhtFuture<Object> request(GridCacheContext cctx,
+        GridNearAtomicAbstractUpdateRequest req,
         AffinityTopologyVersion topVer);
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloaderAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloaderAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloaderAdapter.java
index 47c37f5..98874e4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloaderAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePreloaderAdapter.java
@@ -21,9 +21,9 @@ import java.util.Collection;
 import java.util.UUID;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
-import org.apache.ignite.cache.affinity.AffinityFunction;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtFuture;
 import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridNearAtomicAbstractUpdateRequest;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionDemandMessage;
@@ -39,15 +39,15 @@ import org.jetbrains.annotations.Nullable;
  * Adapter for preloading which always assumes that preloading finished.
  */
 public class GridCachePreloaderAdapter implements GridCachePreloader {
-    /** Cache context. */
-    protected final GridCacheContext<?, ?> cctx;
+    /** */
+    protected final CacheGroupContext grp;
+
+    /** */
+    protected final GridCacheSharedContext ctx;
 
     /** Logger. */
     protected final IgniteLogger log;
 
-    /** Affinity. */
-    protected final AffinityFunction aff;
-
     /** Start future (always completed by default). */
     private final IgniteInternalFuture finFut;
 
@@ -55,15 +55,16 @@ public class GridCachePreloaderAdapter implements GridCachePreloader {
     protected IgnitePredicate<GridCacheEntryInfo> preloadPred;
 
     /**
-     * @param cctx Cache context.
+     * @param grp Cache group.
      */
-    public GridCachePreloaderAdapter(GridCacheContext<?, ?> cctx) {
-        assert cctx != null;
+    public GridCachePreloaderAdapter(CacheGroupContext grp) {
+        assert grp != null;
 
-        this.cctx = cctx;
+        this.grp = grp;
 
-        log = cctx.logger(getClass());
-        aff = cctx.config().getAffinity();
+        ctx = grp.shared();
+
+        log = ctx.logger(getClass());
 
         finFut = new GridFinishedFuture();
     }
@@ -74,16 +75,6 @@ public class GridCachePreloaderAdapter implements GridCachePreloader {
     }
 
     /** {@inheritDoc} */
-    @Override public void stop() {
-        // No-op.
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onKernalStart() throws IgniteCheckedException {
-        // No-op.
-    }
-
-    /** {@inheritDoc} */
     @Override public void onKernalStop() {
         // No-op.
     }
@@ -130,7 +121,7 @@ public class GridCachePreloaderAdapter implements GridCachePreloader {
 
     /** {@inheritDoc} */
     @Override public void unwindUndeploys() {
-        cctx.deploy().unwind(cctx);
+        grp.unwindUndeploys();
     }
 
     /** {@inheritDoc} */
@@ -144,15 +135,15 @@ public class GridCachePreloaderAdapter implements GridCachePreloader {
     }
 
     /** {@inheritDoc} */
-    @Override public IgniteInternalFuture<Object> request(Collection<KeyCacheObject> keys,
+    @Override public GridDhtFuture<Object> request(GridCacheContext ctx, Collection<KeyCacheObject> keys,
         AffinityTopologyVersion topVer) {
-        return new GridFinishedFuture<>();
+        return null;
     }
 
     /** {@inheritDoc} */
-    @Override public IgniteInternalFuture<Object> request(GridNearAtomicAbstractUpdateRequest req,
+    @Override public GridDhtFuture<Object> request(GridCacheContext ctx, GridNearAtomicAbstractUpdateRequest req,
         AffinityTopologyVersion topVer) {
-        return new GridFinishedFuture<>();
+        return null;
     }
 
     /** {@inheritDoc} */
@@ -168,7 +159,6 @@ public class GridCachePreloaderAdapter implements GridCachePreloader {
     /** {@inheritDoc} */
     @Override public Runnable addAssignments(GridDhtPreloaderAssignments assignments,
         boolean forcePreload,
-        Collection<String> caches,
         int cnt,
         Runnable next,
         @Nullable GridFutureAdapter<Boolean> forcedRebFut) {


[04/18] ignite git commit: ignite-5075 'logical' caches sharing the same 'physical' cache group

Posted by sb...@apache.org.
http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePeekModesAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePeekModesAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePeekModesAbstractTest.java
index 81c0799..7725b19 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePeekModesAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCachePeekModesAbstractTest.java
@@ -1022,7 +1022,7 @@ public abstract class IgniteCachePeekModesAbstractTest extends IgniteCacheAbstra
         // Swap and offheap are disabled for near cache.
         IgniteCacheOffheapManager offheapManager = ctx.isNear() ? ctx.near().dht().context().offheap() : ctx.offheap();
         //First count entries...
-        int cnt = (int)offheapManager.entriesCount(part);
+        int cnt = (int)offheapManager.cacheEntriesCount(ctx.cacheId(), part);
 
         GridCacheAffinityManager affinity = ctx.affinity();
         AffinityTopologyVersion topVer = affinity.affinityTopologyVersion();

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheStartTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheStartTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheStartTest.java
index eb8077f..c889c31 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheStartTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheStartTest.java
@@ -18,9 +18,9 @@
 package org.apache.ignite.internal.processors.cache;
 
 import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.IgniteKernal;
 import org.apache.ignite.internal.util.lang.GridAbsPredicate;
 import org.apache.ignite.internal.util.typedef.internal.CU;
@@ -176,8 +176,9 @@ public class IgniteCacheStartTest extends GridCommonAbstractTest {
      * @param idx Node index.
      * @param cacheName Cache name.
      * @param expCache {@code True} if cache should be created.
+     * @throws IgniteCheckedException If failed.
      */
-    private void checkCache(int idx, final String cacheName, final boolean expCache) throws IgniteInterruptedCheckedException {
+    private void checkCache(int idx, final String cacheName, final boolean expCache) throws IgniteCheckedException {
         final IgniteKernal node = (IgniteKernal)ignite(idx);
 
         assertTrue(GridTestUtils.waitForCondition(new GridAbsPredicate() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteOnePhaseCommitInvokeTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteOnePhaseCommitInvokeTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteOnePhaseCommitInvokeTest.java
index a5cb3f2..bccebaa 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteOnePhaseCommitInvokeTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteOnePhaseCommitInvokeTest.java
@@ -127,10 +127,12 @@ public class IgniteOnePhaseCommitInvokeTest extends GridCommonAbstractTest {
 
         final Ignite clientNode = startGrid(1);
 
+        final int grpId = groupIdForCache(srv0, CACHE_NAME);
+
         TestRecordingCommunicationSpi.spi(srv0).blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
             @Override public boolean apply(ClusterNode node, Message msg) {
                 return msg instanceof GridDhtPartitionSupplyMessage &&
-                    ((GridDhtPartitionSupplyMessage)msg).cacheId() == CU.cacheId(CACHE_NAME);
+                    ((GridDhtPartitionSupplyMessage)msg).groupId() == grpId;
             }
         });
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java
index 057b0d6..2e551f9 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTopologyValidatorGridSplitCacheTest.java
@@ -202,6 +202,8 @@ public class IgniteTopologyValidatorGridSplitCacheTest extends GridCommonAbstrac
 
     /**
      * Resolves split by client node join.
+     *
+     * @throws Exception If failed.
      */
     private void resolveSplit() throws Exception {
         startGrid(RESOLVER_GRID_IDX);
@@ -305,6 +307,7 @@ public class IgniteTopologyValidatorGridSplitCacheTest extends GridCommonAbstrac
             return true;
         }
 
+        /** {@inheritDoc} */
         @Override public void start() throws IgniteException {
             if (ignite.cluster().localNode().isClient())
                 return;
@@ -327,12 +330,15 @@ public class IgniteTopologyValidatorGridSplitCacheTest extends GridCommonAbstrac
 
         /**
          * @param node Node.
+         * @return {@code True} if this is marker node.
          */
         private boolean isMarkerNode(ClusterNode node) {
             return node.isClient() && node.attribute(ACTIVATOR_NODE_ATTR) != null;
         }
 
-        @Override public void stop() throws IgniteException {
+        /** {@inheritDoc} */
+        @Override public void stop() {
+            // No-op.
         }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxStoreExceptionAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxStoreExceptionAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxStoreExceptionAbstractSelfTest.java
index 872fe77..863ab38 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxStoreExceptionAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/IgniteTxStoreExceptionAbstractSelfTest.java
@@ -370,7 +370,7 @@ public abstract class IgniteTxStoreExceptionAbstractSelfTest extends GridCacheAb
 
             GridCacheAdapter cache = grid.internalCache(DEFAULT_CACHE_NAME);
 
-            GridCacheMapEntry entry = cache.map().getEntry(cache.context().toCacheKeyObject(key));
+            GridCacheMapEntry entry = cache.map().getEntry(cache.context(), cache.context().toCacheKeyObject(key));
 
             log.info("Entry: " + entry);
 
@@ -383,7 +383,7 @@ public abstract class IgniteTxStoreExceptionAbstractSelfTest extends GridCacheAb
             }
 
             if (cache.isNear()) {
-                entry = ((GridNearCacheAdapter)cache).dht().map().getEntry(cache.context().toCacheKeyObject(key));
+                entry = ((GridNearCacheAdapter)cache).dht().map().getEntry(cache.context(), cache.context().toCacheKeyObject(key));
 
                 log.info("Dht entry: " + entry);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java
index 06d62e4..5dace92 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/binary/GridCacheBinaryObjectsAbstractSelfTest.java
@@ -175,7 +175,7 @@ public abstract class GridCacheBinaryObjectsAbstractSelfTest extends GridCommonA
         for (int i = 0; i < gridCount(); i++) {
             GridCacheAdapter<Object, Object> c = ((IgniteKernal)grid(i)).internalCache(DEFAULT_CACHE_NAME);
 
-            for (GridCacheEntryEx e : c.map().entries()) {
+            for (GridCacheEntryEx e : c.map().entries(c.context().cacheId())) {
                 Object key = e.key().value(c.context().cacheObjectContext(), false);
                 Object val = CU.value(e.rawGet(), c.context(), false);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheQueueCleanupSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheQueueCleanupSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheQueueCleanupSelfTest.java
index 654e729..75183b0 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheQueueCleanupSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheQueueCleanupSelfTest.java
@@ -21,9 +21,11 @@ import java.util.Iterator;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteQueue;
 import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CollectionConfiguration;
 import org.apache.ignite.internal.IgniteInternalFuture;
@@ -177,20 +179,15 @@ public class GridCacheQueueCleanupSelfTest extends IgniteCollectionAbstractTest
         // Check that items of removed queue are removed, items of new queue not.
         assertTrue(GridTestUtils.waitForCondition(new PAX() {
             @SuppressWarnings("WhileLoopReplaceableByForEach")
-            @Override public boolean applyx() {
+            @Override public boolean applyx() throws IgniteCheckedException {
                 int cnt = 0;
 
                 for (int i = 0; i < gridCount(); i++) {
                     GridCacheAdapter<Object, Object> cache =
-                        ((IgniteKernal)grid(i)).context().cache().internalCache(queueCacheName);
-
-                    Iterator<GridCacheMapEntry> entries = cache.map().entries().iterator();
+                        grid(i).context().cache().internalCache(queueCacheName);
 
-                    while (entries.hasNext()) {
+                    for (Object e : cache.localEntries(new CachePeekMode[]{CachePeekMode.ALL}))
                         cnt++;
-
-                        entries.next();
-                    }
                 }
 
                 if (cnt > 501) { // 500 items + header.

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetAbstractSelfTest.java
index 517a7ad..53c1eb7 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetAbstractSelfTest.java
@@ -34,14 +34,14 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteSet;
 import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.CachePeekMode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.CollectionConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
-import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
 import org.apache.ignite.internal.util.lang.GridAbsPredicate;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -804,17 +804,12 @@ public abstract class GridCacheSetAbstractSelfTest extends IgniteCollectionAbstr
         GridCacheContext cctx = GridTestUtils.getFieldValue(set0, "cctx");
 
         for (int i = 0; i < gridCount(); i++) {
-            Iterator<GridCacheMapEntry> entries =
-                (grid(i)).context().cache().internalCache(cctx.name()).map().entries().iterator();
+            GridCacheAdapter cache = grid(i).context().cache().internalCache(cctx.name());
 
-            while (entries.hasNext()) {
-                GridCacheEntryEx entry = entries.next();
+            for (Object e : cache.localEntries(new CachePeekMode[]{CachePeekMode.ALL})) {
+                cnt++;
 
-                if (entry.hasValue()) {
-                    cnt++;
-
-                    log.info("Unexpected entry: " + entry);
-                }
+                log.info("Unexpected entry: " + e);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetFailoverAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetFailoverAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetFailoverAbstractSelfTest.java
index 1e11c06..f8af2a2 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetFailoverAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/GridCacheSetFailoverAbstractSelfTest.java
@@ -29,6 +29,7 @@ import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteSet;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheEntryEx;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.internal.processors.datastructures.SetItemKey;
@@ -174,8 +175,9 @@ public abstract class GridCacheSetFailoverAbstractSelfTest extends IgniteCollect
             Set<IgniteUuid> setIds = new HashSet<>();
 
             for (int i = 0; i < gridCount(); i++) {
-                Iterator<GridCacheMapEntry> entries =
-                    grid(i).context().cache().internalCache(DEFAULT_CACHE_NAME).map().entries().iterator();
+                GridCacheAdapter cache = grid(i).context().cache().internalCache(DEFAULT_CACHE_NAME);
+
+                Iterator<GridCacheMapEntry> entries = cache.map().entries(cache.context().cacheId()).iterator();
 
                 while (entries.hasNext()) {
                     GridCacheEntryEx entry = entries.next();

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedQueueNoBackupsTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedQueueNoBackupsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedQueueNoBackupsTest.java
index 67b7f8f..aa075c0 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedQueueNoBackupsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedQueueNoBackupsTest.java
@@ -24,6 +24,7 @@ import org.apache.ignite.cache.CacheAtomicityMode;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.configuration.CollectionConfiguration;
 import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.testframework.GridTestUtils;
@@ -72,8 +73,9 @@ public class IgnitePartitionedQueueNoBackupsTest extends GridCachePartitionedQue
         for (int i = 0; i < gridCount(); i++) {
             IgniteKernal grid = (IgniteKernal)grid(i);
 
-            Iterator<GridCacheMapEntry> entries =
-                grid.context().cache().internalCache(cctx.name()).map().entries().iterator();
+            GridCacheAdapter cache = grid.context().cache().internalCache(cctx.name());
+
+            Iterator<GridCacheMapEntry> entries = cache.map().entries(cache.context().cacheId()).iterator();
 
             if (entries.hasNext()) {
                 if (setNodeId == null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedSetNoBackupsSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedSetNoBackupsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedSetNoBackupsSelfTest.java
index a73aa4a..4daaeca 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedSetNoBackupsSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/datastructures/partitioned/IgnitePartitionedSetNoBackupsSelfTest.java
@@ -22,6 +22,7 @@ import java.util.Set;
 import java.util.UUID;
 import org.apache.ignite.configuration.CollectionConfiguration;
 import org.apache.ignite.internal.IgniteKernal;
+import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheMapEntry;
 import org.apache.ignite.testframework.GridTestUtils;
@@ -57,8 +58,9 @@ public class IgnitePartitionedSetNoBackupsSelfTest extends GridCachePartitionedS
         for (int i = 0; i < gridCount(); i++) {
             IgniteKernal grid = (IgniteKernal)grid(i);
 
-            Iterator<GridCacheMapEntry> entries =
-                grid.context().cache().internalCache(cctx.name()).map().entries().iterator();
+            GridCacheAdapter cache  = grid.context().cache().internalCache(cctx.name());
+
+            Iterator<GridCacheMapEntry> entries = cache.map().entries(cache.context().cacheId()).iterator();
 
             if (entries.hasNext()) {
                 if (setNodeId == null)

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheDiscoveryDataConcurrentJoinTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheDiscoveryDataConcurrentJoinTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheDiscoveryDataConcurrentJoinTest.java
index a80830a..4cf89b2 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheDiscoveryDataConcurrentJoinTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheDiscoveryDataConcurrentJoinTest.java
@@ -57,6 +57,9 @@ public class CacheDiscoveryDataConcurrentJoinTest extends GridCommonAbstractTest
     /** */
     private ThreadLocal<Integer> staticCaches = new ThreadLocal<>();
 
+    /** */
+    private boolean withCacheGrp;
+
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
         IgniteConfiguration cfg = super.getConfiguration(gridName);
@@ -158,11 +161,22 @@ public class CacheDiscoveryDataConcurrentJoinTest extends GridCommonAbstractTest
                 }
             }
 
+            checkCacheDiscoveryDataConsistent();
+
             stopAllGrids();
         }
     }
 
     /**
+     * @throws Exception If failed.
+     */
+    public void testConcurrentJoinCacheWithGroup() throws Exception {
+        withCacheGrp = true;
+
+        testConcurrentJoin();
+    }
+
+    /**
      * @param caches Number of caches.
      * @return Cache configurations.
      */
@@ -186,6 +200,9 @@ public class CacheDiscoveryDataConcurrentJoinTest extends GridCommonAbstractTest
         ccfg.setAtomicityMode(TRANSACTIONAL);
         ccfg.setAffinity(new RendezvousAffinityFunction(false, 16));
 
+        if (withCacheGrp)
+            ccfg.setGroupName("group1");
+
         return ccfg;
     }
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheGroupsPreloadTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheGroupsPreloadTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheGroupsPreloadTest.java
new file mode 100644
index 0000000..8859638
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheGroupsPreloadTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.distributed;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ *
+ */
+public class CacheGroupsPreloadTest extends GridCommonAbstractTest {
+    /** */
+    private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);
+
+    /** */
+    private static final String CACHE1 = "cache1";
+
+    /** */
+    private static final String CACHE2 = "cache2";
+
+    /** */
+    private static final String GROUP1 = "group1";
+
+    /** */
+    private static final String GROUP2 = "group2";
+
+    /** */
+    private CacheAtomicityMode atomicityMode = CacheAtomicityMode.ATOMIC;
+
+    /** */
+    private CacheMode cacheMode = CacheMode.PARTITIONED;
+
+    /** */
+    private boolean sameGrp = true;
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
+
+        CacheConfiguration cfg1 = defaultCacheConfiguration()
+            .setName(CACHE1)
+            .setGroupName(GROUP1)
+            .setCacheMode(cacheMode)
+            .setAtomicityMode(atomicityMode)
+            .setBackups(1);
+
+        CacheConfiguration cfg2 = new CacheConfiguration(cfg1)
+            .setName(CACHE2);
+
+        if (!sameGrp)
+            cfg2.setGroupName(GROUP2);
+
+        cfg.setCacheConfiguration(cfg1, cfg2);
+
+        return cfg;
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload1() throws Exception {
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload2() throws Exception {
+        atomicityMode = CacheAtomicityMode.TRANSACTIONAL;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload3() throws Exception {
+        cacheMode = CacheMode.REPLICATED;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload4() throws Exception {
+        cacheMode = CacheMode.REPLICATED;
+        atomicityMode = CacheAtomicityMode.TRANSACTIONAL;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload5() throws Exception {
+        sameGrp = false;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload6() throws Exception {
+        sameGrp = false;
+        atomicityMode = CacheAtomicityMode.TRANSACTIONAL;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload7() throws Exception {
+        sameGrp = false;
+        cacheMode = CacheMode.REPLICATED;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testCachePreload8() throws Exception {
+        sameGrp = false;
+        cacheMode = CacheMode.REPLICATED;
+        atomicityMode = CacheAtomicityMode.TRANSACTIONAL;
+
+        cachePreloadTest();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    private void cachePreloadTest() throws Exception {
+        IgniteCache<Object, Object> cache = startGrid(0).cache(CACHE1);
+
+        for (int i = 0; i < 1000; i++)
+            cache.put(i, CACHE1 + "-" + i);
+
+        cache = startGrid(1).cache(CACHE1);
+
+        for (int i = 0; i < 1000; i++)
+            assertEquals(CACHE1 + "-" + i, cache.get(i));
+
+        cache = ignite(1).cache(CACHE2);
+
+        for (int i = 0; i < 1000; i++)
+            cache.put(i, CACHE2 + "-" + i);
+
+        cache = startGrid(2).cache(CACHE1);
+
+        for (int i = 0; i < 1000; i++)
+            assertEquals(CACHE1 + "-" + i, cache.get(i));
+
+        cache = ignite(2).cache(CACHE2);
+
+        for (int i = 0; i < 1000; i++)
+            assertEquals(CACHE2 + "-" + i, cache.get(i));
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java
index bc435e2..20cef30 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CacheLateAffinityAssignmentTest.java
@@ -2008,12 +2008,14 @@ public class CacheLateAffinityAssignmentTest extends GridCommonAbstractTest {
      * @param cacheName Cache name.
      */
     private void blockSupplySend(TestRecordingCommunicationSpi spi, final String cacheName) {
+        final int grpId = groupIdForCache(spi.ignite(), cacheName);
+
         spi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
             @Override public boolean apply(ClusterNode node, Message msg) {
                 if (!msg.getClass().equals(GridDhtPartitionSupplyMessage.class))
                     return false;
 
-                return ((GridDhtPartitionSupplyMessage)msg).cacheId() == CU.cacheId(cacheName);
+                return ((GridDhtPartitionSupplyMessage)msg).groupId() == grpId;
             }
         });
     }
@@ -2420,7 +2422,7 @@ public class CacheLateAffinityAssignmentTest extends GridCommonAbstractTest {
 
         Collection<ClusterNode> allNodes = ctx.discovery().cacheNodes(topVer0);
 
-        for (DynamicCacheDescriptor cacheDesc : ctx.cache().cacheDescriptors()) {
+        for (DynamicCacheDescriptor cacheDesc : ctx.cache().cacheDescriptors().values()) {
             if (assignments.get(cacheDesc.cacheId()) != null)
                 continue;
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCachePartitionLossPolicySelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCachePartitionLossPolicySelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCachePartitionLossPolicySelfTest.java
index 5bbeef9..47fefe5 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCachePartitionLossPolicySelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCachePartitionLossPolicySelfTest.java
@@ -19,7 +19,6 @@ package org.apache.ignite.internal.processors.cache.distributed;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import javax.cache.CacheException;
@@ -42,7 +41,6 @@ import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
-import org.apache.ignite.util.TestTcpCommunicationSpi;
 
 import static org.apache.ignite.cache.CacheMode.PARTITIONED;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
@@ -67,23 +65,8 @@ public class IgniteCachePartitionLossPolicySelfTest extends GridCommonAbstractTe
     @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
         IgniteConfiguration cfg = super.getConfiguration(gridName);
 
-        TcpDiscoverySpi disco = new TcpDiscoverySpi();
-        disco.setIpFinder(ipFinder);
-        cfg.setDiscoverySpi(disco);
+        ((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(ipFinder);
 
-        if (gridName.matches(".*\\d")) {
-            String idStr = UUID.randomUUID().toString();
-
-            char[] chars = idStr.toCharArray();
-
-            chars[chars.length - 3] = '0';
-            chars[chars.length - 2] = '0';
-            chars[chars.length - 1] = gridName.charAt(gridName.length() - 1);
-
-            cfg.setNodeId(UUID.fromString(new String(chars)));
-        }
-
-        cfg.setCommunicationSpi(new TestTcpCommunicationSpi());
         cfg.setClientMode(client);
 
         CacheConfiguration<Integer, Integer> cacheCfg = new CacheConfiguration<>(CACHE_NAME);

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheReadFromBackupTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheReadFromBackupTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheReadFromBackupTest.java
index 1433daa..9907937 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheReadFromBackupTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/IgniteCacheReadFromBackupTest.java
@@ -41,7 +41,6 @@ import org.apache.ignite.internal.TestRecordingCommunicationSpi;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearGetRequest;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearSingleGetRequest;
-import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.lang.IgniteBiPredicate;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
@@ -196,12 +195,14 @@ public class IgniteCacheReadFromBackupTest extends GridCommonAbstractTest {
                     TestRecordingCommunicationSpi spi =
                         (TestRecordingCommunicationSpi)ignite.configuration().getCommunicationSpi();
 
+                    final int grpId = groupIdForCache(ignite, ccfg.getName());
+
                     spi.blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
                         @Override public boolean apply(ClusterNode node, Message msg) {
                             if (!msg.getClass().equals(GridDhtPartitionSupplyMessage.class))
                                 return false;
 
-                            return ((GridDhtPartitionSupplyMessage)msg).cacheId() == CU.cacheId(ccfg.getName());
+                            return ((GridDhtPartitionSupplyMessage)msg).groupId() == grpId;
                         }
                     });
                 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest.java
new file mode 100644
index 0000000..3eafe68
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.distributed.dht;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public class GridCachePartitionedNearDisabledMultiNodeWithGroupFullApiSelfTest
+    extends GridCachePartitionedNearDisabledMultiNodeFullApiSelfTest {
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration cacheConfiguration(String igniteInstanceName) throws Exception {
+        CacheConfiguration ccfg = super.cacheConfiguration(igniteInstanceName);
+
+        ccfg.setGroupName("group1");
+
+        return ccfg;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/IgniteCacheAtomicProtocolTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/IgniteCacheAtomicProtocolTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/IgniteCacheAtomicProtocolTest.java
index 888fae3..749ebe8 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/IgniteCacheAtomicProtocolTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/IgniteCacheAtomicProtocolTest.java
@@ -34,12 +34,11 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteKernal;
 import org.apache.ignite.internal.TestRecordingCommunicationSpi;
-import org.apache.ignite.internal.processors.cache.GridCacheMessage;
+import org.apache.ignite.internal.processors.cache.GridCacheGroupIdMessage;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionSupplyMessage;
 import org.apache.ignite.internal.util.lang.GridAbsPredicate;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.G;
-import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiPredicate;
 import org.apache.ignite.lang.IgniteFuture;
@@ -100,11 +99,13 @@ public class IgniteCacheAtomicProtocolTest extends GridCommonAbstractTest {
      *
      */
     private void blockRebalance() {
+        final int grpId = groupIdForCache(ignite(0), TEST_CACHE);
+
         for (Ignite node : G.allGrids()) {
             testSpi(node).blockMessages(new IgniteBiPredicate<ClusterNode, Message>() {
                 @Override public boolean apply(ClusterNode node, Message msg) {
                     return (msg instanceof GridDhtPartitionSupplyMessage)
-                        && ((GridCacheMessage)msg).cacheId() == CU.cacheId(TEST_CACHE);
+                        && ((GridCacheGroupIdMessage)msg).groupId() == grpId;
                 }
             });
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicMultiNodeWithGroupFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicMultiNodeWithGroupFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicMultiNodeWithGroupFullApiSelfTest.java
new file mode 100644
index 0000000..30337d9
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicMultiNodeWithGroupFullApiSelfTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.distributed.near;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public class GridCacheAtomicMultiNodeWithGroupFullApiSelfTest extends GridCacheAtomicMultiNodeFullApiSelfTest {
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration cacheConfiguration(String igniteInstanceName) throws Exception {
+        CacheConfiguration ccfg = super.cacheConfiguration(igniteInstanceName);
+
+        ccfg.setGroupName("group1");
+
+        return ccfg;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest.java
new file mode 100644
index 0000000..f57d81a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.distributed.near;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public class GridCacheAtomicNearEnabledMultiNodeWithGroupFullApiSelfTest extends
+    GridCacheAtomicNearEnabledMultiNodeFullApiSelfTest {
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration cacheConfiguration(String igniteInstanceName) throws Exception {
+        CacheConfiguration ccfg = super.cacheConfiguration(igniteInstanceName);
+
+        ccfg.setGroupName("group1");
+
+        return ccfg;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheNearReadersSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheNearReadersSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheNearReadersSelfTest.java
index 40a3af2..cbb2032 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheNearReadersSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCacheNearReadersSelfTest.java
@@ -253,11 +253,12 @@ public class GridCacheNearReadersSelfTest extends GridCommonAbstractTest {
 
         awaitPartitionMapExchange();
 
-        GridCacheContext ctx = ((IgniteKernal) g1).internalCache(DEFAULT_CACHE_NAME).context();
+        GridCacheContext ctx = ((IgniteKernal)g1).internalCache(DEFAULT_CACHE_NAME).context();
 
         List<KeyCacheObject> cacheKeys = F.asList(ctx.toCacheKeyObject(1), ctx.toCacheKeyObject(2));
 
         IgniteInternalFuture<Object> f1 = ((IgniteKernal)g1).internalCache(DEFAULT_CACHE_NAME).preloader().request(
+            ctx,
             cacheKeys,
             new AffinityTopologyVersion(2));
 
@@ -266,6 +267,7 @@ public class GridCacheNearReadersSelfTest extends GridCommonAbstractTest {
 
 
         IgniteInternalFuture<Object> f2 = ((IgniteKernal)g2).internalCache(DEFAULT_CACHE_NAME).preloader().request(
+            ((IgniteKernal)g2).internalCache(DEFAULT_CACHE_NAME).context(),
             cacheKeys,
             new AffinityTopologyVersion(2));
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCachePartitionedMultiNodeWithGroupFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCachePartitionedMultiNodeWithGroupFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCachePartitionedMultiNodeWithGroupFullApiSelfTest.java
new file mode 100644
index 0000000..542628a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/GridCachePartitionedMultiNodeWithGroupFullApiSelfTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.distributed.near;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public class GridCachePartitionedMultiNodeWithGroupFullApiSelfTest extends GridCachePartitionedMultiNodeFullApiSelfTest {
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration cacheConfiguration(String igniteInstanceName) throws Exception {
+        CacheConfiguration ccfg = super.cacheConfiguration(igniteInstanceName);
+
+        ccfg.setGroupName("group1");
+
+        return ccfg;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/replicated/preloader/GridCacheReplicatedPreloadSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/replicated/preloader/GridCacheReplicatedPreloadSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/replicated/preloader/GridCacheReplicatedPreloadSelfTest.java
index 346f908..e90b7e1 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/replicated/preloader/GridCacheReplicatedPreloadSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/replicated/preloader/GridCacheReplicatedPreloadSelfTest.java
@@ -268,7 +268,8 @@ public class GridCacheReplicatedPreloadSelfTest extends GridCommonAbstractTest {
                     break;
             }
 
-            assert evts != null && evts.size() == 2 : "Wrong events received: " + evts;
+            assertNotNull(evts);
+            assertEquals("Wrong events received: " + evts, 2, evts.size());
 
             Iterator<Event> iter = evts.iterator();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheClientNearCacheExpiryTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheClientNearCacheExpiryTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheClientNearCacheExpiryTest.java
index f7164a0..3417ba8 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheClientNearCacheExpiryTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheClientNearCacheExpiryTest.java
@@ -28,8 +28,7 @@ import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.NearCacheConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
-import org.apache.ignite.internal.processors.cache.GridCacheConcurrentMap;
-import org.apache.ignite.internal.processors.cache.GridCacheProxyImpl;
+import org.apache.ignite.internal.processors.cache.GridCacheLocalConcurrentMap;
 import org.apache.ignite.internal.processors.cache.IgniteCacheAbstractTest;
 import org.apache.ignite.internal.processors.cache.IgniteCacheProxy;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -84,6 +83,13 @@ public class IgniteCacheClientNearCacheExpiryTest extends IgniteCacheAbstractTes
     public void testExpirationOnClient() throws Exception {
         Ignite ignite = grid(NODES - 1);
 
+        // Check size of near entries via reflection because entries is filtered for size() API call.
+        IgniteEx igniteEx = (IgniteEx)ignite;
+
+        GridCacheAdapter internalCache = igniteEx.context().cache().internalCache(DEFAULT_CACHE_NAME);
+
+        GridCacheLocalConcurrentMap map = GridTestUtils.getFieldValue(internalCache, GridCacheAdapter.class, "map");
+
         assertTrue(ignite.configuration().isClientMode());
 
         IgniteCache<Object, Object> cache = ignite.cache(DEFAULT_CACHE_NAME);
@@ -103,17 +109,11 @@ public class IgniteCacheClientNearCacheExpiryTest extends IgniteCacheAbstractTes
             assertEquals(i, cacheWithExpiry.localPeek(i));
         }
 
+        assertEquals(KEYS_COUNT * 2, map.publicSize(internalCache.context().cacheId()));
 
         U.sleep(1000);
 
-        // Check size of near entries via reflection because entries is filtered for size() API call.
-        IgniteEx igniteEx = (IgniteEx)ignite;
-        GridCacheConcurrentMap map = GridTestUtils.getFieldValue(
-            ((GridCacheProxyImpl)igniteEx.cachex(DEFAULT_CACHE_NAME)).delegate(),
-            GridCacheAdapter.class,
-            "map");
-
-        assertEquals(KEYS_COUNT, map.publicSize());
+        assertEquals(KEYS_COUNT, map.publicSize(internalCache.context().cacheId()));
 
         assertEquals(KEYS_COUNT, cache.size());
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
index 9d21823..227fe1f 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/expiry/IgniteCacheTtlCleanupSelfTest.java
@@ -82,6 +82,6 @@ public class IgniteCacheTtlCleanupSelfTest extends GridCacheAbstractSelfTest {
         CacheObjectContext cacheObjCtx = cacheAdapter.context().cacheObjectContext();
 
         for (int i = 0; i < 100; i++)
-            assertNull(cacheAdapter.map().getEntry(cacheObjects.toCacheKeyObject(cacheObjCtx, null, i, true)));
+            assertNull(cacheAdapter.map().getEntry(cacheAdapter.context(), cacheObjects.toCacheKeyObject(cacheObjCtx, null, i, true)));
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheJdbcBlobStoreNodeRestartTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheJdbcBlobStoreNodeRestartTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheJdbcBlobStoreNodeRestartTest.java
index 1c29098..83ddf67 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheJdbcBlobStoreNodeRestartTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/integration/IgniteCacheJdbcBlobStoreNodeRestartTest.java
@@ -23,6 +23,9 @@ import org.apache.ignite.cache.store.CacheStore;
 import org.apache.ignite.cache.store.jdbc.CacheJdbcBlobStore;
 import org.apache.ignite.configuration.NearCacheConfiguration;
 
+/**
+ *
+ */
 public class IgniteCacheJdbcBlobStoreNodeRestartTest extends IgniteCacheStoreNodeRestartAbstractTest {
     /** {@inheritDoc} */
     @Override protected CacheStore getStore() {

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalAtomicWithGroupFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalAtomicWithGroupFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalAtomicWithGroupFullApiSelfTest.java
new file mode 100644
index 0000000..4597a75
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalAtomicWithGroupFullApiSelfTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.local;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public class GridCacheLocalAtomicWithGroupFullApiSelfTest extends GridCacheLocalAtomicFullApiSelfTest {
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration cacheConfiguration(String igniteInstanceName) throws Exception {
+        CacheConfiguration ccfg = super.cacheConfiguration(igniteInstanceName);
+
+        ccfg.setGroupName("group1");
+
+        return ccfg;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalFullApiSelfTest.java
index 0c7a217..8cb9369 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalFullApiSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalFullApiSelfTest.java
@@ -22,7 +22,6 @@ import java.util.Map;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.processors.cache.GridCacheAbstractFullApiSelfTest;
 import org.apache.ignite.internal.util.typedef.F;

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalWithGroupFullApiSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalWithGroupFullApiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalWithGroupFullApiSelfTest.java
new file mode 100644
index 0000000..867a2de
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/local/GridCacheLocalWithGroupFullApiSelfTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.local;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+
+/**
+ *
+ */
+public class GridCacheLocalWithGroupFullApiSelfTest extends GridCacheLocalFullApiSelfTest {
+    /** {@inheritDoc} */
+    @Override protected CacheConfiguration cacheConfiguration(String igniteInstanceName) throws Exception {
+        CacheConfiguration ccfg = super.cacheConfiguration(igniteInstanceName);
+
+        ccfg.setGroupName("group1");
+
+        return ccfg;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryConcurrentPartitionUpdateTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryConcurrentPartitionUpdateTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryConcurrentPartitionUpdateTest.java
index 9c7c836..6c74f79 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryConcurrentPartitionUpdateTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryConcurrentPartitionUpdateTest.java
@@ -35,6 +35,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.util.lang.GridAbsPredicate;
+import org.apache.ignite.internal.util.typedef.T2;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
 import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
@@ -78,52 +79,74 @@ public class CacheContinuousQueryConcurrentPartitionUpdateTest extends GridCommo
      * @throws Exception If failed.
      */
     public void testConcurrentUpdatePartitionAtomic() throws Exception {
-        concurrentUpdatePartition(ATOMIC);
+        concurrentUpdatePartition(ATOMIC, false);
     }
 
     /**
      * @throws Exception If failed.
      */
     public void testConcurrentUpdatePartitionTx() throws Exception {
-        concurrentUpdatePartition(TRANSACTIONAL);
+        concurrentUpdatePartition(TRANSACTIONAL, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConcurrentUpdatePartitionAtomicCacheGroup() throws Exception {
+        concurrentUpdatePartition(ATOMIC, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void testConcurrentUpdatePartitionTxCacheGroup() throws Exception {
+        concurrentUpdatePartition(TRANSACTIONAL, true);
     }
 
     /**
      * @param atomicityMode Cache atomicity mode.
+     * @param cacheGrp {@code True} if test cache multiple caches in the same group.
      * @throws Exception If failed.
      */
-    private void concurrentUpdatePartition(CacheAtomicityMode atomicityMode) throws Exception {
+    private void concurrentUpdatePartition(CacheAtomicityMode atomicityMode, boolean cacheGrp) throws Exception {
         Ignite srv = startGrid(0);
 
         client = true;
 
         Ignite client = startGrid(1);
 
-        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+        List<AtomicInteger> cntrs = new ArrayList<>();
+        List<String> caches = new ArrayList<>();
 
-        ccfg.setWriteSynchronizationMode(FULL_SYNC);
-        ccfg.setAtomicityMode(atomicityMode);
+        if (cacheGrp) {
+            for (int i = 0; i < 3; i++) {
+                CacheConfiguration<Object, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME + i);
 
-        IgniteCache clientCache = client.createCache(ccfg);
+                ccfg.setGroupName("testGroup");
+                ccfg.setWriteSynchronizationMode(FULL_SYNC);
+                ccfg.setAtomicityMode(atomicityMode);
 
-        final AtomicInteger evtCnt = new AtomicInteger();
+                IgniteCache<Object, Object> cache = client.createCache(ccfg);
 
-        ContinuousQuery<Object, Object> qry = new ContinuousQuery<>();
-
-        qry.setLocalListener(new CacheEntryUpdatedListener<Object, Object>() {
-            @Override public void onUpdated(Iterable<CacheEntryEvent<?, ?>> evts) {
-                for (CacheEntryEvent evt : evts) {
-                    assertNotNull(evt.getKey());
-                    assertNotNull(evt.getValue());
+                caches.add(cache.getName());
 
-                    evtCnt.incrementAndGet();
-                }
+                cntrs.add(startListener(cache).get1());
             }
-        });
+        }
+        else {
+            CacheConfiguration<Object, Object> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
 
-        clientCache.query(qry);
+            ccfg.setWriteSynchronizationMode(FULL_SYNC);
+            ccfg.setAtomicityMode(atomicityMode);
 
-        Affinity<Integer> aff = srv.affinity(DEFAULT_CACHE_NAME);
+            IgniteCache<Object, Object> cache = client.createCache(ccfg);
+
+            caches.add(cache.getName());
+
+            cntrs.add(startListener(cache).get1());
+        }
+
+        Affinity<Integer> aff = srv.affinity(caches.get(0));
 
         final List<Integer> keys = new ArrayList<>();
 
@@ -143,7 +166,10 @@ public class CacheContinuousQueryConcurrentPartitionUpdateTest extends GridCommo
         final int THREADS = 10;
         final int UPDATES = 1000;
 
-        final IgniteCache<Object, Object> srvCache = srv.cache(DEFAULT_CACHE_NAME);
+        final List<IgniteCache<Object, Object>> srvCaches = new ArrayList<>();
+
+        for (String cacheName : caches)
+            srvCaches.add(srv.cache(cacheName));
 
         for (int i = 0; i < 15; i++) {
             log.info("Iteration: " + i);
@@ -152,60 +178,124 @@ public class CacheContinuousQueryConcurrentPartitionUpdateTest extends GridCommo
                 @Override public Void call() throws Exception {
                     ThreadLocalRandom rnd = ThreadLocalRandom.current();
 
-                    for (int i = 0; i < UPDATES; i++)
-                        srvCache.put(keys.get(rnd.nextInt(KEYS)), i);
+                    for (int i = 0; i < UPDATES; i++) {
+                        for (int c = 0; c < srvCaches.size(); c++)
+                            srvCaches.get(c).put(keys.get(rnd.nextInt(KEYS)), i);
+                    }
 
                     return null;
                 }
             }, THREADS, "update");
 
-            GridTestUtils.waitForCondition(new GridAbsPredicate() {
-                @Override public boolean apply() {
-                    log.info("Events: " + evtCnt.get());
+            for (final AtomicInteger evtCnt : cntrs) {
+                GridTestUtils.waitForCondition(new GridAbsPredicate() {
+                    @Override public boolean apply() {
+                        log.info("Events: " + evtCnt.get());
 
-                    return evtCnt.get() >= THREADS * UPDATES;
-                }
-            }, 5000);
+                        return evtCnt.get() >= THREADS * UPDATES;
+                    }
+                }, 5000);
 
-            assertEquals(THREADS * UPDATES, evtCnt.get());
+                assertEquals(THREADS * UPDATES, evtCnt.get());
 
-            evtCnt.set(0);
+                evtCnt.set(0);
+            }
         }
     }
 
     /**
+     * @param cache Cache.
+     * @return Event counter.
+     */
+    private T2<AtomicInteger, QueryCursor> startListener(IgniteCache<Object, Object> cache) {
+        final AtomicInteger evtCnt = new AtomicInteger();
+
+        ContinuousQuery<Object, Object> qry = new ContinuousQuery<>();
+
+        qry.setLocalListener(new CacheEntryUpdatedListener<Object, Object>() {
+            @Override public void onUpdated(Iterable<CacheEntryEvent<?, ?>> evts) {
+                for (CacheEntryEvent evt : evts) {
+                    assertNotNull(evt.getKey());
+                    assertNotNull(evt.getValue());
+
+                    if ((Integer)evt.getValue() >= 0)
+                        evtCnt.incrementAndGet();
+                }
+            }
+        });
+
+        QueryCursor cur = cache.query(qry);
+
+        return new T2<>(evtCnt, cur);
+    }
+
+    /**
      * @throws Exception If failed.
      */
     public void testConcurrentUpdatesAndQueryStartAtomic() throws Exception {
-        concurrentUpdatesAndQueryStart(ATOMIC);
+        concurrentUpdatesAndQueryStart(ATOMIC, false);
     }
 
     /**
      * @throws Exception If failed.
      */
     public void testConcurrentUpdatesAndQueryStartTx() throws Exception {
-        concurrentUpdatesAndQueryStart(TRANSACTIONAL);
+        concurrentUpdatesAndQueryStart(TRANSACTIONAL, false);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void _testConcurrentUpdatesAndQueryStartAtomicCacheGroup() throws Exception {
+        concurrentUpdatesAndQueryStart(ATOMIC, true);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public void _testConcurrentUpdatesAndQueryStartTxCacheGroup() throws Exception {
+        concurrentUpdatesAndQueryStart(TRANSACTIONAL, true);
     }
 
     /**
      * @param atomicityMode Cache atomicity mode.
+     * @param cacheGrp {@code True} if test cache multiple caches in the same group.
      * @throws Exception If failed.
      */
-    private void concurrentUpdatesAndQueryStart(CacheAtomicityMode atomicityMode) throws Exception {
+    private void concurrentUpdatesAndQueryStart(CacheAtomicityMode atomicityMode, boolean cacheGrp) throws Exception {
         Ignite srv = startGrid(0);
 
         client = true;
 
         Ignite client = startGrid(1);
 
-        CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+        List<String> caches = new ArrayList<>();
+
+        if (cacheGrp) {
+            for (int i = 0; i < 3; i++) {
+                CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME + i);
 
-        ccfg.setWriteSynchronizationMode(FULL_SYNC);
-        ccfg.setAtomicityMode(atomicityMode);
+                ccfg.setGroupName("testGroup");
+                ccfg.setWriteSynchronizationMode(FULL_SYNC);
+                ccfg.setAtomicityMode(atomicityMode);
 
-        IgniteCache clientCache = client.createCache(ccfg);
+                IgniteCache cache = client.createCache(ccfg);
 
-        Affinity<Integer> aff = srv.affinity(DEFAULT_CACHE_NAME);
+                caches.add(cache.getName());
+            }
+        }
+        else {
+            CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+            ccfg.setWriteSynchronizationMode(FULL_SYNC);
+            ccfg.setAtomicityMode(atomicityMode);
+
+            IgniteCache cache = client.createCache(ccfg);
+
+            caches.add(cache.getName());
+        }
+
+        Affinity<Integer> aff = srv.affinity(caches.get(0));
 
         final List<Integer> keys = new ArrayList<>();
 
@@ -225,38 +315,27 @@ public class CacheContinuousQueryConcurrentPartitionUpdateTest extends GridCommo
         final int THREADS = 10;
         final int UPDATES = 1000;
 
-        for (int i = 0; i < 5; i++) {
-            log.info("Iteration: " + i);
-
-            ContinuousQuery<Object, Object> qry = new ContinuousQuery<>();
-
-            final AtomicInteger evtCnt = new AtomicInteger();
-
-            qry.setLocalListener(new CacheEntryUpdatedListener<Object, Object>() {
-                @Override public void onUpdated(Iterable<CacheEntryEvent<?, ?>> evts) {
-                    for (CacheEntryEvent evt : evts) {
-                        assertNotNull(evt.getKey());
-                        assertNotNull(evt.getValue());
+        final List<IgniteCache<Object, Object>> srvCaches = new ArrayList<>();
 
-                        if ((Integer)evt.getValue() >= 0)
-                            evtCnt.incrementAndGet();
-                    }
-                }
-            });
-
-            QueryCursor cur;
+        for (String cacheName : caches)
+            srvCaches.add(srv.cache(cacheName));
 
-            final IgniteCache<Object, Object> srvCache = srv.cache(DEFAULT_CACHE_NAME);
+        for (int i = 0; i < 5; i++) {
+            log.info("Iteration: " + i);
 
             final AtomicBoolean stop = new AtomicBoolean();
 
+            List<T2<AtomicInteger, QueryCursor> > qrys = new ArrayList<>();
+
             try {
                 IgniteInternalFuture fut = GridTestUtils.runMultiThreadedAsync(new Callable<Void>() {
                     @Override public Void call() throws Exception {
                         ThreadLocalRandom rnd = ThreadLocalRandom.current();
 
-                        while (!stop.get())
-                            srvCache.put(keys.get(rnd.nextInt(KEYS)), rnd.nextInt(100) - 200);
+                        while (!stop.get()) {
+                            for (IgniteCache<Object, Object> srvCache : srvCaches)
+                                srvCache.put(keys.get(rnd.nextInt(KEYS)), rnd.nextInt(100) - 200);
+                        }
 
                         return null;
                     }
@@ -264,7 +343,8 @@ public class CacheContinuousQueryConcurrentPartitionUpdateTest extends GridCommo
 
                 U.sleep(1000);
 
-                cur = clientCache.query(qry);
+                for (String cache : caches)
+                    qrys.add(startListener(client.cache(cache)));
 
                 U.sleep(1000);
 
@@ -280,25 +360,30 @@ public class CacheContinuousQueryConcurrentPartitionUpdateTest extends GridCommo
                 @Override public Void call() throws Exception {
                     ThreadLocalRandom rnd = ThreadLocalRandom.current();
 
-                    for (int i = 0; i < UPDATES; i++)
-                        srvCache.put(keys.get(rnd.nextInt(KEYS)), i);
+                    for (int i = 0; i < UPDATES; i++) {
+                        for (IgniteCache<Object, Object> srvCache : srvCaches)
+                            srvCache.put(keys.get(rnd.nextInt(KEYS)), i);
+                    }
 
                     return null;
                 }
             }, THREADS, "update");
 
+            for (T2<AtomicInteger, QueryCursor>  qry : qrys) {
+                final AtomicInteger evtCnt = qry.get1();
 
-            GridTestUtils.waitForCondition(new GridAbsPredicate() {
-                @Override public boolean apply() {
-                    log.info("Events: " + evtCnt.get());
+                GridTestUtils.waitForCondition(new GridAbsPredicate() {
+                    @Override public boolean apply() {
+                        log.info("Events: " + evtCnt.get());
 
-                    return evtCnt.get() >= THREADS * UPDATES;
-                }
-            }, 5000);
+                        return evtCnt.get() >= THREADS * UPDATES;
+                    }
+                }, 5000);
 
-            assertEquals(THREADS * UPDATES, evtCnt.get());
+                assertEquals(THREADS * UPDATES, qry.get1().get());
 
-            cur.close();
+                qry.get2().close();
+            }
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java
index 2fb7fcb..54791d7 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java
@@ -2243,13 +2243,11 @@ public abstract class CacheContinuousQueryFailoverAbstractSelfTest extends GridC
      * @throws Exception If failed.
      */
     public void testNoEventLossOnTopologyChange() throws Exception {
-        final int stableNodeCnt = 1;
-
         final int batchLoadSize = 2000;
 
         final int restartCycles = 5;
 
-        Ignite qryClient = startGridsMultiThreaded(stableNodeCnt);
+        Ignite qryClient = startGrid(0);
 
         final CacheEventListener4 lsnr = new CacheEventListener4(atomicityMode() == CacheAtomicityMode.ATOMIC);
 
@@ -2282,7 +2280,7 @@ public abstract class CacheContinuousQueryFailoverAbstractSelfTest extends GridC
 
                 log.info("Batch loaded. Iteration: " + iteration);
 
-                final long expCnt = putCnt * stableNodeCnt + ignoredDupEvts;
+                final long expCnt = putCnt + ignoredDupEvts;
 
                 GridTestUtils.waitForCondition(new GridAbsPredicate() {
                     @Override public boolean apply() {
@@ -2316,7 +2314,6 @@ public abstract class CacheContinuousQueryFailoverAbstractSelfTest extends GridC
                     String msg = sb.toString();
 
                     // In atomic mode CQ can receive duplicate update events if update retried after fails.
-                    // E.g. topology change
                     if (atomicityMode() == CacheAtomicityMode.ATOMIC && msg.isEmpty() && cnt > expCnt)
                         ignoredDupEvts += cnt - expCnt;
                     else

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxOptimisticDeadlockDetectionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxOptimisticDeadlockDetectionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxOptimisticDeadlockDetectionTest.java
index b48c4ba..1573576 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxOptimisticDeadlockDetectionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxOptimisticDeadlockDetectionTest.java
@@ -399,7 +399,7 @@ public class TxOptimisticDeadlockDetectionTest extends GridCommonAbstractTest {
 
                 KeyCacheObject keyCacheObj = intCache.context().toCacheKeyObject(key0);
 
-                GridCacheMapEntry entry = map.getEntry(keyCacheObj);
+                GridCacheMapEntry entry = map.getEntry(intCache.context(), keyCacheObj);
 
                 if (entry != null)
                     assertNull("Entry still has locks " + entry, entry.mvccAllLocal());

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxPessimisticDeadlockDetectionTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxPessimisticDeadlockDetectionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxPessimisticDeadlockDetectionTest.java
index bc297a2..61f7125 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxPessimisticDeadlockDetectionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/transactions/TxPessimisticDeadlockDetectionTest.java
@@ -408,7 +408,7 @@ public class TxPessimisticDeadlockDetectionTest extends GridCommonAbstractTest {
 
                 KeyCacheObject keyCacheObj = intCache.context().toCacheKeyObject(key0);
 
-                GridCacheMapEntry entry = map.getEntry(keyCacheObj);
+                GridCacheMapEntry entry = map.getEntry(intCache.context(), keyCacheObj);
 
                 if (entry != null)
                     assertNull("Entry still has locks " + entry, entry.mvccAllLocal());

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/loadtests/hashmap/GridCacheTestContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/loadtests/hashmap/GridCacheTestContext.java b/modules/core/src/test/java/org/apache/ignite/loadtests/hashmap/GridCacheTestContext.java
index f12537b..310082d 100644
--- a/modules/core/src/test/java/org/apache/ignite/loadtests/hashmap/GridCacheTestContext.java
+++ b/modules/core/src/test/java/org/apache/ignite/loadtests/hashmap/GridCacheTestContext.java
@@ -81,14 +81,11 @@ public class GridCacheTestContext<K, V> extends GridCacheContext<K, V> {
                 null
             ),
             defaultCacheConfiguration(),
+            null,
             CacheType.USER,
             AffinityTopologyVersion.ZERO,
-            UUID.randomUUID(),
             true,
             true,
-            null,
-            null,
-            null,
             new GridCacheEventManager(),
             new CacheOsStoreManager(null, new CacheConfiguration()),
             new GridCacheEvictionManager(),
@@ -97,7 +94,6 @@ public class GridCacheTestContext<K, V> extends GridCacheContext<K, V> {
             new CacheDataStructuresManager(),
             new GridCacheTtlManager(),
             new GridOsCacheDrManager(),
-            null,
             new CacheOsConflictResolutionManager<K, V>(),
             new CachePluginManager(ctx, new CacheConfiguration()),
             new GridCacheAffinityManager()

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/spi/communication/GridCacheMessageSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/spi/communication/GridCacheMessageSelfTest.java b/modules/core/src/test/java/org/apache/ignite/spi/communication/GridCacheMessageSelfTest.java
index 25ff2fd..4a6b765 100644
--- a/modules/core/src/test/java/org/apache/ignite/spi/communication/GridCacheMessageSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/spi/communication/GridCacheMessageSelfTest.java
@@ -216,6 +216,16 @@ public class GridCacheMessageSelfTest extends GridCommonAbstractTest {
         }
 
         /** {@inheritDoc} */
+        @Override public int handlerId() {
+            return 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean cacheGroupMessage() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
         @Override public boolean addDeploymentInfo() {
             return false;
         }
@@ -311,6 +321,16 @@ public class GridCacheMessageSelfTest extends GridCommonAbstractTest {
         }
 
         /** {@inheritDoc} */
+        @Override public int handlerId() {
+            return 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean cacheGroupMessage() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
         @Override public boolean addDeploymentInfo() {
             return false;
         }
@@ -434,6 +454,16 @@ public class GridCacheMessageSelfTest extends GridCommonAbstractTest {
         }
 
         /** {@inheritDoc} */
+        @Override public int handlerId() {
+            return 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean cacheGroupMessage() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
         @Override public boolean addDeploymentInfo() {
             return false;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
index 46d97a0..cbcbaee 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java
@@ -101,6 +101,8 @@ import org.apache.ignite.testframework.config.GridTestProperties;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
+import static org.springframework.util.FileSystemUtils.deleteRecursively;
+
 /**
  * Utility class for tests.
  */
@@ -1876,4 +1878,11 @@ public final class GridTestUtils {
 
         return b.toString();
     }
+
+    /**
+     * @throws Exception If failed.
+     */
+    public static void deleteDbFiles() throws Exception {
+        deleteRecursively(U.resolveWorkDirectory(U.defaultWorkDirectory(), "db", false));
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/7e45010b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
index 281901a..9d0af5d 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
@@ -68,6 +68,7 @@ import org.apache.ignite.internal.binary.BinaryContext;
 import org.apache.ignite.internal.binary.BinaryEnumCache;
 import org.apache.ignite.internal.binary.BinaryMarshaller;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
 import org.apache.ignite.internal.util.GridClassLoaderCache;
 import org.apache.ignite.internal.util.GridTestClockTimer;
@@ -2102,6 +2103,22 @@ public abstract class GridAbstractTest extends TestCase {
     }
 
     /**
+     * @param node Node.
+     * @param cacheName Cache name.
+     * @return Cache group ID for given cache name.
+     */
+    protected final int groupIdForCache(Ignite node, String cacheName) {
+        for (CacheGroupContext grp : ((IgniteKernal)node).context().cache().cacheGroups()) {
+            if (grp.hasCache(cacheName))
+                return grp.groupId();
+        }
+
+        fail("Failed to find group for cache: " + cacheName);
+
+        return 0;
+    }
+
+    /**
      *
      */
     private static interface WriteReplaceOwner {