You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by av...@apache.org on 2019/05/15 15:16:33 UTC

[ignite] 01/01: Squashed commit of the following:

This is an automated email from the ASF dual-hosted git repository.

av pushed a commit to branch ignite-10078-jepsen1
in repository https://gitbox.apache.org/repos/asf/ignite.git

commit ee0ad939b9e56fbc11a7b3bd25bc5c315289915c
Author: Anton Vinogradov <av...@apache.org>
AuthorDate: Wed May 15 18:14:53 2019 +0300

    Squashed commit of the following:
    
    commit ca67cc531b1cdfd47f22e949716b43ebd3ae1ca2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed May 15 15:51:54 2019 +0300
    
        IGNITE-10078 Fix several issues.
    
    commit e1f5f4e33b25a6ac4fd1082749d8aae079759799
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed May 8 10:24:27 2019 +0300
    
        IGNITE-10078 Minor codestyle fix.
    
    commit 2c0d7a3b837cc5ce7e2bbe5d712b156dc41a431f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue May 7 10:28:38 2019 +0300
    
        IGNITE-10078 Fix codestyle issues.
    
    commit cda9e5090f5750b78a069b3848049560c56148b2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon May 6 18:51:22 2019 +0300
    
        IGNITE-10078 Disabled RollbackRecord for MVCC caches.
    
    commit 296abfbd886b9ad4fe488a6749803ee5fc9fcf47
    Merge: 70b02dec4d 7a42592843
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon May 6 18:30:03 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
    
    commit 70b02dec4dbb41851a976c0d7c41ab2ad065829c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon May 6 16:58:39 2019 +0300
    
        IGNITE-10078 Fix in-memory partition clearing issue, reverted a  revert of IGNITE-10799.
    
    commit 3689f9593a7b264617e7c95a309163c9e7ef8c6c
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun May 5 17:41:49 2019 +0300
    
        Revert "IGNITE-10799 Optimize affinity recalculation in case of node join or leave - Fixes #6242."
    
        This reverts commit afe7933b156d51691997fefd251b76de5ea15e1a.
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentCache.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
    
    commit 83685aa98a1d67334c6d80f025e5495e6d8d9e61
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Tue Apr 30 13:05:02 2019 +0300
    
        IGNITE-10078 TC check.
    
    commit d7eeb7fc9fb273eac41b7c297688d65f3c582bbb
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Tue Apr 30 11:39:47 2019 +0300
    
        IGNITE-10078 Revert to old initAffinityOnNodeJoin
    
    commit 04114b944d82409d2dcd13f89d4dd79297b87456
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 29 18:44:57 2019 +0300
    
        IGNITE-10078 Fix concurrent isolated updater and tx mode.
    
    commit ec11a9e91b83beddf3a79e6de7484e04cbd1fea2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 29 16:29:26 2019 +0300
    
        IGNITE-10078 Cleanup.
    
    commit 4741ad3267d76e3a7a496926fff534b508f39a5c
    Merge: 4a44047cbe 2ad6c85a67
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 29 14:06:29 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV2Serializer.java
    
    commit 4a44047cbe1f1e44d19e02a401d319799ac452ce
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Apr 28 17:46:13 2019 +0300
    
        IGNITE-10078 Fix issue with cache group name (not properly handled in test code).
    
    commit 15ea64f792f29db3dd88da66b1974df025e1bf2f
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Apr 28 14:55:22 2019 +0300
    
        IGNITE-10078 Fix issue with isolated updater.
    
    commit d05fb3f0971a62097d28ef52de1ecd41309d61d4
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sat Apr 27 15:30:50 2019 +0300
    
        IGNITE-10078 Partial revert of affinity calculation optimization on node left.
    
    commit 1c4a20a7276eefc5f2da6decc580bef6eb1bbfd7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 26 19:12:39 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
    
    commit 349e8d6d6603fb6b82e3ba14b8178a7be0b70ece
    Merge: 19ca96fa9c a998b25828
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 26 19:11:43 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
    
    commit 19ca96fa9ce8c25eed0d047b8d0f8a069e58a96e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 26 18:51:43 2019 +0300
    
        IGNITE-10078 Minor.
    
    commit e2a44a3c989abe1db59759a0ed742f022f569a7f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 26 18:49:36 2019 +0300
    
        IGNITE-10078 Final codestyle fix.
    
    commit 25dc10a2b87b65fb80df80ff331893a89cd177e9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 26 17:39:42 2019 +0300
    
        IGNITE-10078 Fix wrong late affinity calculation.
    
    commit a31aaa18abaed5f5e590ed4c7fc2a3d205a9f4f9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 25 19:59:18 2019 +0300
    
        IGNITE-10078 Cleanup.
    
    commit 7d41dba297ac11cf9ced030e634e05cd3aed641d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 25 19:16:37 2019 +0300
    
        IGNITE-10078 Cleanup.
    
    commit cf11881fb1a5f22ca6624389ad661a3f7b9a4aa7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 25 18:21:01 2019 +0300
    
        IGNITE-10078 Cleanup.
    
    commit 3b265806d61a5d5b25f50c4920eeb858a196578a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 25 16:35:06 2019 +0300
    
        IGNITE-10078 Cleanup.
    
    commit 03dbe7cd0fdbb969b38647b43fbbca9476b2c869
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 25 14:41:53 2019 +0300
    
        IGNITE-10078 Fix CacheGroupTest scenarios with mixed tx-atomic caches in groups.
    
    commit 6980832cbf5dd00b964a9e2f6a87229f542a53e9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 24 18:47:32 2019 +0300
    
        IGNITE-10078 Fix issue with isolated updater in test.
    
    commit 6860fa3b3df0e1179ec63b55ba1ded4c79fcf61a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 24 18:01:35 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java
        #	modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
    
    commit be160d5edf77853de47a323b5519bc283aebc1dd
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 24 15:05:44 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java
        #	modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
    
    commit 05e1df5d507bd0a6af302cd463cf14de00d09a98
    Merge: 4ccae674c3 c2c0b148ae
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 24 15:05:32 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java
        #	modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
    
    commit 4ccae674c3ad14e994d4c3600e2f9bed3224f34a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 24 14:50:00 2019 +0300
    
        IGNITE-10078 Cleanup TODOs.
    
    commit 6223cb2518b00defd6f47ebfef1feae7ded16fbb
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 23 20:35:40 2019 +0300
    
        IGNITE-10078 Cleanup TODOs.
    
    commit 3e7da5ab7867f08052676f1939c903dde35ef71e
    Merge: e48ce33e4d db285122a7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 23 15:54:29 2019 +0300
    
        Merge branch 'ignite-10078' of https://github.com/gridgain/apache-ignite into ignite-10078
    
    commit e48ce33e4d000221aebc78ec0e053f3c096f00c6
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 23 15:53:41 2019 +0300
    
        IGNITE-10078 Implemented "old mode" for atomic or mixed cache groups.
    
    commit db285122a76c86f9f0a171ad64728473ead69468
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Mon Apr 22 23:34:03 2019 +0300
    
        IGNITE-10078 Fix NPE in exchange future.
    
    commit 3cc00ab8f6b77982c3da8c82f20f014250a0929c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 22 17:58:01 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite3.java
    
    commit 9062305f0ed4860904d5c4a6a24dd181e8af8dc1
    Merge: 60fc9b26d3 d6827886a4
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 22 17:57:48 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cluster/GridClusterStateProcessor.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite3.java
    
    commit 60fc9b26d3dfd798611094bc67d2047a6aa5da23
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Apr 21 18:56:16 2019 +0300
    
        IGNITE-10078 Fix issue with re-rebalance of moving partition on coordinator.
    
    commit 7b2b7dbf074841090bebb9dc48f5b02121172fad
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 19 19:30:05 2019 +0300
    
        IGNITE-10078 Proper fix for clearing partition + full rebalance.
    
    commit 165a4d17ec97b92f90ea4a19a89108bc1589b1e3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 19:50:14 2019 +0300
    
        IGNITE-10078 Cleanup - wip.
    
    commit b8a97b78a2ab81eeb8a5136b58bb31d99d0b91e8
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 18:48:57 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/DefaultFreeList.java
    
    commit 030b4606a938d41151303878a71396ef0f5eb38c
    Merge: 204202826e 280894315f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 18:44:15 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheAffinitySharedManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/DefaultFreeList.java
    
    commit 204202826ee5a7cc261d8b8107b51f26dc0e7ea3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 17:51:14 2019 +0300
    
        IGNITE-10078 Minor style.
    
    commit 13a89dd7aa3c472cef427e84bc2c63ceb5401865
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 17:23:58 2019 +0300
    
        IGNITE-10078 More logging for flaky test CacheRentingStateRepair.
    
    commit 60da22ae7ca84b0e83fc7d235d2a8bd07cb92292
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 17:21:26 2019 +0300
    
        IGNITE-10078 More logging for flaky test CacheRentingStateRepair.
    
    commit 069842550d674e7fa28938b4f3f61389b174ffd2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 13:17:41 2019 +0300
    
        IGNITE-10078 Extending expected exceptions list in continuous restarts test.
    
    commit f779e293dc687f50e0db4645a7c7f9ba6d234907
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 18 12:01:44 2019 +0300
    
        IGNITE-10078 Extending expected exceptions list in continuous restarts test.
    
    commit a5609b1585fa6efec058736b1c489776532ad229
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 17 20:20:30 2019 +0300
    
        IGNITE-10078 Ignore flaky test.
    
    commit 88429164db2405d920418390fe88b358654bdbc1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 17 20:15:55 2019 +0300
    
        IGNITE-10078 Fix data store init on activation.
    
    commit 41c0ef30f54d7378a1dccef5b94ff2b89f847715
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 15 20:02:12 2019 +0300
    
        IGNITE-10078 In-place update fix, improved logging for rebalance.
    
    commit d7686fee1b8285b89cacbd561c53721679568984
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Apr 14 21:54:32 2019 +0300
    
        IGNITE-10078 Disabled debugging counter for TC run.
    
    commit 31df1cbc6890a94e82796dc3558693caef0ab665
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Apr 14 19:31:03 2019 +0300
    
        IGNITE-10078 Valid late assignment switch.
    
    commit cbd4e0cfc0b00858412f97962998e3a22e64d6e6
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 12 19:07:22 2019 +0300
    
        IGNITE-10078 minor renaming.
    
    commit 27c5c8e4904430a8733fa0e0e750ed23b8c9f691
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 12 18:14:17 2019 +0300
    
        IGNITE-10078 Fix for force clearing partition if was in moving state before rebalance.
    
    commit 40e88354d64aad4f44e76faf5d3d0be4278efd14
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 12 15:30:50 2019 +0300
    
        IGNITE-10078 Desync on full rebalance reproducer.
    
    commit b7a9dd115711b15fb04a8cf2e6859aeab246d105
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 11 15:07:33 2019 +0300
    
        IGNITE-10078 Additional logging.
    
    commit 4a4979c57e2ac876e455c1742ed86e2aa1512bf5
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 9 19:20:13 2019 +0300
    
        IGNITE-10078 Additional logging.
    
    commit 9ca272222ebc80f87b08c9527db42a2b6352813b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 9 18:43:40 2019 +0300
    
        IGNITE-10078 Desync fix test 3.
    
    commit a21cce2b2922b71c8cc559ee7be31c3b82ea9636
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 9 18:21:42 2019 +0300
    
        IGNITE-10078 Desync fix test 2.
    
    commit aea2d5cdf02adc11ce7870b7d2515e8a10330e5a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 9 15:49:49 2019 +0300
    
        IGNITE-10078 Desync fix test.
    
    commit a27761d262f840890cd01b57ff16e537596fd114
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 8 19:34:51 2019 +0300
    
        IGNITE-10078 Reproducer for desync + hist rebalance issue.
    
    commit 048b0a68583ccc1de94afa6f7961bcbd9ddb14b6
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 8 18:32:22 2019 +0300
    
        IGNITE-10078 Fixed concurrency issue.
    
    commit 109ef03f46b5075612b789afe4ab3a1e6ee675f8
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 5 18:01:58 2019 +0300
    
        IGNITE-10078 Cleaned up tests.
    
    commit d7a48d6873ab3075d1bafcd1699512a9aabd1cb3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 5 16:30:29 2019 +0300
    
        IGNITE-10078 Disable removal from deferred queue on rebalance by ttl.
    
    commit ee3306749d993d7534ba2abb2f29a06cf48ca219
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 5 14:18:26 2019 +0300
    
        IGNITE-10078 Next fix try.
    
    commit 29565048837a952928e7a3e4796161677dda2cdc
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 5 14:16:16 2019 +0300
    
        IGNITE-10078 Next fix try.
    
    commit 8ee04285948e3d3f1b9e913aa41c7cb4b78e3ada
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 5 13:56:45 2019 +0300
    
        IGNITE-10078 Reproduced desync issue.
    
    commit 560341ec7fc665e3bc08eebe817eb6b1e20d41df
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Apr 5 11:47:20 2019 +0300
    
        IGNITE-10078 Test deferred queue fix.
    
    commit b9ab5bdde2babd2e52c0d06f36d0900feda0049e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 4 19:04:18 2019 +0300
    
        IGNITE-10078 WAL reader.
    
    commit a55854aa6bda5bbd4c0ae5bd3303c8808ae2a984
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 4 17:51:28 2019 +0300
    
        IGNITE-10078 Added test for scenario when missed update is received after checkpoint.
    
    commit 5bc28b8e392b7202da1cfa96fd53830a96b404c7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Apr 4 17:51:09 2019 +0300
    
        IGNITE-10078 Added test for scenario when missed update is received after checkpoint.
    
    commit 8019f91cf0d4619225dbeb5a20d54d29d4a74d1b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 3 20:15:52 2019 +0300
    
        IGNITE-10078 Fix logical recovery issue with zero counter.
    
    commit 63eff8ed8160b9498cb38e3f64125a237e3de219
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Apr 3 18:47:14 2019 +0300
    
        IGNITE-10078 Use rollbackrecord on logical recovery to avoid unnecessary rebalancing.
    
    commit c0e7d577149fdb65e918af6d3a33423ce7edcb9a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 2 11:54:53 2019 +0300
    
        IGNITE-10078 Fix naming for out-of-order updates.
    
    commit 2f85c2c91ea3726dbcfc73294189db1bd5a28742
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 2 11:44:20 2019 +0300
    
        IGNITE-10078 Pre-create partition only if having valid state.
    
    commit eb911681d38e9ef437428c31e6e304ea22276376
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Apr 2 10:50:24 2019 +0300
    
        IGNITE-10078 Minor.
    
    commit 9eeb2dcc0eb6e6e27139935bcb9e240cfb3b110d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 1 20:18:33 2019 +0300
    
        IGNITE-10078 Added assertion for sequential partition.
    
    commit 46a89b1dd65443d9f9f180e2438cb458a52d3577
    Merge: ab14a8ec9f 963a40b7aa
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 1 14:55:09 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit ab14a8ec9f56d1b97f274f54d0dbbfae66fe7ccd
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 1 14:53:58 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
    
    commit 196b9f2bf83b44e55cfc9f599aedf7b7cd1da25e
    Merge: 4b9301c439 9a9c817f8f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 1 14:53:38 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionsExchangeFuture.java
    
    commit 4b9301c43919c50472f96a1b529eef66b96daeeb
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Apr 1 12:08:11 2019 +0300
    
        IGNITE-10078 Minor code cleanup.
    
    commit 237ffd0b1b9084ec73af395d8a072c1173fec2bf
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Mar 31 19:32:49 2019 +0300
    
        IGNITE-10078 Partition update counter debugging.
    
    commit c832ab48533547cb3179d3b537c7c9061b8b6642
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Mar 24 15:41:46 2019 +0300
    
        IGNITE-10078 Fix update counter overlap issue.
    
    commit 2a8f9dd5366efb2b6a49ef8b066e3971eb3a6d5f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 22 19:22:56 2019 +0300
    
        IGNITE-10078 Cleanup TxPartitionCouterState* tests.
    
    commit 473f7b7a1ea7b0a2ae19272bf482354f5f1309c5
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 22 17:56:30 2019 +0300
    
        IGNITE-10078 Cleanup TxPartitionCouterState* tests.
    
    commit 7021afe4655cc9458401e90475fc6920134f4f4e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 22 16:22:46 2019 +0300
    
        IGNITE-10078 Cleanup TxPartitionCouterState* tests.
    
    commit 6184535280a7365466691cd8831980f7c03f5df5
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Thu Mar 21 23:02:03 2019 +0300
    
        IGNITE-10078 Fix wrong test.
    
    commit 20ab7b99ff73c811a7e354e3cc63a910bdf17eb0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 21 19:09:33 2019 +0300
    
        IGNITE-10078 Fixed test.
    
    commit ebe1e51e286dfb71683fbbf92d7d7733a6df8b29
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 21 16:25:24 2019 +0300
    
        IGNITE-10078 Fixed new record type and gaps shring issues.
    
    commit 556fe9070d7f4a74cd7c08fd6dcb69e0d7787ecf
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 21 11:38:13 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
        #	modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
    
    commit 2c876b7a0063ea3fcff80cbf16dd66b63c825713
    Merge: f802c53c0c b221ab8dd1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 21 11:37:24 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
        #	modules/core/src/test/java/org/apache/ignite/testframework/junits/GridAbstractTest.java
    
    commit f802c53c0c15face1035d3616ca4ec3231dfa171
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Mar 20 19:42:08 2019 +0300
    
        IGNITE-10078 Fixed issues in partition metastore. Removed copypaste.
    
    commit 2400675fe8698dfc364a7efba515a599c07f3351
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 19 20:52:05 2019 +0300
    
        IGNITE-10078 Add partition metastore.
    
    commit 9b1a06b2e24881a34052b73c6266dc61bf8f2bbb
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 19 19:29:39 2019 +0300
    
        IGNITE-10078 Add partition metastore.
    
    commit 59aa188edf14dc51186b457afb309bf032f272b6
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 19 19:13:38 2019 +0300
    
        IGNITE-10078 Add partition metastore.
    
    commit 55c6f7cbba1df258704772264302cd2c6da5b0b0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 18 20:03:50 2019 +0300
    
        IGNITE-10078 Fix wrong (partially) assertion.
    
    commit ac2cff4c742cccd0287434fc06cf02daa7ff418b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 18 14:38:30 2019 +0300
    
        IGNITE-10078 Fix metastore default partition.
    
    commit 725c0ea33a31b8cc87b510dc7f0ecf0e63247f68
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 18 11:27:18 2019 +0300
    
        IGNITE-10078 Minor.
    
    commit 7af2cef3dbae5dd7da355986c321019a836f88e4
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Mon Mar 18 00:32:24 2019 +0300
    
        IGNITE-10078 Fix freelist issue.
    
    commit 0dc46d6296a603c2bba93e6d055f60457cb750e4
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Mar 17 20:50:46 2019 +0300
    
        IGNITE-10078 Fix freelist issue.
    
    commit 73880630a11733cecf80b49b661576bac9e49aff
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 15 19:15:31 2019 +0300
    
        IGNITE-10078 Fix concurrent modification issue for gaps.
    
    commit 7880d721d8543c01cc40ff766c268133fcb02741
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 15 16:11:48 2019 +0300
    
        IGNITE-10078 Code style.
    
    commit 304ea22552a8f545ffbcdea1618a42f56bec9814
    Merge: cff5b4e4d0 ee172d2604
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 15 15:53:13 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
    
    commit cff5b4e4d05bbfcaff84533c6798460c9be12878
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 15 15:43:30 2019 +0300
    
        IGNITE-10078 FreeList refactoring.
    
    commit 70024029c8f27c76ed70d72867c3220ab9c21112
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Mar 15 15:25:47 2019 +0300
    
        IGNITE-10078 FreeList refactoring.
    
    commit 843c84b5994697f3f01d1eb2039680a0c0384139
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 14 20:31:54 2019 +0300
    
        IGNITE-10078 FreeList refactoring.
    
    commit eb6e9a303947d831419fc8cb5750b3e4a38102c6
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 12 20:05:34 2019 +0300
    
        IGNITE-10078 FreeList refactoring.
    
    commit 1530626b8f4836a5f2b6b80a467d25f85a5c468d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 12 14:19:56 2019 +0300
    
        IGNITE-10078 FreeList refactoring.
    
    commit 7feed34f26b467225beb4515d71d0ec97decba0b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 12 14:18:05 2019 +0300
    
        IGNITE-10078 FreeList refactoring.
    
    commit 76d367098ec8854b2c9d0121c1d798bd8f2e3724
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Mon Mar 11 21:52:57 2019 +0300
    
        IGNITE-10078 Refactoring freelist to work with any Storable.
    
    commit b747416898445e5f502f404ba2973867872f1186
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Mon Mar 11 16:51:48 2019 +0300
    
        IGNITE-10078 Added local cache tx test to ignored for MVCC.
    
    commit 20634d470e922fa2a5268cec4be49ddf3e4bf8a9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 11 13:31:58 2019 +0300
    
        IGNITE-10078 Javadoc cleanup.
    
    commit 50394ae2d2b535a4f0757133e4c7508293cd956d
    Merge: 7ace0b5be5 3e80ca44e4
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 11 12:11:46 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit 7ace0b5be5ee5d1026b2fe9fe7267de6ecd38a97
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 11 12:04:08 2019 +0300
    
        IGNITE-10078 Removed local cache tx test from MVCC suite.
    
    commit 96c492bc642fb0ff8bf2efd7fde56c68a3f152a8
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Mar 10 20:58:57 2019 +0300
    
        IGNITE-10078 Fixed mixed local and dht tx.
    
    commit 1e5796cfb4a8718d5d7fca3f7b050dbe99535615
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Fri Mar 8 20:00:10 2019 +0300
    
        IGNITE-10078 Cleaned up tests with all owners restart in the middle of tx.
    
    commit 29b87e76545f9a07b1ea53b01edd3e0f5bb9bf89
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 7 17:18:19 2019 +0300
    
        IGNITE-10078 Added fail all test for history rebalance.
    
    commit 57ff05af9f00cc53ed829329e2b02e4e09c58b38
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 7 17:02:30 2019 +0300
    
        IGNITE-10078 Cleaned up restart all on partial commit test. Property for failing nodes on counter inconsistency.
    
    commit 2b30ec1aaae0b6328a802d801af656ca700afc33
    Merge: 8d39aab13b cfa12b19fa
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Mar 7 10:50:11 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit 8d39aab13b8ac604ea30addd8cb9b13376699f2e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Mar 5 13:17:52 2019 +0300
    
        IGNITE-10078 Change counter generation for mvcc mode to not use reservation mode.
    
    commit 4a57bbdf6037df2c46f8f77cb67d3d42ad4dc76e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 4 19:48:23 2019 +0300
    
        IGNITE-10078 Cleanup.
    
    commit 63ba1379e2edd8bc6f87b3116295326c4674dec3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 4 17:23:05 2019 +0300
    
        IGNITE-10078 Removed unneeded code.
    
    commit 0cd54d08722b12f9d25559c5f54d593a2434dbb0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 4 17:19:11 2019 +0300
    
        IGNITE-10078 Removed unneeded code.
    
    commit f21879a780f9320b4db4a75170473c84122bfee2
    Merge: ed202a97cc 6008a0af5f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Mar 4 16:12:52 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit ed202a97ccc7314ed0f7ab51285ebf2634a93e67
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Mar 3 21:44:20 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 91b5c27ff774e5eba799846fa570bf615c3a68c5
    Merge: 949abb2e01 7100b05d96
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Mar 3 13:20:23 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit 949abb2e01f6154adbd56458a3e5e29361a7ea07
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 28 20:37:53 2019 +0300
    
        IGNITE-10078 removed commons-cli dep, uncommented mvcc tests.
    
    commit b416274f1d4bce8586e157d70d2515054b7efbd9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 28 20:01:37 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedTtlCleanupManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/PartitionCountersNeighborcastFuture.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLocalTxStoreExceptionSelfTest.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite1.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite2.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite3.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite4.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite5.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite6.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite7.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite8.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite9.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
    
    commit 4f9f9db63579129c3039a9c77c1982c95b25c3f9
    Merge: 82869a6569 efc6b96578
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 28 19:36:44 2019 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedTtlCleanupManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryHandler.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/PartitionCountersNeighborcastFuture.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/GridCacheLocalTxStoreExceptionSelfTest.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/IgnitePdsContinuousRestartTest.java
        #	modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/CacheContinuousQueryFailoverAbstractSelfTest.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite1.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite2.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite3.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite4.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite5.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite6.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite7.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite8.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheMvccTestSuite9.java
        #	modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite9.java
    
    commit 82869a65696545370aacb10ce053446694ec809c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 28 17:30:20 2019 +0300
    
        IGNITE-10078 wip.
    
    commit a9a8f60163511a0bc150ee00cff05125b36eb670
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 28 17:27:08 2019 +0300
    
        IGNITE-10078 debugging memleak.
    
    commit 86d1505501b25765cabf50f48b2793317aa181c7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 28 13:39:32 2019 +0300
    
        IGNITE-10078 debugging memleak.
    
    commit 63575fd7e028079058b38c924bbe5baa68663c54
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Wed Feb 27 21:23:25 2019 +0300
    
        IGNITE-10078 wip.
    
    commit fd39008ae713fd024ed2f9104865442a36af3144
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Feb 27 19:03:49 2019 +0300
    
        IGNITE-10078 debugging memleak.
    
    commit 9dc944768108dbef274b3e10268b2be701df53c9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Feb 27 18:58:19 2019 +0300
    
        IGNITE-10078 debugging memleak.
    
    commit 5cc20becc77161e0f59a612721f928b909f609ee
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Wed Feb 27 09:28:12 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 08f4612f2224cbc7df1b947e54b6bcd153c88759
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Wed Feb 27 00:51:29 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 9e8bf896ebbb9079441ab65092c42bd016b164e1
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Mon Feb 25 22:52:46 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 8273eafe35b0f93c57cee20a4bd8a36de0434860
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Feb 25 18:00:25 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 8288ba34a8e41a13ed92f66400aaa6fb29a0fcca
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Feb 24 19:28:57 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 20d052268c27da3492e795cfeb38000921b7c83c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Feb 20 19:30:08 2019 +0300
    
        IGNITE-10078 wip.
    
    commit e48cd10c562d780ce54100be26611ace644fbaa6
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Feb 17 21:34:52 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 7f67efd960e2d5333cf0574f38d2154671c67e13
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Feb 17 18:30:44 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 9fa1128e22be21d88cd0e600232b7aa7148b7dca
    Author: Alexey Scherbakov <al...@gmail.com>
    Date:   Sun Feb 17 18:18:32 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 9b6d2e67c2940579f95cd32b08cc0f75131f883d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Feb 15 19:33:43 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 6ef3ddfe7291aa301090f5466ee44a7e1c475769
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Feb 14 16:41:30 2019 +0300
    
        IGNITE-10078 wip.
    
    commit aedadfbddae26807b3c8710c75bab3f7f5c52f1e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Feb 12 16:00:26 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 5b46f2cd69ed32b12f6745eca46ace1940b82436
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Feb 12 15:54:43 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 3d656cb3d7bcd7bd806c100ace10964585505376
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Feb 11 15:58:09 2019 +0300
    
        IGNITE-10078 wip.
    
    commit f3a9ae136df9c3867887e77da2630a7c88b74340
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 30 19:37:34 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 51b420a3493a39ed1cc890e423f61915a404b9a5
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Jan 28 20:07:52 2019 +0300
    
        IGNITE-10078 wip.
    
    commit d35ba43b4dc18cde5bfb5891b59dd55b38814529
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Jan 25 19:35:43 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 2435f3855692c7e359f752ad6ff734fb3a3eb9c3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Jan 25 12:05:08 2019 +0300
    
        IGNITE-10078 Implement read only counters to avoid unnecessary storage init.
    
    commit 1eab30f08e14bc0c6e519dbd9b1efe97173dd2f1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 23 19:27:35 2019 +0300
    
        IGNITE-10078 wip.
    
    commit e4ac96856ae975856acd5a67761ddd2ebc20ab56
    Merge: b56a1b4422 15cefeabe1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 23 19:27:22 2019 +0300
    
        IGNITE-10078 wip.
    
    commit b56a1b442232358e1754e5ade92bbf4fc2e7ff22
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Jan 20 20:33:03 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 1aaab90c9329910e906eeb3e01eb23381d77adde
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Jan 20 17:35:03 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 7bde03c5a98f229277a98bad76a3bcb95fcb0530
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Jan 14 19:56:15 2019 +0300
    
        IGNITE-10078 wip.
    
    commit e5da1e699595089b9a999ecde134a570ecc50637
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Jan 13 21:30:03 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 18cb5c844aef1230892a42fc034a09546686f96d
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Jan 13 20:11:27 2019 +0300
    
        IGNITE-10078 wip.
    
    commit f2b8509559118fb7ba4d1e9b7fc5d8c069b0e578
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Jan 10 15:12:33 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 6858d29c24610c4d318ce698248ac98aa52034d9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 9 20:11:53 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 06922c86c9d75a85780d3755dacb6a8ebb526c73
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 9 18:19:24 2019 +0300
    
        IGNITE-10078 wip.
    
    commit bd658cdc017d56d0954b90966ccb6577753b7131
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 9 17:58:31 2019 +0300
    
        IGNITE-10078 wip.
    
    commit a6841c4f59a76475b9fafcb75bc5552a0da5e117
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Jan 9 17:41:11 2019 +0300
    
        IGNITE-10078 wip.
    
    commit deb7c1a170c1ed7365523cb42c2c8781d2cf8a4e
    Author: ascherbakoff <al...@gmail.com>
    Date:   Tue Jan 8 21:30:29 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 9375a0c1147b69cf3e5eb959248d9a101a89448e
    Author: ascherbakoff <al...@gmail.com>
    Date:   Mon Jan 7 21:29:29 2019 +0300
    
        IGNITE-10078 wip.
    
    commit dc25d341d50f4dbc49f0afa9f409732d48a96393
    Author: ascherbakoff <al...@gmail.com>
    Date:   Mon Jan 7 21:27:17 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 4aedc9f113e4b1ce81cc22b4812dc343022dd7e8
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sat Jan 5 22:07:32 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 233fa73a5c2dc148433b1c4a62c73daf1643e175
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sat Jan 5 22:07:18 2019 +0300
    
        IGNITE-10078 wip.
    
    commit d349054573203b696f7194fd5d60d20c6136219e
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sat Jan 5 16:45:51 2019 +0300
    
        IGNITE-10078 wip.
    
    commit b4ee593ae7a63b4c2384cca8be6445c402a2c88a
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Jan 4 20:32:57 2019 +0300
    
        IGNITE-10078 wip.
    
    commit c0ac8df87163626e73b5b0a9cc7f9bb81ffefd62
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Jan 4 20:30:52 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 6e31e7379e9597ace892b37c1e9db06be37369db
    Merge: 30393a15f6 bd6bc433bc
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Jan 4 20:20:59 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 30393a15f6e848096490ca50de75ab0ad7f60da8
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Jan 4 19:57:04 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 78b976ba29a54322fb77978dd837d1235f9f956c
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Jan 4 19:55:34 2019 +0300
    
        IGNITE-10078 wip.
    
    commit b71bb041e634da4f5498b483b6cf0b616a310f16
    Author: ascherbakoff <al...@gmail.com>
    Date:   Wed Jan 2 17:16:37 2019 +0300
    
        IGNITE-10078 wip.
    
    commit 4e395780f2e930832ddb6af256bd9960ba34e56e
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Dec 28 16:59:46 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 7f57adc354054d8a176d6abdfa191472d86d23da
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Dec 28 13:26:28 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 78b7b21544ae18dffcbd28e11d870fb4e87e8968
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 19:04:07 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 458b9afdc28e318f6f1e2d84a482e6db56f89e0c
    Merge: 806f33ea32 ca71fe9ba1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 19:03:22 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 806f33ea327669874bfc55a1c3ab8b2f336df05e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 18:23:46 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 0f4ea2945da15b8f05fd8c552ff49ff54bc49083
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 18:13:50 2018 +0300
    
        IGNITE-10078 wip.
    
    commit f6408cb2335bc7a59237c38f405ea7fa18147ea1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 17:33:41 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 804f15318e3c8843125b1c666559fea217df958b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 17:11:22 2018 +0300
    
        IGNITE-10078 wip.
    
    commit bc90e89f9f02116cb481b18067f77cfb1b667333
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 17:06:57 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 70e055bc445e21ecade2674ff3f6afe0778e42f7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 16:27:54 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 5c43d5b85676b9d2e9773aa660fc026b69b76746
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 27 15:50:08 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 77b244819fcd3d60089dcca1919e79a12ade1e2e
    Author: ascherbakoff <al...@gmail.com>
    Date:   Wed Dec 26 23:18:06 2018 +0300
    
        IGNITE-10078 wip.
    
    commit b7aadbe362ff10e12fa45366a95450eb6dfb5139
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 20:58:34 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 5d348022d338643c3fb083f98fe2a3f3e8dd9902
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 20:47:02 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 37056a266068007833ede53dddaad133a5ceda52
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 20:24:36 2018 +0300
    
        IGNITE-10078 wip.
    
    commit a4f05d7c0fd2380694358d7e74ddb86ccc5dafec
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 18:07:28 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ba12e3eea5eba72ad934b8ea9c29c7d9cd620be1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 17:23:32 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 1c2fa92b3038b151e504d22884d53d54c590dc85
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 17:05:27 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 1f5f06922feaa8719e227df9b7258511e21ddcb8
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 13:38:29 2018 +0300
    
        IGNITE-10078 wip.
    
    commit cad6c2bf2180c528486333f0dc4538b3b4f9ff13
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 26 11:45:58 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ba6332c328ea9fe30781e6a5cc1115380a71857b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 20:44:13 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 897a527e79bda8125c777d8fda9a595074c350eb
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 20:40:17 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ef371035ba5ff530e74962846731ec7791aa20ec
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 19:49:26 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 5b24857533ec3fa4b8f6e767cc148e964f5cfe07
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 19:46:40 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 158c1649562469257d7b32c7854d88cfbae46dcc
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 17:15:54 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 1960c4b2e88069210fc2acbb8d49abe46c4328a7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 17:03:40 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ecabcf35bc124ab66b4027096724a994d3cd6b56
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 25 11:56:50 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 0c3d2c9315b0577a878f94a8934f966ce25c6cc9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 24 18:47:13 2018 +0300
    
        IGNITE-10078 wip.
    
    commit d212202a3911dc0c6a99dbb1d67e437b5109452d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 24 18:42:06 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 987b344942483bd3e82f64f5bc47cba7f8cefb52
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 24 15:08:01 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 79dd016d0ac88ae770150033035533d205aac62b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 24 14:53:19 2018 +0300
    
        IGNITE-10078 wip.
    
    commit dc110f0b8f56b72317b9446b4155529fc7dbf37d
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Dec 23 21:25:35 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 39f7e740216e95f4253c0796fa087ee69796748d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 21 20:50:42 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2b668f0d20828c729c5a3536da806d4c43758792
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 21 20:49:19 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ef0da6871a45e313d63ed7c364ea0f21b071b72e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 21 20:48:53 2018 +0300
    
        IGNITE-10078 wip.
    
    commit f213e4ed5d78202aeace9bb8629595709fa6874a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 21 19:11:01 2018 +0300
    
        IGNITE-10078 wip.
    
    commit aaa280fa7016fb7ca63ae55d80c3857e11f09229
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 19 20:03:30 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2ce83a2dcc9cde0428211f85d7eaa7eac5329368
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 19 14:30:35 2018 +0300
    
        IGNITE-10078 wip.
    
    commit e1a19d5d830513a007266908bd24859cd0e79d9c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 18:53:57 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 56c48d55c555579abc28df3257d42a36806dbe80
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 18:38:22 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 8c58d38f3179645b7dc5e00f72212be5f32d3bfb
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 17:56:27 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 8e19369343c50cc49826ee909d4a5bdff6de67a9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 17:06:03 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 9d708ca48cdff7da4a4e04e3ef80807ae3d3005f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 14:34:38 2018 +0300
    
        IGNITE-10078 wip.
    
    commit f3072e0288036c0543dade5909810f0600f669e2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 14:14:52 2018 +0300
    
        IGNITE-10078 Enable one-phase commit.
    
    commit c4b9664a3e6b6ab8229c1c714c16fb8c527e3eb0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 18 14:12:08 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 161ebfa94f91469b1d945f75478661d3defc7831
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 17 19:54:15 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 737b0b31ffff42fd8e33a85ce7b1098f66eae57f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 17 19:43:02 2018 +0300
    
        IGNITE-10078 wip.
    
    commit a7078dffdce93ce40411aa334cdb4c6282b168a4
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 17 18:02:09 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 3f9f79bb7460bbc56f5c6a428d4302e814060f25
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 17 13:43:38 2018 +0300
    
        IGNITE-10078 wip.
    
    commit bdd8eb4ff0358a6359480449536ab6e18dd9d770
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 17 13:35:01 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2916f2a33772fa9d6bf3cd24b8bab26848b7bfb5
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Dec 16 16:17:50 2018 +0300
    
        IGNITE-10078 wip.
    
    commit d3845bce883c741eea9058518e4cef5e6d0e64d9
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Dec 16 15:20:24 2018 +0300
    
        IGNITE-10078 wip.
    
    commit e7258b457f1b571b60bee063790cfbd2bb0d66c1
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 19:56:22 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 1fde3b63ea500b10d5aaf2ba7fc2faa047afb034
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 14:49:35 2018 +0300
    
        IGNITE-10078 wip.
    
    commit f46ae88f3326b2f832e76b4e47a3fa2ad302b275
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 14:36:10 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 61b0c6d1c34a55b8e3c8d38ad6b2e33292809937
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 14:22:39 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2debfa927c8f8b96dc8e31b19e7be1b131b26c5c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 13:41:06 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 909f2a349514ee42694b5b7a9cf1c559c16765b3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 13:11:57 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 7877cb7ff8577ad5fac6361539fa56ef3c82c09b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 13:09:43 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 143339e0d8a985dea90bfe730f1941f3741bfe4b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 14 12:52:10 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 4280181b45cbb19aac66e2011786a299e6af5346
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 20:11:48 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 823eef29dcd530e5afce3f39ec2f74be41ea7b08
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 19:39:32 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2a3ee55e26b2b556827bc32d4854c104343d2766
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 19:18:39 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2976329006cd1d8b7cf5b2d6ae2ab9fe96a0e217
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 18:25:10 2018 +0300
    
        IGNITE-10078 wip.
    
    commit fb92f91ece0e9749b4613a413022b4c447a49e89
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 18:11:13 2018 +0300
    
        IGNITE-10078 wip.
    
    commit c02c54bfe64bf5daa33a31576b95a09128400e44
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 18:04:13 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 0ffdba5b270e2fa9c4a1aab475e13223868f2a7c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 16:28:54 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 9b9cb50a2e6d650b34a7df82cfc80837badbaae3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 16:24:02 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 546275e360c200baf1987f06b5724abc67de8c33
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 16:15:17 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 9cf1280d47202a71e155fc400f5e09fc59313020
    Merge: 96aabe9b7b 2bf085e877
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 14:14:03 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 96aabe9b7bdd6b04198ec958538b94085aa5739b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 13:54:59 2018 +0300
    
        IGNITE-10078 wip.
    
    commit d3bbeddf6eeb02346c301dfa230795e7341de28a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 13 12:55:52 2018 +0300
    
        IGNITE-10078 wip.
    
    commit bf7337235b8aedf05b195ecf1b39486b407eb426
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 20:22:12 2018 +0300
    
        IGNITE-10078 wip.
    
    commit e624147bbe21b026079a2c4da22de06be0afd226
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 20:09:40 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 261ddd48dc4f408443f56b52df84d075765dc615
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 20:01:42 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 4e3c20e9012e83b34414ae9c3222347ba20febc7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 19:57:09 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 17ae851b34c800c3986ce697da30bdc070206daf
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 19:27:44 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 7016b30f0c42e369d713f18b4bffa101ac09626e
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 19:22:25 2018 +0300
    
        IGNITE-10078 wip.
    
    commit c53346f53f0bc5d8fabbb9e831eaa38725a85e62
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 18:51:16 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 6ed9fba3d42113046a9520ce4b910d645734a3f7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 18:41:10 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 98ce00d16c4e73414a51a4ca9486d0e37969b556
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 14:52:17 2018 +0300
    
        IGNITE-10078 wip.
    
    commit fe9719f7e16d35ffcfd448d2622c39e46bd01503
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 14:49:00 2018 +0300
    
        IGNITE-10078 wip.
    
    commit db071ae2add8d687b1604aec42f1e683fc7608bc
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 14:16:26 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2206b0d117c9628a6e13c2785a9cc8cd4f44bcb3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 12 14:06:28 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 67dd96faf96048b1f50bf529fc268f1115e09c68
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 11 19:27:33 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 3951f0baefab993882e2c8d8a1f916a23d89af85
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 11 19:24:45 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 8948804cebb8ff409ef4d700f0d52d589cb4bf16
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 11 18:53:41 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 4d182aa68b3b424390b04d7a1b8bcea79cd0070c
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 11 18:50:35 2018 +0300
    
        IGNITE-10078 wip.
    
    commit beb453b7b22aec136b6d79a0acd3cc7c7899d451
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 18:40:42 2018 +0300
    
        IGNITE-10078 wip.
    
    commit dd05b00f0afbe0a2121091c8e20d10222c6b6f0a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 18:34:03 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 8d3842da5e130a361e52ace7b913933e6eb0d383
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 18:29:14 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 0b904406859e2851ec484022044255d32804e3bd
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 18:25:15 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 7e3df8f06124745c8aedd9f5c32535c98840a1bb
    Merge: 1fc29dc2e0 87510597f5
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 18:20:33 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 1fc29dc2e0514aa20fd08dcf60536eded391faea
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 17:06:42 2018 +0300
    
        IGNITE-10078 wip.
    
    commit a84bd13a5aaeb203d84ef31f14fc967708c697f6
    Merge: 26cbfe770b 4c48ae0ad2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 17:04:25 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 26cbfe770b37bf7d0f946868cc191b37baf94ef8
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 10 16:51:43 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ada58ba6f146b6d556197fd658287173dc197629
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Dec 9 21:25:26 2018 +0300
    
        IGNITE-10078 wip.
    
    commit abb77c3d8167f6e3cbf5890cec8df9d930d802e1
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Dec 9 20:22:31 2018 +0300
    
        IGNITE-10078 wip.
    
    commit baab06840ac6ffeaf322be7ef0bedca6e6f90671
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Dec 7 20:05:45 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 5ce148b0faa5415798a486b4d13fba8003d1623a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 6 18:47:35 2018 +0300
    
        IGNITE-10078 wip.
    
    commit aec4bf2336338bc995993045e3bca194606980ee
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 6 18:46:58 2018 +0300
    
        IGNITE-10078 wip.
    
    commit d3827452dcd0ca23b35b71f619e9beb854943618
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 6 16:45:12 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ae248aae731c429d49b303a7c8dc3adf2cd53943
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 6 15:28:37 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 272e08e602a0b04d966c1800efc142f44d615797
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 6 14:27:21 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 3e747c8d12af9fed9851050c2dfa6d08439d68c0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Dec 6 13:44:13 2018 +0300
    
        IGNITE-10078 wip.
    
    commit a0e6a1d329911d40f68c1cd4b93f9d586878dab0
    Author: ascherbakoff <al...@gmail.com>
    Date:   Wed Dec 5 23:25:06 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 0a0328ee8c4faa2af1d0c31913b12a02d1d3fef5
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 5 20:10:54 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 424855918b0f3c634b410fe4b90af0b3235a627a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 5 20:10:11 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 39770b9ff5b273e80084306f5173f7496e9c29b0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Dec 5 19:09:15 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 8cadd0666822420b1601f0acda3bf072296824a9
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 4 20:21:38 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 5446ac7ef00b9521da9c586d72fd3a42d6a722b7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Dec 4 20:10:41 2018 +0300
    
        IGNITE-10078 wip.
    
    commit f45b1a5cefe332d538a1c505b9d1840d359da8a2
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 3 19:58:33 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 191cf44411a230099bbe0ff97c589d3a617bae18
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 3 19:55:52 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 68598271b519dc409680f86d5e303eaa24b91b3f
    Merge: 0448651174 826cc5573f
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Dec 3 10:59:15 2018 +0300
    
        Merge branch 'ignite-10078' of https://github.com/gridgain/apache-ignite into ignite-10078
    
    commit 826cc5573f42daefa4914beba9d37df6d5e8faf8
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Dec 2 22:13:19 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 603930c315e3b1999de0313e8ad2a4114bfa8cbe
    Merge: 7a42fdbd82 2a0e354260
    Author: ascherbakoff <al...@gmail.com>
    Date:   Fri Nov 30 12:05:35 2018 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit 0448651174c41d33a10df44add94c25cd62d6b2d
    Merge: 7a42fdbd82 0e34042816
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Nov 28 16:52:10 2018 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit 7a42fdbd821c2e57c3edff16cca719e17505b68d
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Nov 22 13:21:36 2018 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionUpdateCounter.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
    
    commit e9d52f94e9cb76f620bd9700aa1e7925f0a95842
    Merge: a666157dc4 d81acdc29a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Nov 22 13:14:10 2018 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
        # Conflicts:
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupContext.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCachePartitionExchangeManager.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionUpdateCounter.java
        #	modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
    
    commit a666157dc488c96fb5758daee24101c579e57b19
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Nov 21 19:10:03 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 06aba801780def39cb00c23a3467a534f44b0b84
    Author: ascherbakoff <al...@gmail.com>
    Date:   Wed Nov 21 01:31:03 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 0b28c3d6da4285724116213d0b433160f62324cd
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Nov 19 17:05:32 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 29eee5c2c2790ff474957760973dd617a0584a61
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Nov 19 17:02:32 2018 +0300
    
        IGNITE-10078 wip.
    
    commit aacff35491a07f93ea844e360f774a0708b155c8
    Merge: 97aba597a9 355ce6fe88
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Nov 19 16:55:38 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 97aba597a95ea4dc5e2684dfb7bc68c23e3a98d0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Nov 19 16:49:39 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 30a8f30eef9d76d5aa72f2bc43882c7e47f5d221
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Nov 19 16:20:16 2018 +0300
    
        IGNITE-10078 wip.
    
    commit d176c8b30c303f19b5beeff640e7ed5b961f81c3
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Mon Nov 19 13:34:39 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 22436a83dc0993461c08155b9e68b612dda3360a
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Nov 18 18:37:16 2018 +0300
    
        IGNITE-10078 wip.
    
    commit ef7a7857fc00bebe9fc9289ecdb687c43973e947
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Nov 18 17:43:15 2018 +0300
    
        IGNITE-10078 wip.
    
    commit e721aba13512433caf9b6420e00dfef76dce3c47
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Nov 18 17:31:20 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2efba0a10b64490ac435e0157a5eeca8c2712ecc
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Nov 18 17:12:14 2018 +0300
    
        IGNITE-10078 wip.
    
    commit e210e1cb26e774625252b1a2f11d295f80f48351
    Author: ascherbakoff <al...@gmail.com>
    Date:   Sun Nov 18 17:06:45 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 63d7ab771d664eafc1e5f18f7a2f51500113316b
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Fri Nov 16 18:51:59 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 2317758268879c647d80367f44c121645713de78
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Nov 14 17:45:49 2018 +0300
    
        IGNITE-10078 wip.
    
    commit 64d7e2d2a235fb822b68ca21f02d85f43f88b019
    Merge: defbcc167a 2439ade4d6
    Author: ascherbakoff <al...@gmail.com>
    Date:   Mon Nov 12 22:42:43 2018 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit defbcc167aaf358991c2874b2b0d600b6248f921
    Author: ascherbakoff <al...@gmail.com>
    Date:   Mon Nov 12 22:41:57 2018 +0300
    
        IGNITE-10078 wip.
    
    commit a43e5876cb81bb00fdbccc004d28e9c4ceda475a
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Tue Nov 6 10:31:38 2018 +0300
    
        IGNITE-10078 wip.
    
    commit beb579c83782ef4d2be91a29c43b99410e4f8d6d
    Merge: 253df1059b 6f9c702cd7
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Thu Nov 1 12:51:24 2018 +0300
    
        Merge branch 'master' of https://github.com/apache/ignite into ignite-10078
    
    commit 253df1059b0ad0f003a711e63007df7b2e1b77c0
    Author: Aleksei Scherbakov <al...@gmail.com>
    Date:   Wed Oct 31 18:38:07 2018 +0300
    
        IGNITE-10078 wip.
    
    Signed-off-by: Anton Vinogradov <av...@apache.org>
---
 .../org/apache/ignite/IgniteSystemProperties.java  |    7 +
 .../src/main/java/org/apache/ignite/Ignition.java  |    2 +-
 .../apache/ignite/internal/pagemem/FullPageId.java |    6 +-
 .../pagemem/wal/record/RollbackRecord.java         |  115 +++
 .../internal/pagemem/wal/record/WALRecord.java     |    8 +-
 .../delta/MetaPageUpdatePartitionDataRecord.java   |   38 +-
 .../delta/MetaPageUpdatePartitionDataRecordV2.java |  103 ++
 .../wal/record/delta/PartitionMetaStateRecord.java |    8 +-
 .../processors/affinity/AffinityAssignment.java    |    2 +-
 .../affinity/GridAffinityAssignmentCache.java      |   14 +-
 .../cache/CacheAffinitySharedManager.java          |  334 ++----
 .../processors/cache/CacheEntryInfoCollection.java |   19 +
 .../processors/cache/CacheGroupContext.java        |   13 +
 .../processors/cache/GridCacheMapEntry.java        |   51 +-
 .../cache/GridCachePartitionExchangeManager.java   |   10 +-
 .../processors/cache/GridCacheProcessor.java       |   32 +-
 .../cache/GridCacheSharedTtlCleanupManager.java    |    9 +-
 .../internal/processors/cache/GridCacheUtils.java  |    3 +-
 .../cache/IgniteCacheOffheapManager.java           |   61 +-
 .../cache/IgniteCacheOffheapManagerImpl.java       |   94 +-
 .../cache/PartitionAtomicUpdateCounterImpl.java    |  155 +++
 ....java => PartitionMvccTxUpdateCounterImpl.java} |   41 +-
 .../PartitionTxUpdateCounterDebugWrapper.java      |  201 ++++
 .../cache/PartitionTxUpdateCounterImpl.java        |  450 ++++++++
 .../processors/cache/PartitionUpdateCounter.java   |  223 ++--
 .../internal/processors/cache/WalStateManager.java |   10 +-
 .../GridDistributedTxRemoteAdapter.java            |    3 +-
 .../cache/distributed/dht/GridDhtCacheEntry.java   |   28 +-
 .../distributed/dht/GridDhtTxFinishFuture.java     |    9 +-
 .../distributed/dht/GridDhtTxFinishRequest.java    |    7 -
 .../distributed/dht/GridDhtTxPrepareFuture.java    |   35 +-
 .../dht/PartitionUpdateCountersMessage.java        |   24 +
 .../dht/preloader/GridDhtPartitionDemander.java    |    6 +-
 .../dht/preloader/GridDhtPartitionSupplier.java    |    9 +
 .../preloader/GridDhtPartitionsExchangeFuture.java |   75 +-
 .../dht/preloader/GridDhtPreloader.java            |    8 +-
 .../dht/preloader/GridDhtPreloaderAssignments.java |    3 +-
 .../preloader/IgniteDhtDemandedPartitionsMap.java  |    3 +
 .../dht/topology/GridClientPartitionTopology.java  |    7 +-
 .../dht/topology/GridDhtLocalPartition.java        |  121 ++-
 .../dht/topology/GridDhtPartitionTopology.java     |   12 +-
 .../dht/topology/GridDhtPartitionTopologyImpl.java |  120 ++-
 .../dht/topology/PartitionsEvictManager.java       |   52 +-
 .../GridNearOptimisticTxPrepareFutureAdapter.java  |    6 +-
 .../cache/distributed/near/GridNearTxLocal.java    |   15 +-
 .../processors/cache/persistence/CacheDataRow.java |    8 +
 .../cache/persistence/CacheDataRowAdapter.java     |    2 +-
 .../GridCacheDatabaseSharedManager.java            |   78 +-
 .../cache/persistence/GridCacheOffheapManager.java |  365 +++++--
 .../processors/cache/persistence/RowStore.java     |    6 +
 .../processors/cache/persistence/Storable.java     |    7 +
 .../persistence/checkpoint/CheckpointEntry.java    |    3 +-
 .../persistence/freelist/AbstractFreeList.java     |   37 +-
 .../persistence/freelist/CacheFreeListImpl.java    |   10 +-
 .../cache/persistence/freelist/PagesList.java      |   14 +-
 .../SimpleDataRow.java}                            |   82 +-
 .../cache/persistence/metastorage/MetaStorage.java |  133 +--
 .../metastorage/MetastorageDataRow.java            |   64 +-
 .../metastorage/MetastorageRowStore.java           |   32 +-
 .../MetastoreDataPageIO.java}                      |   39 +-
 .../PartitionMetaStorage.java}                     |   33 +-
 .../partstorage/PartitionMetaStorageImpl.java      |  140 +++
 .../cache/persistence/tree/BPlusTree.java          |    1 +
 .../persistence/tree/io/AbstractDataPageIO.java    |    1 -
 .../cache/persistence/tree/io/PageIO.java          |    7 +
 .../persistence/tree/io/PagePartitionMetaIO.java   |   34 +
 .../persistence/tree/io/PagePartitionMetaIOV2.java |   51 +-
 .../persistence/tree/io/SimpleDataPageIO.java      |   35 +-
 .../cache/persistence/tree/util/PageHandler.java   |    3 +-
 .../persistence/wal/FileWriteAheadLogManager.java  |   19 +-
 .../cache/persistence/wal/record/RecordTypes.java  |    1 +
 .../wal/serializer/RecordDataV1Serializer.java     |   37 +-
 .../wal/serializer/RecordDataV2Serializer.java     |   22 +
 .../CacheContinuousQueryEventBuffer.java           |   14 +-
 .../continuous/CacheContinuousQueryHandler.java    |    6 +-
 .../cache/transactions/IgniteTxEntry.java          |   19 +
 .../cache/transactions/IgniteTxHandler.java        |   88 +-
 .../cache/transactions/IgniteTxLocalAdapter.java   |   34 +-
 .../cache/transactions/IgniteTxManager.java        |    4 +-
 .../processors/cache/transactions/TxCounters.java  |   25 +-
 .../cluster/GridClusterStateProcessor.java         |    3 +-
 .../ignite/internal/stat/IoStatisticsHolder.java   |    1 -
 .../apache/ignite/internal/util/IgniteTree.java    |    5 +-
 .../apache/ignite/internal/util/IgniteUtils.java   |   62 +-
 modules/core/src/test/config/log4j-test.xml        |    9 +
 .../ignite/cache/ResetLostPartitionTest.java       |    5 +-
 .../internal/TestRecordingCommunicationSpi.java    |   55 +-
 .../processors/cache/IgniteCacheGroupsTest.java    |   16 +-
 .../CachePartitionLossDetectionOnNodeLeftTest.java |  115 +++
 .../distributed/CacheRentingStateRepairTest.java   |   25 +-
 .../GridCacheRebalancingWithAsyncClearingTest.java |   12 +-
 .../IgnitePdsCacheRebalancingAbstractTest.java     |    4 +
 .../IgnitePdsContinuousRestartTest.java            |   10 +
 ...tePdsContinuousRestartTestWithExpiryPolicy.java |    1 +
 .../persistence/IgnitePdsCorruptedStoreTest.java   |   40 +-
 .../db/IgnitePdsPartitionPreloadTest.java          |   19 +-
 .../persistence/db/wal/IgniteWalRebalanceTest.java |    2 +-
 .../db/wal/IgniteWalSerializerVersionTest.java     |    2 +-
 .../db/wal/WalRecoveryTxLogicalRecordsTest.java    |  162 ++-
 .../transactions/PartitionUpdateCounterTest.java   |  425 ++++++++
 .../TxLocalDhtMixedCacheModesTest.java             |   86 ++
 .../TxPartitionCounterStateAbstractTest.java       | 1081 ++++++++++++++++++++
 ...ounterStateConsistencyHistoryRebalanceTest.java |  191 ++++
 .../TxPartitionCounterStateConsistencyTest.java    |  490 +++++++++
 ...ateOnePrimaryOneBackupHistoryRebalanceTest.java |   41 +
 ...rtitionCounterStateOnePrimaryOneBackupTest.java |  491 +++++++++
 ...imaryTwoBackupsFailAllHistoryRebalanceTest.java |  100 ++
 ...ounterStateOnePrimaryTwoBackupsFailAllTest.java |  352 +++++++
 ...teOnePrimaryTwoBackupsHistoryRebalanceTest.java |   64 ++
 ...titionCounterStateOnePrimaryTwoBackupsTest.java |  913 +++++++++++++++++
 .../TxPartitionCounterStatePutTest.java            |  289 ++++++
 ...titionCounterStateTwoPrimaryTwoBackupsTest.java |  225 ++++
 .../TxPartitionCounterStateWithFilterTest.java     |  200 ++++
 .../cache/transactions/TxRollbackAsyncTest.java    |    2 +-
 .../testframework/junits/GridAbstractTest.java     |    3 +
 .../ignite/testframework/junits/Repeat.java}       |   51 +-
 .../ignite/testframework/junits/RepeatRule.java    |   67 ++
 .../junits/common/GridCommonAbstractTest.java      |  196 +++-
 .../testsuites/IgniteCacheMvccTestSuite6.java      |    4 +
 .../testsuites/IgniteCacheMvccTestSuite9.java      |   22 +
 .../ignite/testsuites/IgniteCacheTestSuite6.java   |    6 +
 .../ignite/testsuites/IgniteCacheTestSuite9.java   |   24 +
 .../processors/cache/CacheIndexStreamerTest.java   |    3 +
 123 files changed, 8591 insertions(+), 1289 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
index 800ca56..a8916b8 100644
--- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
+++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java
@@ -1145,6 +1145,13 @@ public final class IgniteSystemProperties {
     public static final String IGNITE_DIAGNOSTIC_WARN_LIMIT = "IGNITE_DIAGNOSTIC_WARN_LIMIT";
 
     /**
+     * Flag to enable triggering failure handler for node if unrecoverable partition inconsistency is
+     * discovered during partition update counters exchange.
+     */
+    public static final String IGNITE_FAIL_NODE_ON_UNRECOVERABLE_PARTITION_INCONSISTENCY =
+        "IGNITE_FAIL_NODE_ON_UNRECOVERABLE_PARTITION_INCONSISTENCY";
+
+    /**
      * Allow use composite _key, _val columns at the INSERT/UPDATE/MERGE statements.
      */
     public static final String IGNITE_SQL_ALLOW_KEY_VAL_UPDATES = "IGNITE_SQL_ALLOW_KEY_VAL_UPDATES";
diff --git a/modules/core/src/main/java/org/apache/ignite/Ignition.java b/modules/core/src/main/java/org/apache/ignite/Ignition.java
index 4c7a491..dd0d796 100644
--- a/modules/core/src/main/java/org/apache/ignite/Ignition.java
+++ b/modules/core/src/main/java/org/apache/ignite/Ignition.java
@@ -583,4 +583,4 @@ public class Ignition {
 
         return TcpIgniteClient.start(cfg);
     }
-}
\ No newline at end of file
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/FullPageId.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/FullPageId.java
index 9e24943..17c552d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/FullPageId.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/FullPageId.java
@@ -40,10 +40,10 @@ import org.apache.ignite.internal.util.typedef.internal.U;
  * <p>
  * <h3>Page ID rotation</h3>
  * There are scenarios when we reference one page (B) from within another page (A) by page ID. It is also
- * possible that this first page (B) is de-allocated and allocated again for a different purpose. In this
- * case we should have a mechanism to determine that page (B) cannot be used after reading it's ID in page (A).
+ * possible that this first page (B) is concurrently reused for a different purpose. In this
+ * case we should have a mechanism to determine that the reference from page (A) to page (B) is no longer valid.
  * This is ensured by page ID rotation - together with page's (B) ID we should write some value that is incremented
- * each time a page is de-allocated (page ID rotation). This ID should be verified after page read and a page
+ * each time a page is reused (page ID rotation). This ID should be verified after page read and a page
  * should be discarded if full ID is different.
  * <p>
  * Effective page ID is page ID with zeroed bits used for page ID rotation.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/RollbackRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/RollbackRecord.java
new file mode 100644
index 0000000..fd6a00e
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/RollbackRecord.java
@@ -0,0 +1,115 @@
+/*
+ * 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.pagemem.wal.record;
+
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Rollback record. Used to close gap in partition update sequence on tx rollback.
+ */
+public class RollbackRecord extends WALRecord {
+    /** Cache ID. */
+    @GridToStringInclude
+    protected int grpId;
+
+    /** Partition ID. */
+    @GridToStringInclude
+    protected int partId;
+
+    /** Rollback start. */
+    @GridToStringInclude
+    protected long start;
+
+    /** Rollback range. */
+    @GridToStringInclude
+    protected long range;
+
+    /**
+     * @param grpId Group id.
+     * @param partId Partition id.
+     * @param start Start.
+     * @param range Range.
+     */
+    public RollbackRecord(int grpId, int partId, long start, long range) {
+        this.grpId = grpId;
+        this.partId = partId;
+        this.start = start;
+        this.range = range;
+    }
+
+    /**
+     * @return Cache ID.
+     */
+    public int groupId() {
+        return grpId;
+    }
+
+    /**
+     * @return Partition ID.
+     */
+    public int partitionId() {
+        return partId;
+    }
+
+    /**
+     * @return Rollback start.
+     */
+    public long start() {
+        return start;
+    }
+
+    /**
+     * @return Rollback range.
+     */
+    public long range() {
+        return range;
+    }
+
+    /**
+     * Returns a number of overlapping update counters.
+     *
+     * @param from From counter (not inclusive).
+     * @param to To counter (inclusive).
+     */
+    public long overlap(long from, long to) {
+        long to0 = start + range;
+
+        // from lies within (start, to0]
+        if (start <= from && from < to0)
+            return Math.min(to0 - from, to - from);
+
+        // start lies within (from, to]
+        if (from <= start && start < to)
+            return Math.min(to - start, range);
+
+        return 0;
+    }
+
+
+
+    /** {@inheritDoc} */
+    @Override public RecordType type() {
+        return RecordType.ROLLBACK_TX_RECORD;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(RollbackRecord.class, this);
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
index 5d72768..9cf2613 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/WALRecord.java
@@ -208,7 +208,13 @@ public abstract class WALRecord {
         MVCC_DATA_RECORD (LOGICAL),
 
         /** Mvcc Tx state change record. */
-        MVCC_TX_RECORD (LOGICAL);
+        MVCC_TX_RECORD (LOGICAL),
+
+        /** Rollback tx record. */
+        ROLLBACK_TX_RECORD (LOGICAL),
+
+        /** */
+        PARTITION_META_PAGE_UPDATE_COUNTERS_V2 (PHYSICAL);
 
         /**
          * When you're adding a new record don't forget to choose record purpose explicitly
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 28294a9..3e2b67b 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
@@ -17,7 +17,11 @@
 
 package org.apache.ignite.internal.pagemem.wal.record.delta;
 
+import java.io.DataInput;
+import java.io.IOException;
+import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
 import org.apache.ignite.internal.util.typedef.internal.S;
@@ -57,8 +61,7 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
         int partSize,
         long cntrsPageId,
         byte state,
-        int allocatedIdxCandidate
-    ) {
+        int allocatedIdxCandidate) {
         super(grpId, pageId);
 
         this.updateCntr = updateCntr;
@@ -70,6 +73,20 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
     }
 
     /**
+     * @param in Input.
+     */
+    public MetaPageUpdatePartitionDataRecord(DataInput in) throws IOException{
+        super(in.readInt(), in.readLong());
+
+        this.updateCntr = in.readLong();
+        this.globalRmvId = in.readLong();
+        this.partSize = in.readInt();
+        this.cntrsPageId = in.readLong();
+        this.state = in.readByte();
+        this.allocatedIdxCandidate = in.readInt();
+    }
+
+    /**
      * @return Update counter.
      */
     public long updateCounter() {
@@ -123,6 +140,21 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
         return allocatedIdxCandidate;
     }
 
+    /**
+     * @param buf Buffer.
+     */
+    public void toBytes(ByteBuffer buf) {
+        buf.putInt(groupId());
+        buf.putLong(pageId());
+
+        buf.putLong(updateCounter());
+        buf.putLong(globalRemoveId());
+        buf.putInt(partitionSize());
+        buf.putLong(countersPageId());
+        buf.put(state());
+        buf.putInt(allocatedIndexCandidate());
+    }
+
     /** {@inheritDoc} */
     @Override public RecordType type() {
         return RecordType.PARTITION_META_PAGE_UPDATE_COUNTERS;
@@ -130,6 +162,6 @@ public class MetaPageUpdatePartitionDataRecord extends PageDeltaRecord {
 
     /** {@inheritDoc} */
     @Override public String toString() {
-        return S.toString(MetaPageUpdatePartitionDataRecord.class, this, "super", super.toString());
+        return S.toString(MetaPageUpdatePartitionDataRecord.class, this, "partId", PageIdUtils.partId(pageId()), "super", super.toString());
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecordV2.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecordV2.java
new file mode 100644
index 0000000..ab3ccf8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/MetaPageUpdatePartitionDataRecordV2.java
@@ -0,0 +1,103 @@
+/*
+ * 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.pagemem.wal.record.delta;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIOV2;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+public class MetaPageUpdatePartitionDataRecordV2 extends MetaPageUpdatePartitionDataRecord {
+    /** */
+    private long link;
+
+    /**
+     * @param grpId Group id.
+     * @param pageId Page id.
+     * @param updateCntr Update counter.
+     * @param globalRmvId Global remove id.
+     * @param partSize Partition size.
+     * @param cntrsPageId Cntrs page id.
+     * @param state State.
+     * @param allocatedIdxCandidate Allocated index candidate.
+     * @param link Link.
+     */
+    public MetaPageUpdatePartitionDataRecordV2(
+        int grpId,
+        long pageId,
+        long updateCntr,
+        long globalRmvId,
+        int partSize,
+        long cntrsPageId,
+        byte state,
+        int allocatedIdxCandidate,
+        long link) {
+        super(grpId, pageId, updateCntr, globalRmvId, partSize, cntrsPageId, state, allocatedIdxCandidate);
+        this.link = link;
+    }
+
+    /**
+     * @param in Input.
+     */
+    public MetaPageUpdatePartitionDataRecordV2(DataInput in) throws IOException {
+        super(in);
+
+        this.link = in.readLong();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void applyDelta(PageMemory pageMem, long pageAddr) throws IgniteCheckedException {
+        super.applyDelta(pageMem, pageAddr);
+
+        PagePartitionMetaIOV2 io = (PagePartitionMetaIOV2)PagePartitionMetaIO.VERSIONS.forPage(pageAddr);
+
+        io.setGapsLink(pageAddr, link);
+    }
+
+    /**
+     *
+     */
+    public long link() {
+        return link;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void toBytes(ByteBuffer buf) {
+        super.toBytes(buf);
+
+        buf.putLong(link());
+    }
+
+    /** {@inheritDoc} */
+    @Override public RecordType type() {
+        return RecordType.PARTITION_META_PAGE_UPDATE_COUNTERS_V2;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(MetaPageUpdatePartitionDataRecordV2.class, this, "partId", PageIdUtils.partId(pageId()), "super", super.toString());
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/PartitionMetaStateRecord.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/PartitionMetaStateRecord.java
index e1e74c4..399ffc2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/PartitionMetaStateRecord.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/record/delta/PartitionMetaStateRecord.java
@@ -35,12 +35,14 @@ public class PartitionMetaStateRecord extends WALRecord implements WalRecordCach
     /** Partition id. */
     private final int partId;
 
-    /** Update counter. */
+    /** @deprecated Update counter. */
     private final long updateCounter;
 
     /**
      * @param grpId Cache group ID.
-     * @param state Page ID.
+     * @param partId Partition ID.
+     * @param state State.
+     * @param updateCounter Update counter.
      */
     public PartitionMetaStateRecord(int grpId, int partId, GridDhtPartitionState state, long updateCounter) {
         this.grpId = grpId;
@@ -74,7 +76,7 @@ public class PartitionMetaStateRecord extends WALRecord implements WalRecordCach
     }
 
     /**
-     *
+     * @return Rollback counter.
      */
     public long updateCounter() {
         return updateCounter;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
index b8b1089..502672c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/AffinityAssignment.java
@@ -74,7 +74,7 @@ public interface AffinityAssignment {
     public Collection<UUID> getIds(int part);
 
     /**
-     * @return Nodes having parimary and backup assignments.
+     * @return Nodes having primary and backup assignments.
      */
     public Set<ClusterNode> nodes();
 
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 7c2b1dc..53664b8 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
@@ -352,22 +352,22 @@ public class GridAffinityAssignmentCache {
             if (hasBaseline && changedBaseline) {
                 recalculateBaselineAssignment(topVer, events, prevAssignment, sorted, blt);
 
-                assignment = IdealAffinityAssignment.createWithPreservedPrimaries(
+                assignment = IdealAffinityAssignment.create(
                     topVer,
-                    baselineAssignmentWithoutOfflineNodes(topVer),
-                    baselineAssignment
+                    sorted,
+                    baselineAssignmentWithoutOfflineNodes(topVer)
                 );
             }
             else if (skipCalculation)
                 assignment = prevAssignment;
-            else if (hasBaseline && !changedBaseline) {
+            else if (hasBaseline) {
                 if (baselineAssignment == null)
                     recalculateBaselineAssignment(topVer, events, prevAssignment, sorted, blt);
 
-                assignment = IdealAffinityAssignment.createWithPreservedPrimaries(
+                assignment = IdealAffinityAssignment.create(
                     topVer,
-                    baselineAssignmentWithoutOfflineNodes(topVer),
-                    baselineAssignment
+                    sorted,
+                    baselineAssignmentWithoutOfflineNodes(topVer)
                 );
             }
             else {
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 9a80d8a..7bdb602 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
@@ -269,7 +269,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         CacheAffinityChangeMessage msg = null;
 
         synchronized (mux) {
-            if (waitInfo == null || !waitInfo.topVer.equals(lastAffVer) )
+            if (waitInfo == null || !waitInfo.topVer.equals(lastAffVer))
                 return;
 
             Map<Integer, UUID> partWait = waitInfo.waitGrps.get(checkGrpId);
@@ -324,7 +324,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      */
     public Set<Integer> waitGroups() {
         synchronized (mux) {
-            if (waitInfo == null || !waitInfo.topVer.equals(lastAffVer) )
+            if (waitInfo == null || !waitInfo.topVer.equals(lastAffVer))
                 return Collections.emptySet();
 
             return new HashSet<>(waitInfo.waitGrps.keySet());
@@ -332,6 +332,21 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     }
 
     /**
+     * @param grpId Group id.
+     * @param part Part.
+     * @param node Node.
+     * @param topVer Topology version.
+     */
+    public void addToWaitGroup(int grpId, int part, UUID node, AffinityTopologyVersion topVer) {
+        synchronized (mux) {
+            if (waitInfo == null)
+                waitInfo = new WaitRebalanceInfo(topVer);
+
+            waitInfo.add(grpId, part, node, null);
+        }
+    }
+
+    /**
      * @param waitInfo Cache rebalance information.
      * @return Message.
      */
@@ -495,6 +510,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                             clientTop.fullUpdateCounters(),
                             Collections.<Integer>emptySet(),
                             null,
+                            null,
                             null
                         );
                     }
@@ -560,7 +576,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                     -1,
                     false);
 
-                grp.topology().update(topVer, partMap, null, Collections.emptySet(), null, null);
+                grp.topology().update(topVer, partMap, null, Collections.emptySet(), null, null, null);
 
                 topFut.validate(grp, discoCache.allNodes());
             }
@@ -791,8 +807,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     }
 
     /**
-     * Called during the rollback of the exchange partitions procedure
-     * in order to stop the given cache even if it's not fully initialized (e.g. failed on cache init stage).
+     * Called during the rollback of the exchange partitions procedure in order to stop the given cache even if it's not
+     * fully initialized (e.g. failed on cache init stage).
      *
      * @param fut Exchange future.
      * @param crd Coordinator flag.
@@ -803,7 +819,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         boolean crd,
         final ExchangeActions exchActions
     ) {
-        assert exchActions != null && !exchActions.empty() && exchActions.cacheStartRequests().isEmpty(): exchActions;
+        assert exchActions != null && !exchActions.empty() && exchActions.cacheStartRequests().isEmpty() : exchActions;
 
         IgniteInternalFuture<?> res = cachesRegistry.update(exchActions);
 
@@ -1613,182 +1629,15 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @throws IgniteCheckedException If failed.
      */
     public Map<Integer, CacheGroupAffinityMessage> onServerLeftWithExchangeMergeProtocol(
-        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException
-    {
-        final ExchangeDiscoveryEvents evts = fut.context().events();
-
-        assert fut.context().mergeExchanges();
-        assert evts.hasServerLeft();
-
-        if (evts.hasServerLeft() && evts.hasServerJoin())
-            return onReassignmentEnforced(fut);
-        else
-            return onServerLeftWithExchangeMergeProtocolLightweight(fut);
-    }
-
-    /**
-     * @param fut Current exchange future.
-     * @return Computed difference with ideal affinity.
-     * @throws IgniteCheckedException If failed.
-     */
-    private Map<Integer, CacheGroupAffinityMessage> onServerLeftWithExchangeMergeProtocolLightweight(
-        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException
-    {
+        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException {
         final ExchangeDiscoveryEvents evts = fut.context().events();
-        final AffinityTopologyVersion topVer = evts.topologyVersion();
 
         assert fut.context().mergeExchanges();
         assert evts.hasServerLeft();
 
-        final WaitRebalanceInfo waitRebalanceInfo =
-            new WaitRebalanceInfo(fut.context().events().lastServerEventVersion());
-
-        final Map<Integer, Map<Integer, List<Long>>> diff = new ConcurrentHashMap<>();
-
-        final Set<ClusterNode> aliveNodes = new HashSet<>(fut.context().events().discoveryCache().serverNodes());
-
-        forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
-            @Override public void applyx(CacheGroupDescriptor desc) throws IgniteCheckedException {
-                AffinityTopologyVersion topVer = evts.topologyVersion();
-
-                CacheGroupHolder grpHolder = getOrCreateGroupHolder(topVer, desc);
-
-                IdealAffinityAssignment idealAssignment = grpHolder.affinity().calculate(topVer, evts, evts.discoveryCache());
-
-                if (!grpHolder.rebalanceEnabled || fut.cacheGroupAddedOnExchange(desc.groupId(), desc.receivedFrom())) {
-                    grpHolder.affinity().initialize(topVer, idealAssignment.assignment());
-
-                    return;
-                }
-
-                AffinityTopologyVersion affTopVer = grpHolder.affinity().lastVersion();
-
-                List<List<ClusterNode>> curAssignment = grpHolder.affinity().assignments(affTopVer);
-
-                assert curAssignment != null;
-
-                List<List<ClusterNode>> newAssignment = new ArrayList<>(idealAssignment.assignment());
-
-                GridDhtPartitionTopology top = grpHolder.topology(fut.context().events().discoveryCache());
-
-                BitSet processedPartitions = new BitSet(curAssignment.size());
-
-                Map<Integer, List<Long>> cacheAffinityDiff = new HashMap<>();
-
-                for (ClusterNode leftNode : evts.leftServerNodes()) {
-                    for (int p : idealAssignment.idealPrimaries(leftNode)) {
-                        List<ClusterNode> curOwners = curAssignment.get(p);
-
-                        if (curOwners.isEmpty())
-                            continue;
-
-                        List<ClusterNode> idealOwners = idealAssignment.assignment().get(p);
-
-                        List<ClusterNode> newOwners = null;
-
-                        if (idealOwners.isEmpty())
-                            newOwners = selectCurrentAliveOwners(aliveNodes, curOwners);
-                        else if (curOwners.get(0).equals(leftNode))
-                            newOwners = selectPrimaryTopologyOwnerFromIdealAssignment(
-                                grpHolder.aff,
-                                p,
-                                top,
-                                idealOwners,
-                                waitRebalanceInfo
-                            );
-                        else if (!curOwners.get(0).equals(idealOwners.get(0)))
-                            newOwners = latePrimaryAssignment(
-                                grpHolder.aff,
-                                p,
-                                curOwners.get(0),
-                                idealOwners,
-                                waitRebalanceInfo
-                            );
-
-                        if (newOwners != null) {
-                            newAssignment.set(p, newOwners);
-
-                            List<Long> clusterNodesAsOrder = newOwners.stream()
-                                .map(NODE_TO_ORDER::apply)
-                                .collect(Collectors.toList());
-
-                            cacheAffinityDiff.put(p, clusterNodesAsOrder);
-
-                            processedPartitions.set(p);
-                        }
-                    }
-                }
-
-                Set<Integer> partitionsWithChangedPrimary = grpHolder.affinity().partitionPrimariesDifferentToIdeal(affTopVer);
-
-                // We need to re-check partitions for further correct late affinity assignment
-                // where primary node is not as in ideal assignment.
-                for (int p : partitionsWithChangedPrimary) {
-                    if (processedPartitions.get(p))
-                        continue;
-
-                    List<ClusterNode> curOwners = curAssignment.get(p);
-
-                    if (curOwners.isEmpty())
-                        continue;
-
-                    List<ClusterNode> idealOwners = idealAssignment.assignment().get(p);
-
-                    List<ClusterNode> newOwners = null;
-
-                    if (idealOwners.isEmpty())
-                        newOwners = selectCurrentAliveOwners(aliveNodes, curOwners);
-                    else if (!aliveNodes.contains(curOwners.get(0)))
-                        newOwners = selectPrimaryTopologyOwnerFromIdealAssignment(
-                            grpHolder.aff,
-                            p,
-                            top,
-                            idealOwners,
-                            waitRebalanceInfo
-                        );
-                    else if (!curOwners.get(0).equals(idealOwners.get(0)))
-                        // Current distribution was already not ideal. Preserve it for late affinity assignment.
-                        newOwners = latePrimaryAssignment(
-                            grpHolder.aff,
-                            p,
-                            curOwners.get(0),
-                            idealOwners,
-                            waitRebalanceInfo
-                        );
-
-                    if (newOwners != null) {
-                        newAssignment.set(p, newOwners);
-
-                        List<Long> clusterNodesAsOrder = newOwners.stream()
-                            .map(NODE_TO_ORDER::apply)
-                            .collect(Collectors.toList());
-
-                        cacheAffinityDiff.put(p, clusterNodesAsOrder);
-                    }
-                }
-
-                if (!cacheAffinityDiff.isEmpty())
-                    diff.put(grpHolder.groupId(), cacheAffinityDiff);
-
-                grpHolder.affinity().initialize(topVer, newAssignment);
-
-                fut.timeBag().finishLocalStage("Affinity initialization (on server left) " +
-                    "[grp=" + desc.cacheOrGroupName() + "]");
-            }
-        });
-
-        synchronized (mux) {
-            this.waitInfo = !waitRebalanceInfo.empty() ? waitRebalanceInfo : null;
-
-            WaitRebalanceInfo info = this.waitInfo;
-
-            if (log.isDebugEnabled()) {
-                log.debug("Computed new affinity after node left [topVer=" + topVer +
-                    ", waitGrps=" + (info != null ? groupNames(info.waitGrps.keySet()) : null) + ']');
-            }
-        }
+        Map<Integer, CacheGroupAffinityMessage> result = onReassignmentEnforced(fut);
 
-        return CacheGroupAffinityMessage.createAffinityDiffMessages(diff);
+        return result;
     }
 
     /**
@@ -1862,8 +1711,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @throws IgniteCheckedException If failed.
      */
     public Map<Integer, CacheGroupAffinityMessage> onCustomEventWithEnforcedAffinityReassignment(
-        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException
-    {
+        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException {
         assert DiscoveryCustomEvent.requiresCentralizedAffinityAssignment(fut.firstEvent());
 
         Map<Integer, CacheGroupAffinityMessage> result = onReassignmentEnforced(fut);
@@ -1879,8 +1727,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @throws IgniteCheckedException If failed.
      */
     private Map<Integer, CacheGroupAffinityMessage> onReassignmentEnforced(
-        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException
-    {
+        final GridDhtPartitionsExchangeFuture fut) throws IgniteCheckedException {
         final ExchangeDiscoveryEvents evts = fut.context().events();
 
         forAllRegisteredCacheGroups(new IgniteInClosureX<CacheGroupDescriptor>() {
@@ -1966,7 +1813,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @param crd Coordinator flag.
      * @throws IgniteCheckedException If failed.
      */
-    public void onBaselineTopologyChanged(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException {
+    public void onBaselineTopologyChanged(final GridDhtPartitionsExchangeFuture fut,
+        boolean crd) throws IgniteCheckedException {
         assert !fut.firstEvent().eventNode().isClient();
 
         WaitRebalanceInfo waitRebalanceInfo = initAffinityOnNodeJoin(fut, crd);
@@ -2094,8 +1942,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      * @param discoCache Discovery data cache.
      * @param affCache Affinity.
      * @param fetchFut Affinity fetch future.
-     * @throws IgniteCheckedException If failed.
      * @return Affinity assignment response.
+     * @throws IgniteCheckedException If failed.
      */
     private GridDhtAffinityAssignmentResponse fetchAffinity(
         AffinityTopologyVersion topVer,
@@ -2139,10 +1987,11 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
      *
      * @param fut Exchange future.
      * @param crd Coordinator flag.
-     * @throws IgniteCheckedException If failed.
      * @return {@code True} if affinity should be assigned by coordinator.
+     * @throws IgniteCheckedException If failed.
      */
-    public boolean onCentralizedAffinityChange(final GridDhtPartitionsExchangeFuture fut, boolean crd) throws IgniteCheckedException {
+    public boolean onCentralizedAffinityChange(final GridDhtPartitionsExchangeFuture fut,
+        boolean crd) throws IgniteCheckedException {
         assert (fut.events().hasServerLeft() && !fut.firstEvent().eventNode().isClient()) ||
             DiscoveryCustomEvent.requiresCentralizedAffinityAssignment(fut.firstEvent()) : fut.firstEvent();
 
@@ -2169,8 +2018,8 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     /**
      * @param fut Exchange future.
      * @param newAff {@code True} if there are no older nodes with affinity info available.
-     * @throws IgniteCheckedException If failed.
      * @return Future completed when caches initialization is done.
+     * @throws IgniteCheckedException If failed.
      */
     public IgniteInternalFuture<?> initCoordinatorCaches(
         final GridDhtPartitionsExchangeFuture fut,
@@ -2241,10 +2090,10 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                         }
 
                         GridDhtAssignmentFetchFuture fetchFut = new GridDhtAssignmentFetchFuture(
-                                cctx,
-                                desc.groupId(),
-                                futureToFetchAffinity.topologyVersion(),
-                                futureToFetchAffinity.events().discoveryCache()
+                            cctx,
+                            desc.groupId(),
+                            futureToFetchAffinity.topologyVersion(),
+                            futureToFetchAffinity.events().discoveryCache()
                         );
 
                         fetchFut.init(false);
@@ -2255,13 +2104,13 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
 
                         fetchFut.listen(new IgniteInClosureX<IgniteInternalFuture<GridDhtAffinityAssignmentResponse>>() {
                             @Override public void applyx(IgniteInternalFuture<GridDhtAffinityAssignmentResponse> fetchFut)
-                                    throws IgniteCheckedException {
+                                throws IgniteCheckedException {
                                 fetchAffinity(
-                                        futureToFetchAffinity0.topologyVersion(),
-                                        futureToFetchAffinity0.events(),
-                                        futureToFetchAffinity0.events().discoveryCache(),
-                                        aff,
-                                        (GridDhtAssignmentFetchFuture)fetchFut
+                                    futureToFetchAffinity0.topologyVersion(),
+                                    futureToFetchAffinity0.events(),
+                                    futureToFetchAffinity0.events().discoveryCache(),
+                                    aff,
+                                    (GridDhtAssignmentFetchFuture)fetchFut
                                 );
 
                                 aff.calculate(topVer, fut.events(), fut.events().discoveryCache());
@@ -2375,30 +2224,30 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
                 if (cctx.localNode().isClient() && cctx.cache().cacheGroup(desc.groupId()) == null)
                     return;
 
-                CacheGroupHolder cache = getOrCreateGroupHolder(evts.topologyVersion(), desc);
+                CacheGroupHolder grpHolder = getOrCreateGroupHolder(evts.topologyVersion(), desc);
 
-                boolean latePrimary = cache.rebalanceEnabled;
+                boolean latePrimary = grpHolder.rebalanceEnabled;
 
                 boolean grpAdded = evts.nodeJoined(desc.receivedFrom());
 
                 initAffinityOnNodeJoin(evts,
                     grpAdded,
-                    cache.affinity(),
+                    grpHolder.affinity(),
                     crd ? waitRebalanceInfo : null,
                     latePrimary);
 
                 if (crd && grpAdded) {
-                    AffinityAssignment aff = cache.aff.cachedAffinity(cache.aff.lastVersion());
+                    AffinityAssignment aff = grpHolder.aff.cachedAffinity(grpHolder.aff.lastVersion());
 
                     assert evts.topologyVersion().equals(aff.topologyVersion()) : "Unexpected version [" +
-                        "grp=" + cache.aff.cacheOrGroupName() +
+                        "grp=" + grpHolder.aff.cacheOrGroupName() +
                         ", evts=" + evts.topologyVersion() +
-                        ", aff=" + cache.aff.lastVersion() + ']';
+                        ", aff=" + grpHolder.aff.lastVersion() + ']';
 
                     Map<UUID, GridDhtPartitionMap> map = affinityFullMap(aff);
 
                     for (GridDhtPartitionMap map0 : map.values())
-                        cache.topology(fut.context().events().discoveryCache()).update(fut.exchangeId(), map0, true);
+                        grpHolder.topology(fut.context().events().discoveryCache()).update(fut.exchangeId(), map0, true);
                 }
 
                 cctx.exchange().exchangerUpdateHeartbeat();
@@ -2466,77 +2315,40 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
         assert affTopVer.topologyVersion() > 0 : "Affinity is not initialized [grp=" + aff.cacheOrGroupName() +
             ", topVer=" + affTopVer + ", node=" + cctx.localNodeId() + ']';
 
-        assert aff.idealAssignmentRaw() != null : "Previous assignment is not available.";
+        List<List<ClusterNode>> curAff = aff.assignments(affTopVer);
+
+        assert aff.idealAssignment() != null : "Previous assignment is not available.";
 
-        IdealAffinityAssignment idealAssignment = aff.calculate(evts.topologyVersion(), evts, evts.discoveryCache());
-        List<List<ClusterNode>> curAssignment = aff.assignments(affTopVer);
+        List<List<ClusterNode>> idealAssignment = aff.calculate(evts.topologyVersion(), evts, evts.discoveryCache()).assignment();
         List<List<ClusterNode>> newAssignment = null;
 
         if (latePrimary) {
-            BitSet processedPartitions = new BitSet(curAssignment.size());
+            for (int p = 0; p < idealAssignment.size(); p++) {
+                List<ClusterNode> newNodes = idealAssignment.get(p);
+                List<ClusterNode> curNodes = curAff.get(p);
 
-            // Late affinity assignment to changed primaries.
-            for (ClusterNode joinedNode : evts.joinedServerNodes()) {
-                Set<Integer> primaries = idealAssignment.idealPrimaries(joinedNode);
-
-                for (int p : primaries) {
-                    List<ClusterNode> curNodes = curAssignment.get(p);
-
-                    if (curNodes.isEmpty())
-                        continue;
-
-                    ClusterNode curPrimary = curNodes.get(0);
+                ClusterNode curPrimary = !curNodes.isEmpty() ? curNodes.get(0) : null;
+                ClusterNode newPrimary = !newNodes.isEmpty() ? newNodes.get(0) : null;
 
+                if (curPrimary != null && newPrimary != null && !curPrimary.equals(newPrimary)) {
                     assert cctx.discovery().node(evts.topologyVersion(), curPrimary.id()) != null : curPrimary;
 
-                    List<ClusterNode> idealNodes = idealAssignment.assignment().get(p);
-
-                    List<ClusterNode> newNodes = latePrimaryAssignment(aff,
+                    List<ClusterNode> nodes0 = latePrimaryAssignment(aff,
                         p,
                         curPrimary,
-                        idealNodes,
-                        rebalanceInfo);
-
-                    if (newAssignment == null)
-                        newAssignment = new ArrayList<>(idealAssignment.assignment());
-
-                    newAssignment.set(p, newNodes);
-
-                    processedPartitions.set(p);
-                }
-            }
-
-            Set<Integer> partitionsWithChangedPrimary = aff.partitionPrimariesDifferentToIdeal(affTopVer);
-
-            for (int p : partitionsWithChangedPrimary) {
-                // Already processed above.
-                if (processedPartitions.get(p))
-                    continue;
-
-                List<ClusterNode> curNodes = curAssignment.get(p);
-
-                if (curNodes.isEmpty())
-                    continue;
-
-                List<ClusterNode> idealOwners = idealAssignment.assignment().get(p);
-
-                if (!curNodes.get(0).equals(idealOwners.get(0))) {
-                    List<ClusterNode> newNodes = latePrimaryAssignment(aff,
-                        p,
-                        curNodes.get(0),
-                        idealOwners,
+                        newNodes,
                         rebalanceInfo);
 
                     if (newAssignment == null)
-                        newAssignment = new ArrayList<>(idealAssignment.assignment());
+                        newAssignment = new ArrayList<>(idealAssignment);
 
-                    newAssignment.set(p, newNodes);
+                    newAssignment.set(p, nodes0);
                 }
             }
         }
 
         if (newAssignment == null)
-            newAssignment = idealAssignment.assignment();
+            newAssignment = idealAssignment;
 
         aff.initialize(evts.topologyVersion(), newAssignment);
     }
@@ -2608,14 +2420,13 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
     }
 
     /**
-     * Initializes current affinity assignment based on partitions availability.
-     * Nodes that have most recent data will be considered affinity nodes.
+     * Initializes current affinity assignment based on partitions availability. Nodes that have most recent data will
+     * be considered affinity nodes.
      *
      * @param topVer Topology version.
      * @param fut Exchange future.
      * @param c Closure converting affinity diff.
      * @param initAff {@code True} if need initialize affinity.
-     *
      * @return Affinity assignment for each of registered cache group.
      */
     private <T> Map<Integer, Map<Integer, List<T>>> initAffinityBasedOnPartitionsAvailability(
@@ -2989,7 +2800,7 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
             GridCacheSharedContext cctx,
             CacheGroupDescriptor grpDesc,
             AffinityTopologyVersion topVer
-            ) throws IgniteCheckedException {
+        ) throws IgniteCheckedException {
             return create(cctx, grpDesc, topVer, null);
         }
 
@@ -3128,14 +2939,13 @@ public class CacheAffinitySharedManager<K, V> extends GridCacheSharedManagerAdap
          * @param waitNode Node rebalancing data.
          * @param assignment New assignment.
          */
-        void add(Integer grpId, Integer part, UUID waitNode, List<ClusterNode> assignment) {
-            assert !F.isEmpty(assignment) : assignment;
-
+        void add(Integer grpId, Integer part, UUID waitNode, @Nullable List<ClusterNode> assignment) {
             deploymentIds.putIfAbsent(grpId, cachesRegistry.group(grpId).deploymentId());
 
             waitGrps.computeIfAbsent(grpId, k -> new HashMap<>()).put(part, waitNode);
 
-            assignments.computeIfAbsent(grpId, k -> new HashMap<>()).put(part, assignment);
+            if (assignment != null)
+                assignments.computeIfAbsent(grpId, k -> new HashMap<>()).put(part, assignment);
         }
 
         /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
index 968afd5..b6da77f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheEntryInfoCollection.java
@@ -128,4 +128,23 @@ public class CacheEntryInfoCollection implements Message {
     @Override public byte fieldsCount() {
         return 1;
     }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("[");
+
+        for (int i = 0; i < infos().size(); i++) {
+            GridCacheEntryInfo info = infos().get(i);
+
+            Object k = info.key().value(null, false);
+
+            b.append("[key=").append(k == null ? "null" : k).append(", ver=").
+                append(info.version()).append(", val=").append(info.value()).append(']');
+        }
+
+        b.append(']');
+
+        return b.toString();
+    }
 }
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
index 29efa76..68dffbf 100644
--- 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
@@ -65,6 +65,7 @@ import org.apache.ignite.lang.IgnitePredicate;
 import org.apache.ignite.mxbean.CacheGroupMetricsMXBean;
 import org.jetbrains.annotations.Nullable;
 
+import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC;
 import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT;
 import static org.apache.ignite.cache.CacheMode.LOCAL;
 import static org.apache.ignite.cache.CacheMode.REPLICATED;
@@ -182,6 +183,8 @@ public class CacheGroupContext {
     /** Statistics holder to track IO operations for data pages. */
     private final IoStatisticsHolder statHolderData;
 
+    /** */
+    private volatile boolean hasAtomicCaches;
 
     /**
      * @param ctx Context.
@@ -357,6 +360,9 @@ public class CacheGroupContext {
 
         if (!drEnabled && cctx.isDrEnabled())
             drEnabled = true;
+
+        if (!hasAtomicCaches)
+            hasAtomicCaches = cctx.config().getAtomicityMode() == ATOMIC;
     }
 
     /**
@@ -1278,4 +1284,11 @@ public class CacheGroupContext {
     public IoStatisticsHolder statisticsHolderData() {
         return statHolderData;
     }
+
+    /**
+     * @return {@code True} if group has atomic caches.
+     */
+    public boolean hasAtomicCaches() {
+        return hasAtomicCaches;
+    }
 }
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 5ca9c41..54dc706 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
@@ -1542,10 +1542,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
             if (cctx.deferredDelete() && deletedUnlocked() && !isInternal() && !detached())
                 deletedUnlocked(false);
 
-            updateCntr0 = nextPartitionCounter(topVer, tx == null || tx.local(), updateCntr);
-
-            if (updateCntr != null && updateCntr != 0)
-                updateCntr0 = updateCntr;
+            updateCntr0 = nextPartitionCounter(tx, updateCntr);
 
             if (tx != null && cctx.group().persistenceEnabled() && cctx.group().walEnabled())
                 logPtr = logTxUpdate(tx, val, expireTime, updateCntr0);
@@ -1765,10 +1762,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 }
             }
 
-            updateCntr0 = nextPartitionCounter(topVer, tx == null || tx.local(), updateCntr);
-
-            if (updateCntr != null && updateCntr != 0)
-                updateCntr0 = updateCntr;
+            updateCntr0 = nextPartitionCounter(tx, updateCntr);
 
             if (tx != null && cctx.group().persistenceEnabled() && cctx.group().walEnabled())
                 logPtr = logTxUpdate(tx, null, 0, updateCntr0);
@@ -2189,7 +2183,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 cctx.cache().metrics0().onInvokeRemove(old != null);
 
             if (lsnrCol != null) {
-                long updateCntr = nextPartitionCounter(AffinityTopologyVersion.NONE, true, null);
+                long updateCntr = nextPartitionCounter(AffinityTopologyVersion.NONE, true, false, null);
 
                 cctx.continuousQueries().onEntryUpdated(
                     lsnrCol,
@@ -2368,12 +2362,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                         else
                             evtVal = (CacheObject)writeObj;
 
-                        long updateCntr0 = nextPartitionCounter(topVer, primary, updateCntr);
+                        assert !primary && updateCntr != null;
 
-                        if (updateCntr != null)
-                            updateCntr0 = updateCntr;
-
-                        onUpdateFinished(updateCntr0);
+                        onUpdateFinished(updateCntr);
 
                         cctx.continuousQueries().onEntryUpdated(
                             key,
@@ -2383,7 +2374,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                             partition(),
                             primary,
                             false,
-                            updateCntr0,
+                            updateCntr,
                             null,
                             topVer);
                     }
@@ -3447,7 +3438,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                 long updateCntr = 0;
 
                 if (!preload)
-                    updateCntr = nextPartitionCounter(topVer, true, null);
+                    updateCntr = nextPartitionCounter(topVer, true, true, null);
 
                 if (walEnabled) {
                     if (cctx.mvccEnabled()) {
@@ -3536,10 +3527,20 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
     /**
      * @param topVer Topology version for current operation.
      * @param primary Primary node update flag.
+     * @param initial {@code True} if initial value.
      * @param primaryCntr Counter assigned on primary node.
      * @return Update counter.
      */
-    protected long nextPartitionCounter(AffinityTopologyVersion topVer, boolean primary, @Nullable Long primaryCntr) {
+    protected long nextPartitionCounter(AffinityTopologyVersion topVer, boolean primary, boolean initial,
+        @Nullable Long primaryCntr) {
+        return 0;
+    }
+
+    /**
+     * @param tx Tx.
+     * @param updateCntr Update counter.
+     */
+    protected long nextPartitionCounter(IgniteInternalTx tx, @Nullable Long updateCntr) {
         return 0;
     }
 
@@ -5574,8 +5575,8 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                         cctx.cacheId(),
                         entry.key(),
                         val,
-                       res.resultType() == ResultType.PREV_NULL ? CREATE :
-                        (res.resultType() == ResultType.REMOVED_NOT_NULL) ? DELETE : UPDATE,
+                        res.resultType() == ResultType.PREV_NULL ? CREATE :
+                            (res.resultType() == ResultType.REMOVED_NOT_NULL) ? DELETE : UPDATE,
                         tx.nearXidVersion(),
                         newVer,
                         expireTime,
@@ -5751,7 +5752,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                     oldRow);
 
                 treeOp = oldRow != null && oldRow.link() == newRow.link() ?
-                    IgniteTree.OperationType.NOOP : IgniteTree.OperationType.PUT;
+                    IgniteTree.OperationType.IN_PLACE : IgniteTree.OperationType.PUT;
             }
             else
                 treeOp = oldRow != null ? IgniteTree.OperationType.REMOVE : IgniteTree.OperationType.NOOP;
@@ -6376,10 +6377,7 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                     ", locNodeId=" + cctx.localNodeId() + ']';
             }
 
-            long updateCntr0 = entry.nextPartitionCounter(topVer, primary, updateCntr);
-
-            if (updateCntr != null)
-                updateCntr0 = updateCntr;
+            long updateCntr0 = entry.nextPartitionCounter(topVer, primary, false, updateCntr);
 
             entry.logUpdate(op, updated, newVer, newExpireTime, updateCntr0);
 
@@ -6466,10 +6464,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.nextPartitionCounter(topVer, primary, updateCntr);
-
-            if (updateCntr != null)
-                updateCntr0 = updateCntr;
+            long updateCntr0 = entry.nextPartitionCounter(topVer, primary, false, updateCntr);
 
             entry.logUpdate(op, null, newVer, 0, updateCntr0);
 
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 199f725..f5ba7a2 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
@@ -1773,7 +1773,9 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                             null,
                             msg.partsToReload(cctx.localNodeId(), grpId),
                             partsSizes.getOrDefault(grpId, Collections.emptyMap()),
-                            msg.topologyVersion());
+                            msg.topologyVersion(),
+                            null
+                        );
                     }
                 }
 
@@ -1815,8 +1817,8 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
 
         try {
             if (msg.exchangeId() == null) {
-                if (log.isTraceEnabled())
-                    log.trace("Received local partition update [nodeId=" + node.id() + ", parts=" +
+                if (log.isDebugEnabled())
+                    log.debug("Received local partition update [nodeId=" + node.id() + ", parts=" +
                         msg + ']');
 
                 boolean updated = false;
@@ -1865,7 +1867,7 @@ public class GridCachePartitionExchangeManager<K, V> extends GridCacheSharedMana
                         U.warn(log, "Client node tries to connect but its exchange " +
                             "info is cleaned up from exchange history. " +
                             "Consider increasing 'IGNITE_EXCHANGE_HISTORY_SIZE' property " +
-                            "or start clients in  smaller batches. " +
+                            "or start clients in smaller batches. " +
                             "Current settings and versions: " +
                             "[IGNITE_EXCHANGE_HISTORY_SIZE=" + EXCHANGE_HISTORY_SIZE + ", " +
                             "initVer=" + initVer + ", " +
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 48797ee..ec4b8cf 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
@@ -119,7 +119,6 @@ import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaS
 import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetastorageLifecycleListener;
 import org.apache.ignite.internal.processors.cache.persistence.metastorage.ReadOnlyMetastorage;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId;
-import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionRecoverState;
 import org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteCacheSnapshotManager;
 import org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotDiscoveryMessage;
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
@@ -236,6 +235,10 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     private static final String INVALID_REGION_CONFIGURATION_MESSAGE = "Failed to join node " +
         "(Incompatible data region configuration [region=%s, locNodeId=%s, isPersistenceEnabled=%s, rmtNodeId=%s, isPersistenceEnabled=%s])";
 
+    /** Template of message of failed node join because encryption settings are different for the same cache. */
+    private static final String ENCRYPT_MISMATCH_MESSAGE = "Failed to join node to the cluster " +
+        "(encryption settings are different for cache '%s' : local=%s, remote=%s.)";
+
     /** */
     private final boolean startClientCaches =
         IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_START_CACHES_ON_JOIN, false);
@@ -1069,7 +1072,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
     }
 
     /**
-     * @param node Remote node to check.
+     * @param rmtNode Remote node to check.
      * @return Data storage configuration
      */
     private DataStorageConfiguration extractDataStorage(ClusterNode rmtNode) {
@@ -3489,12 +3492,12 @@ public class GridCacheProcessor extends GridProcessorAdapter {
                     }
                 }
 
-                DynamicCacheDescriptor localDesc = cacheDescriptor(cacheInfo.cacheData().config().getName());
+                DynamicCacheDescriptor locDesc = cacheDescriptor(cacheInfo.cacheData().config().getName());
 
-                if (localDesc == null)
+                if (locDesc == null)
                     continue;
 
-                QuerySchemaPatch schemaPatch = localDesc.makeSchemaPatch(cacheInfo.cacheData().queryEntities());
+                QuerySchemaPatch schemaPatch = locDesc.makeSchemaPatch(cacheInfo.cacheData().queryEntities());
 
                 if (schemaPatch.hasConflicts() || (isGridActive && !schemaPatch.isEmpty())) {
                     if (errorMsg.length() > 0)
@@ -3502,9 +3505,22 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
                     if (schemaPatch.hasConflicts())
                         errorMsg.append(String.format(MERGE_OF_CONFIG_CONFLICTS_MESSAGE,
-                            localDesc.cacheName(), schemaPatch.getConflictsMessage()));
+                            locDesc.cacheName(), schemaPatch.getConflictsMessage()));
                     else
-                        errorMsg.append(String.format(MERGE_OF_CONFIG_REQUIRED_MESSAGE, localDesc.cacheName()));
+                        errorMsg.append(String.format(MERGE_OF_CONFIG_REQUIRED_MESSAGE, locDesc.cacheName()));
+                }
+
+                // This check must be done on join, otherwise group encryption key will be
+                // written to metastore regardless of validation check and could trigger WAL write failures.
+                boolean locEnc = locDesc.cacheConfiguration().isEncryptionEnabled();
+                boolean rmtEnc = cacheInfo.cacheData().config().isEncryptionEnabled();
+
+                if (locEnc != rmtEnc) {
+                    if (errorMsg.length() > 0)
+                        errorMsg.append("\n");
+
+                    // Message will be printed on remote node, so need to swap local and remote.
+                    errorMsg.append(String.format(ENCRYPT_MISMATCH_MESSAGE, locDesc.cacheName(), rmtEnc, locEnc));
                 }
             }
 
@@ -6016,7 +6032,7 @@ public class GridCacheProcessor extends GridProcessorAdapter {
          */
         private void restorePartitionStates(
             Collection<CacheGroupContext> forGroups,
-            Map<GroupPartitionId, PartitionRecoverState> partitionStates
+            Map<GroupPartitionId, Integer> partitionStates
         ) throws IgniteCheckedException {
             long startRestorePart = U.currentTimeMillis();
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedTtlCleanupManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedTtlCleanupManager.java
index 2254c5e..3623fd9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedTtlCleanupManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheSharedTtlCleanupManager.java
@@ -23,6 +23,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.failure.FailureContext;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.util.worker.GridWorker;
 import org.apache.ignite.thread.IgniteThread;
@@ -164,7 +165,13 @@ public class GridCacheSharedTtlCleanupManager extends GridCacheSharedManagerAdap
                 }
             }
             catch (Throwable t) {
-                if (!(X.hasCause(t, IgniteInterruptedCheckedException.class, InterruptedException.class)))
+                if (X.hasCause(t, NodeStoppingException.class)) {
+                    isCancelled = true; // Treat node stopping as valid worker cancellation.
+
+                    return;
+                }
+
+                if (!(t instanceof IgniteInterruptedCheckedException || t instanceof InterruptedException))
                     err = t;
 
                 throw t;
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 48c86b9..cc5dd42 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
@@ -849,6 +849,7 @@ public class GridCacheUtils {
 
         return tx.getClass().getSimpleName() + "[xid=" + tx.xid() +
             ", xidVersion=" + tx.xidVersion() +
+            ", nearXidVersion=" + tx.nearXidVersion() +
             ", concurrency=" + tx.concurrency() +
             ", isolation=" + tx.isolation() +
             ", state=" + tx.state() +
@@ -857,7 +858,7 @@ public class GridCacheUtils {
             ", nodeId=" + tx.nodeId() +
             ", timeout=" + tx.timeout() +
             ", duration=" + (U.currentTimeMillis() - tx.startTime()) +
-            (tx instanceof GridNearTxLocal ? ", label=" + ((GridNearTxLocal)tx).label() : "") +
+            (tx instanceof GridNearTxLocal ? ", label=" + tx.label() : "") +
             ']';
     }
 
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 b7e8ec7..74a0b30 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
@@ -31,8 +31,9 @@ import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
 import org.apache.ignite.internal.processors.cache.persistence.RootPage;
 import org.apache.ignite.internal.processors.cache.persistence.RowStore;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.SimpleDataRow;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId;
-import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionRecoverState;
+import org.apache.ignite.internal.processors.cache.persistence.partstorage.PartitionMetaStorage;
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.cache.tree.PendingEntriesTree;
 import org.apache.ignite.internal.processors.cache.tree.mvcc.data.MvccUpdateResult;
@@ -90,7 +91,7 @@ public interface IgniteCacheOffheapManager {
      * @return Number of processed partitions.
      * @throws IgniteCheckedException If failed.
      */
-    long restorePartitionStates(Map<GroupPartitionId, PartitionRecoverState> partitionRecoveryStates) throws IgniteCheckedException;
+    long restorePartitionStates(Map<GroupPartitionId, Integer> partitionRecoveryStates) throws IgniteCheckedException;
 
     /**
      * Partition counter update callback. May be overridden by plugin-provided subclasses.
@@ -104,9 +105,10 @@ public interface IgniteCacheOffheapManager {
      * Initial counter will be updated on state restore only
      *
      * @param part Partition
-     * @param cntr New initial counter
+     * @param start Start.
+     * @param delta Delta.
      */
-    public void onPartitionInitialCounterUpdated(int part, long cntr);
+    public void onPartitionInitialCounterUpdated(int part, long start, long delta);
 
     /**
      * Partition counter provider. May be overridden by plugin-provided subclasses.
@@ -584,6 +586,13 @@ public interface IgniteCacheOffheapManager {
      */
     interface CacheDataStore {
         /**
+         * Initialize data store if it exists.
+         *
+         * @return {@code True} if initialized.
+         */
+        boolean init();
+
+        /**
          * @return Partition ID.
          */
         int partId();
@@ -594,13 +603,6 @@ public interface IgniteCacheOffheapManager {
         String name();
 
         /**
-         * @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, @Nullable Map<Integer, Long> cacheSizes);
-
-        /**
          * @param cacheId Cache ID.
          * @return Size.
          */
@@ -630,22 +632,36 @@ public interface IgniteCacheOffheapManager {
         void updateSize(int cacheId, long delta);
 
         /**
-         * @return Update counter.
+         * @return Update counter (LWM).
          */
         long updateCounter();
 
         /**
+         * @return Reserved counter (HWM).
+         */
+        long reservedCounter();
+
+        /**
+         * @return Update counter or {@code null} if store is not yet created.
+         */
+        @Nullable PartitionUpdateCounter partUpdateCounter();
+
+        /**
+         * @param delta Delta.
+         */
+        long reserve(long delta);
+
+        /**
          * @param val Update counter.
          */
         void updateCounter(long val);
 
         /**
          * Updates counters from start value by delta value.
-         *
          * @param start Start.
-         * @param delta Delta
+         * @param delta Delta.
          */
-        void updateCounter(long start, long delta);
+        boolean updateCounter(long start, long delta);
 
         /**
          * @return Next update counter.
@@ -1017,9 +1033,10 @@ public interface IgniteCacheOffheapManager {
         public RowStore rowStore();
 
         /**
-         * @param cntr Counter.
+         * @param start Counter.
+         * @param delta Delta.
          */
-        public void updateInitialCounter(long cntr);
+        public void updateInitialCounter(long start, long delta);
 
         /**
          * Inject rows cache cleaner.
@@ -1047,5 +1064,15 @@ public interface IgniteCacheOffheapManager {
          * @throws IgniteCheckedException If failed.
          */
         public void preload() throws IgniteCheckedException;
+
+        /**
+         * Reset counters for partition.
+         */
+        void resetUpdateCounter();
+
+        /**
+         * Partition storage.
+         */
+        public PartitionMetaStorage<SimpleDataRow> partStorage();
     }
 }
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 a0ebcdc..3706754 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
@@ -36,6 +36,9 @@ import javax.cache.processor.EntryProcessor;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.failure.FailureContext;
+import org.apache.ignite.failure.FailureType;
 import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.pagemem.FullPageId;
 import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageMvccMarkUpdatedRecord;
@@ -59,8 +62,9 @@ import org.apache.ignite.internal.processors.cache.persistence.CacheDataRowAdapt
 import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow;
 import org.apache.ignite.internal.processors.cache.persistence.RootPage;
 import org.apache.ignite.internal.processors.cache.persistence.RowStore;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.SimpleDataRow;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId;
-import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionRecoverState;
+import org.apache.ignite.internal.processors.cache.persistence.partstorage.PartitionMetaStorage;
 import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
@@ -139,6 +143,10 @@ import static org.apache.ignite.internal.util.IgniteTree.OperationType.PUT;
  */
 public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager {
     /** */
+    private final boolean failNodeOnPartitionInconsistency = Boolean.getBoolean(
+        IgniteSystemProperties.IGNITE_FAIL_NODE_ON_UNRECOVERABLE_PARTITION_INCONSISTENCY);
+
+    /** */
     protected GridCacheSharedContext ctx;
 
     /** */
@@ -251,7 +259,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
     }
 
     /** {@inheritDoc} */
-    @Override public long restorePartitionStates(Map<GroupPartitionId, PartitionRecoverState> partitionRecoveryStates) throws IgniteCheckedException {
+    @Override public long restorePartitionStates(Map<GroupPartitionId, Integer> partitionRecoveryStates) throws IgniteCheckedException {
         return 0; // No-op.
     }
 
@@ -692,7 +700,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
     }
 
     /** {@inheritDoc} */
-    @Override public void onPartitionInitialCounterUpdated(int part, long cntr) {
+    @Override public void onPartitionInitialCounterUpdated(int part, long start, long delta) {
         // No-op.
     }
 
@@ -1398,7 +1406,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
         private final CacheDataTree dataTree;
 
         /** Update counter. */
-        protected final PartitionUpdateCounter pCntr = new PartitionUpdateCounter(log);
+        protected final PartitionUpdateCounter pCntr;
 
         /** Partition size. */
         private final AtomicLong storageSize = new AtomicLong();
@@ -1431,6 +1439,14 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
             this.name = name;
             this.rowStore = rowStore;
             this.dataTree = dataTree;
+            if (grp.mvccEnabled())
+                pCntr = new PartitionMvccTxUpdateCounterImpl();
+            else if (grp.hasAtomicCaches())
+                pCntr = new PartitionAtomicUpdateCounterImpl();
+            else {
+                pCntr = ctx.logger(PartitionTxUpdateCounterDebugWrapper.class).isDebugEnabled() ?
+                    new PartitionTxUpdateCounterDebugWrapper(grp, partId) : new PartitionTxUpdateCounterImpl();
+            }
         }
 
         /**
@@ -1448,6 +1464,11 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
         }
 
         /** {@inheritDoc} */
+        @Override public boolean init() {
+            return false;
+        }
+
+        /** {@inheritDoc} */
         @Override public int partId() {
             return partId;
         }
@@ -1527,14 +1548,17 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
             return pCntr.initial();
         }
 
-        /** {@inheritDoc} */
-        @Override public void updateInitialCounter(long cntr) {
-            pCntr.updateInitial(cntr);
+        /** {@inheritDoc}
+         * @param start Start.
+         * @param delta Delta.
+         */
+        @Override public void updateInitialCounter(long start, long delta) {
+            pCntr.updateInitial(start, delta);
         }
 
         /** {@inheritDoc} */
         @Override public long getAndIncrementUpdateCounter(long delta) {
-            return pCntr.getAndAdd(delta);
+            return pCntr.reserve(delta);
         }
 
         /** {@inheritDoc} */
@@ -1543,13 +1567,39 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
         }
 
         /** {@inheritDoc} */
+        @Override public long reservedCounter() {
+            return pCntr.reserved();
+        }
+
+        /** {@inheritDoc} */
+        @Override public PartitionUpdateCounter partUpdateCounter() {
+            return pCntr;
+        }
+
+        /** {@inheritDoc} */
+        @Override public long reserve(long delta) {
+            return pCntr.reserve(delta);
+        }
+
+        /** {@inheritDoc} */
         @Override public void updateCounter(long val) {
-            pCntr.update(val);
+            try {
+                pCntr.update(val);
+            }
+            catch (IgniteCheckedException e) {
+                U.error(log, "Failed to update partition counter. " +
+                    "Most probably a node with most actual data is out of topology or data streamer is used " +
+                    "in preload mode (allowOverride=false) concurrently with cache transactions [grpName=" +
+                    grp.name() + ", partId=" + partId + ']', e);
+
+                if (failNodeOnPartitionInconsistency)
+                    ctx.kernalContext().failure().process(new FailureContext(FailureType.CRITICAL_ERROR, e));
+            }
         }
 
         /** {@inheritDoc} */
-        @Override public void updateCounter(long start, long delta) {
-            pCntr.update(start, delta);
+        @Override public boolean updateCounter(long start, long delta) {
+            return pCntr.update(start, delta);
         }
 
         /** {@inheritDoc} */
@@ -1638,6 +1688,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
                 }
 
                 case NOOP:
+                case IN_PLACE:
                     break;
 
                 default:
@@ -2891,9 +2942,14 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
             rowStore().setRowCacheCleaner(rowCacheCleaner);
         }
 
-        /** {@inheritDoc} */
-        @Override public void init(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes) {
-            pCntr.init(updCntr);
+        /**
+         * @param size Size to init.
+         * @param updCntr Update counter.
+         * @param cacheSizes Cache sizes if store belongs to group containing multiple caches.
+         * @param cntrUpdData Counter updates.
+         */
+        public void restoreState(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes, byte[] cntrUpdData) {
+            pCntr.init(updCntr, cntrUpdData);
 
             storageSize.set(size);
 
@@ -2913,6 +2969,16 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
             // No-op.
         }
 
+        /** {@inheritDoc} */
+        @Override public void resetUpdateCounter() {
+            pCntr.reset();
+        }
+
+        /** {@inheritDoc} */
+        @Override public PartitionMetaStorage<SimpleDataRow> partStorage() {
+            return null;
+        }
+
         /**
          * @param cctx Cache context.
          * @param key Key.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionAtomicUpdateCounterImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionAtomicUpdateCounterImpl.java
new file mode 100644
index 0000000..317c679
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionAtomicUpdateCounterImpl.java
@@ -0,0 +1,155 @@
+/*
+ * 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.Iterator;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.util.GridEmptyIterator;
+import org.apache.ignite.internal.util.GridLongList;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Partition update counter for non-tx scenarios without support for tracking missed updates.
+ * TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11797
+ */
+public class PartitionAtomicUpdateCounterImpl implements PartitionUpdateCounter {
+    /** Counter of applied updates in partition. */
+    private final AtomicLong cntr = new AtomicLong();
+
+    /**
+     * Initial counter is set to update with max sequence number after WAL recovery.
+     */
+    private long initCntr;
+
+    /** {@inheritDoc} */
+    @Override public void init(long initUpdCntr, @Nullable byte[] cntrUpdData) {
+        cntr.set(initUpdCntr);
+
+        initCntr = initUpdCntr;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long initial() {
+        return initCntr;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long get() {
+        return cntr.get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long next() {
+        return cntr.incrementAndGet();
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("StatementWithEmptyBody")
+    @Override public void update(long val) {
+        long cur;
+
+        while(val > (cur = cntr.get()) && !cntr.compareAndSet(cur, val));
+    }
+
+    /**
+     * Updates counter by delta from start position.
+     *
+     * @param start Start.
+     * @param delta Delta.
+     */
+    @Override public boolean update(long start, long delta) {
+        return false; // Prevent RollbackRecord in mixed tx-atomic mode.
+    }
+
+    /**
+     * Updates initial counter on recovery. Not thread-safe.
+     *
+     * @param start Start.
+     * @param delta Delta.
+     */
+    @Override public synchronized void updateInitial(long start, long delta) {
+        update(start + delta);
+
+        initCntr = get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public GridLongList finalizeUpdateCounters() {
+        return new GridLongList(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long reserve(long delta) {
+        return next(delta);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long next(long delta) {
+        return cntr.getAndAdd(delta);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean sequential() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public @Nullable byte[] getBytes() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void reset() {
+        initCntr = 0;
+
+        cntr.set(0);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        PartitionAtomicUpdateCounterImpl cntr = (PartitionAtomicUpdateCounterImpl)o;
+
+        return this.cntr.get() == cntr.cntr.get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long reserved() {
+        return get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized boolean empty() {
+        return get() == 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterator<long[]> iterator() {
+        return new GridEmptyIterator<>();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Counter [init=" + initCntr + ", val=" + get() + ']';
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionMvccTxUpdateCounterImpl.java
similarity index 50%
copy from modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
copy to modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionMvccTxUpdateCounterImpl.java
index 133f0a1..2f5d77a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionMvccTxUpdateCounterImpl.java
@@ -15,38 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.cache.persistence;
-
-import org.apache.ignite.IgniteCheckedException;
+package org.apache.ignite.internal.processors.cache;
 
 /**
- * Simple interface for data, store in some RowStore.
+ * Update counter implementation for MVCC mode.
  */
-public interface Storable {
-    /**
-     * @param link Link for this row.
-     */
-    public void link(long link);
-
-    /**
-     * @return Link for this row.
-     */
-    public long link();
-
-    /**
-     * @return Partition.
-     */
-    public int partition();
-
-    /**
-     * @return Row size in page.
-     * @throws IgniteCheckedException If failed.
-     */
-    public int size() throws IgniteCheckedException;
+public class PartitionMvccTxUpdateCounterImpl extends PartitionTxUpdateCounterImpl {
+    /** {@inheritDoc} */
+    @Override public long reserve(long delta) {
+        return next(delta);
+    }
 
-    /**
-     * @return Row header size in page. Header is indivisible part of row
-     * which is entirely available on the very first page followed by the row link.
-     */
-    public int headerSize();
+    /** {@inheritDoc} */
+    @Override public long reserved() {
+        return get();
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionTxUpdateCounterDebugWrapper.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionTxUpdateCounterDebugWrapper.java
new file mode 100644
index 0000000..a63479b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionTxUpdateCounterDebugWrapper.java
@@ -0,0 +1,201 @@
+/*
+ * 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 org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Update counter implementation useful for debugging.
+ */
+public class PartitionTxUpdateCounterDebugWrapper extends PartitionTxUpdateCounterImpl {
+    /** */
+    private IgniteLogger log;
+
+    /** */
+    private int partId;
+
+    /** */
+    private CacheGroupContext grp;
+
+    /**
+     * @param grp Group.
+     * @param partId Part id.
+     */
+    public PartitionTxUpdateCounterDebugWrapper(CacheGroupContext grp, int partId) {
+        this.log = grp.shared().logger(getClass());
+        this.partId = partId;
+        this.grp = grp;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void init(long initUpdCntr, @Nullable byte[] cntrUpdData) {
+        super.init(initUpdCntr, cntrUpdData);
+
+        log.debug("[op=init" +
+            ", grpId=" + grp.groupId() +
+            ", grpName=" + grp.cacheOrGroupName() +
+            ", caches=" + grp.caches() +
+            ", atomicity=" + grp.config().getAtomicityMode() +
+            ", syncMode=" + grp.config().getWriteSynchronizationMode() +
+            ", mode=" + grp.config().getCacheMode() +
+            ", partId=" + partId +
+            ", gapsLen=" + (cntrUpdData != null ? cntrUpdData.length : 0) +
+            ", cur=" + toString() +
+            ']');
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateInitial(long start, long delta) {
+        SB sb = new SB();
+
+        sb.a("[op=updateInitial" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", range=(" + start + "," + delta + ")" +
+            ", before=" + toString());
+
+        try {
+            super.updateInitial(start, delta);
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public long next() {
+        SB sb = new SB();
+
+        sb.a("[op=next" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", before=" + toString());
+
+        try {
+            return super.next();
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public long next(long delta) {
+        SB sb = new SB();
+
+        sb.a("[op=next" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", delta=" + delta +
+            ", before=" + toString());
+
+        try {
+            return super.next();
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void update(long val) throws IgniteCheckedException {
+        SB sb = new SB();
+
+        sb.a("[op=set" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", val=" + val +
+            ", before=" + toString());
+
+        try {
+            super.update(val);
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized GridLongList finalizeUpdateCounters() {
+        SB sb = new SB();
+
+        sb.a("[op=finalizeUpdateCounters" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", before=" + toString() +
+            ']');
+
+        try {
+            return super.finalizeUpdateCounters();
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized long reserve(long delta) {
+        SB sb = new SB();
+
+        sb.a("[op=reserve" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", delta=" + delta +
+            ", before=" + toString());
+
+        try {
+            return super.reserve(delta);
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized boolean update(long start, long delta) {
+        SB sb = new SB();
+
+        sb.a("[op=update" +
+            ", grpId=" + grp.groupId() +
+            ", partId=" + partId +
+            ", delta=(" + start + "," + delta + ")" +
+            ", before=" + toString());
+
+        boolean updated = false;
+
+        try {
+            updated = super.update(start, delta);
+        }
+        finally {
+            log.debug(sb.a(", after=" + toString() +
+                ']').toString());
+        }
+
+        return updated;
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionTxUpdateCounterImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionTxUpdateCounterImpl.java
new file mode 100644
index 0000000..f8f236f
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionTxUpdateCounterImpl.java
@@ -0,0 +1,450 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.NavigableSet;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.processors.datastreamer.DataStreamerImpl;
+import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.typedef.F;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Update counter implementation used for transactional cache groups.
+ */
+public class PartitionTxUpdateCounterImpl implements PartitionUpdateCounter {
+    /**
+     * Max allowed missed updates. Overflow will trigger critical failure handler to prevent OOM.
+     */
+    public static final int MAX_MISSED_UPDATES = 10_000;
+
+    /** Counter updates serialization version. */
+    private static final byte VERSION = 1;
+
+    /** Queue of finished out of order counter updates. */
+    private TreeSet<Item> queue = new TreeSet<>();
+
+    /** LWM. */
+    private final AtomicLong cntr = new AtomicLong();
+
+    /** HWM. */
+    private final AtomicLong reserveCntr = new AtomicLong();
+
+    /**
+     * Initial counter points to last sequential update after WAL recovery.
+     * @deprecated TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11794
+     */
+    @Deprecated private long initCntr;
+
+    /** {@inheritDoc} */
+    @Override public void init(long initUpdCntr, @Nullable byte[] cntrUpdData) {
+        cntr.set(initUpdCntr);
+
+        initCntr = initUpdCntr;
+
+        queue = fromBytes(cntrUpdData);
+    }
+
+    /** {@inheritDoc} */
+    @Override public long initial() {
+        return initCntr;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long get() {
+        return cntr.get();
+    }
+
+    /** */
+    protected synchronized long highestAppliedCounter() {
+        return queue.isEmpty() ? cntr.get() : queue.last().absolute();
+    }
+
+    /**
+     * @return Next update counter. For tx mode called by {@link DataStreamerImpl} IsolatedUpdater.
+     */
+    @Override public long next() {
+        long next = cntr.incrementAndGet();
+
+        reserveCntr.set(next);
+
+        return next;
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void update(long val) throws IgniteCheckedException {
+        // Absolute counter should be not less than last applied update.
+        // Otherwise supplier doesn't contain some updates and rebalancing couldn't restore consistency.
+        // Best behavior is to stop node by failure handler in such a case.
+        if (!gaps().isEmpty() && val < highestAppliedCounter())
+            throw new IgniteCheckedException("New value is incompatible with current update counter state. " +
+                "Most probably a node with most actual data is out of topology or data streamer is used in isolated " +
+                "mode (allowOverride=true) concurrently with normal cache operations [val=" + val +
+                ", locCntr=" + this + ']');
+
+        long cur = cntr.get();
+
+        // Reserved update counter is updated only on exchange or in non-tx mode.
+        reserveCntr.set(Math.max(cur, val));
+
+        if (val <= cur)
+            return;
+
+        cntr.set(val);
+
+        if (!queue.isEmpty())
+            queue.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized boolean update(long start, long delta) {
+        long cur = cntr.get(), next;
+
+        if (cur > start)
+            return false;
+
+        if (cur < start) {
+            // Try merge with adjacent gaps in sequence.
+            Item tmp = new Item(start, delta);
+            Item ref = tmp;
+
+            NavigableSet<Item> set = queue.headSet(tmp, false);
+
+            // Merge with previous, possibly modifying previous.
+            if (!set.isEmpty()) {
+                Item last = set.last();
+
+                if (last.start + last.delta == start) {
+                    tmp = last;
+
+                    last.delta += delta;
+                }
+                else if (last.within(start) && last.within(start + delta - 1))
+                    return false;
+            }
+
+            // Merge with next, possibly modifying previous and removing next.
+            if (!(set = queue.tailSet(tmp, false)).isEmpty()) {
+                Item first = set.first();
+
+                if (tmp.start + tmp.delta == first.start) {
+                    if (ref != tmp) {
+                        tmp.delta += first.delta;
+
+                        set.pollFirst(); // Merge and remove obsolete head.
+                    }
+                    else {
+                        tmp = first;
+
+                        first.start = start;
+                        first.delta += delta;
+                    }
+                }
+            }
+
+            if (tmp != ref)
+                return true;
+
+            return offer(new Item(start, delta)); // backup node with gaps
+        }
+
+        while (true) {
+            boolean res = cntr.compareAndSet(cur, next = start + delta);
+
+            assert res;
+
+            Item peek = peek();
+
+            if (peek == null || peek.start != next)
+                return true;
+
+            Item item = poll();
+
+            assert peek == item;
+
+            start = item.start;
+            delta = item.delta;
+            cur = next;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void updateInitial(long start, long delta) {
+        long cntr0 = get();
+
+        assert start >= cntr0 : "Illegal update counters order: cur=" + cntr0 + ", new=" + start;
+
+        update(start, delta);
+
+        initCntr = get();
+    }
+
+    /** */
+    private Item poll() {
+        return queue.pollFirst();
+    }
+
+    /** */
+    private Item peek() {
+        return queue.isEmpty() ? null : queue.first();
+    }
+
+    /**
+     * @param item Item.
+     */
+    private boolean offer(Item item) {
+        if (queue.size() == MAX_MISSED_UPDATES) // Should trigger failure handler.
+            throw new IgniteException("Too many gaps [cntr=" + this + ']');
+
+        return queue.add(item);
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized GridLongList finalizeUpdateCounters() {
+        Item item = poll();
+
+        GridLongList gaps = null;
+
+        while (item != null) {
+            if (gaps == null)
+                gaps = new GridLongList((queue.size() + 1) * 2);
+
+            long start = cntr.get() + 1;
+            long end = item.start;
+
+            gaps.add(start);
+            gaps.add(end);
+
+            // Close pending ranges.
+            cntr.set(item.start + item.delta);
+
+            item = poll();
+        }
+
+        return gaps;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long reserve(long delta) {
+        long cntr = get();
+
+        long reserved = reserveCntr.getAndAdd(delta);
+
+        assert reserved >= cntr : "LWM after HWM: lwm=" + cntr + ", hwm=" + reserved;
+
+        return reserved;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long next(long delta) {
+        return cntr.getAndAdd(delta);
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized boolean sequential() {
+        return gaps().isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized @Nullable byte[] getBytes() {
+        if (queue.isEmpty())
+            return null;
+
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+            DataOutputStream dos = new DataOutputStream(bos);
+
+            dos.writeByte(VERSION);
+
+            int size = queue.size();
+
+            dos.writeInt(size);
+
+            for (Item item : queue) {
+                dos.writeLong(item.start);
+                dos.writeLong(item.delta);
+            }
+
+            bos.close();
+
+            return bos.toByteArray();
+        }
+        catch (IOException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /**
+     * @param raw Raw bytes.
+     */
+    private @Nullable TreeSet<Item> fromBytes(@Nullable byte[] raw) {
+        if (raw == null)
+            return new TreeSet<>();
+
+        TreeSet<Item> ret = new TreeSet<>();
+
+        try {
+            ByteArrayInputStream bis = new ByteArrayInputStream(raw);
+
+            DataInputStream dis = new DataInputStream(bis);
+
+            dis.readByte(); // Version.
+
+            int cnt = dis.readInt(); // Holes count.
+
+            while(cnt-- > 0)
+                ret.add(new Item(dis.readLong(), dis.readLong()));
+
+            return ret;
+        }
+        catch (IOException e) {
+            throw new IgniteException(e);
+        }
+    }
+
+    /** */
+    private TreeSet<Item> gaps() {
+        return queue;
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized void reset() {
+        cntr.set(0);
+
+        reserveCntr.set(0);
+
+        queue = new TreeSet<>();
+    }
+
+    /**
+     * Update counter task. Update from start value by delta value.
+     */
+    private static class Item implements Comparable<Item> {
+        /** */
+        private long start;
+
+        /** */
+        private long delta;
+
+        /**
+         * @param start Start value.
+         * @param delta Delta value.
+         */
+        private Item(long start, long delta) {
+            this.start = start;
+            this.delta = delta;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int compareTo(@NotNull Item o) {
+            return Long.compare(this.start, o.start);
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "Item [" +
+                "start=" + start +
+                ", delta=" + delta +
+                ']';
+        }
+
+        /** */
+        public long start() {
+            return start;
+        }
+
+        /** */
+        public long delta() {
+            return delta;
+        }
+
+        /** */
+        public long absolute() {
+            return start + delta;
+        }
+
+        /** */
+        public boolean within(long cntr) {
+            return cntr - start < delta;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            Item item = (Item)o;
+
+            if (start != item.start)
+                return false;
+
+            return  (delta != item.delta);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        PartitionTxUpdateCounterImpl cntr = (PartitionTxUpdateCounterImpl)o;
+
+        if (!queue.equals(cntr.queue))
+            return false;
+
+        return this.cntr.get() == cntr.cntr.get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long reserved() {
+        return reserveCntr.get();
+    }
+
+    /** {@inheritDoc} */
+    @Override public synchronized boolean empty() {
+        return get() == 0 && sequential();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Iterator<long[]> iterator() {
+        return F.iterator(queue.iterator(), item -> {
+            return new long[] {item.start, item.delta};
+        }, true);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return "Counter [lwm=" + get() + ", holes=" + queue +
+            ", maxApplied=" + highestAppliedCounter() + ", hwm=" + reserveCntr.get() + ']';
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionUpdateCounter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionUpdateCounter.java
old mode 100644
new mode 100755
index 39d8d5f..64aff27
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionUpdateCounter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/PartitionUpdateCounter.java
@@ -17,219 +17,116 @@
 
 package org.apache.ignite.internal.processors.cache;
 
-import java.util.TreeSet;
-import java.util.concurrent.atomic.AtomicLong;
-import org.apache.ignite.IgniteLogger;
+import java.util.Iterator;
+import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.util.GridLongList;
-import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
- * Partition update counter with MVCC delta updates capabilities.
+ * Partition update counter maintains three entities for tracking partition update state.
+   <ol>
+ *     <li><b>Low water mark (LWM)</b> or update counter - lowest applied sequential update number.</li>
+ *     <li><b>High water mark (HWM)</b> or reservation counter - highest seen but unapplied yet update number.</li>
+ *     <li>Out-of-order applied updates in range between LWM and HWM.</li>
+ * </ol>
  */
-public class PartitionUpdateCounter {
-    /** */
-    private IgniteLogger log;
-
-    /** Queue of counter update tasks*/
-    private final TreeSet<Item> queue = new TreeSet<>();
-
-    /** Counter. */
-    private final AtomicLong cntr = new AtomicLong();
-
-    /** Initial counter. */
-    private long initCntr;
-
+public interface PartitionUpdateCounter extends Iterable<long[]> {
     /**
-     * @param log Logger.
+     * Restores update counter state.
+     *
+     * @param initUpdCntr LWM.
+     * @param cntrUpdData Counter updates raw data.
      */
-    PartitionUpdateCounter(IgniteLogger log) {
-        this.log = log;
-    }
+    public void init(long initUpdCntr, @Nullable byte[] cntrUpdData);
 
     /**
-     * Sets init counter.
-     *
-     * @param updateCntr Init counter valus.
+     * @deprecated TODO LWM should be used as initial counter https://ggsystems.atlassian.net/browse/GG-17396
      */
-    public void init(long updateCntr) {
-        initCntr = updateCntr;
+    public long initial();
 
-        cntr.set(updateCntr);
-    }
+    /**
+     * Get LWM.
+     */
+    public long get();
 
     /**
-     * @return Initial counter value.
+     * Increment LWM by 1.
+     *
+     * @return New LWM.
      */
-    public long initial() {
-        return initCntr;
-    }
+    public long next();
 
     /**
-     * @return Current update counter value.
+     * Increment LWM by delta.
+     *
+     * @param delta Delta.
      */
-    public long get() {
-        return cntr.get();
-    }
+    public long next(long delta);
 
     /**
-     * Adds delta to current counter value.
+     * Increment HWM by delta.
      *
      * @param delta Delta.
-     * @return Value before add.
+     * @return New HWM.
      */
-    public long getAndAdd(long delta) {
-        return cntr.getAndAdd(delta);
-    }
+    public long reserve(long delta);
 
     /**
-     * @return Next update counter.
+     * Returns HWM.
      */
-    public long next() {
-        return cntr.incrementAndGet();
-    }
+    public long reserved();
 
     /**
-     * Sets value to update counter,
+     * Sets update counter to absolute value. All missed updates will be discarded.
      *
-     * @param val Values.
+     * @param val Absolute value.
+     * @throws IgniteCheckedException if counter cannot be set to passed value due to incompatibility with current state.
      */
-    public void update(long val) {
-        while (true) {
-            long val0 = cntr.get();
-
-            if (val0 >= val)
-                break;
-
-            if (cntr.compareAndSet(val0, val))
-                break;
-        }
-    }
+    public void update(long val) throws IgniteCheckedException;
 
     /**
-     * Updates counter by delta from start position.
+     * Applies counter update out of range. Update ranges must not intersect.
      *
-     * @param start Start.
+     * @param start Start (<= lwm).
      * @param delta Delta.
+     * @return {@code True} if update was actually applied.
      */
-    public synchronized void update(long start, long delta) {
-        long cur = cntr.get(), next;
-
-        if (cur > start) {
-            log.warning("Stale update counter task [cur=" + cur + ", start=" + start + ", delta=" + delta + ']');
-
-            return;
-        }
-
-        if (cur < start) {
-            // backup node with gaps
-            offer(new Item(start, delta));
-
-            return;
-        }
-
-        while (true) {
-            boolean res = cntr.compareAndSet(cur, next = start + delta);
-
-            assert res;
-
-            Item peek = peek();
-
-            if (peek == null || peek.start != next)
-                return;
-
-            Item item = poll();
-
-            assert peek == item;
-
-            start = item.start;
-            delta = item.delta;
-            cur = next;
-        }
-    }
+    public boolean update(long start, long delta);
 
     /**
-     * @param cntr Sets initial counter.
+     * Reset counter internal state to zero.
      */
-    public void updateInitial(long cntr) {
-        if (get() < cntr)
-            update(cntr);
-
-        initCntr = cntr;
-    }
+    public void reset();
 
     /**
-     * @return Retrieves the minimum update counter task from queue.
+     * @param start Counter.
+     * @param delta Delta.
+     * @deprecated TODO https://ggsystems.atlassian.net/browse/GG-17396
      */
-    private Item poll() {
-        return queue.pollFirst();
-    }
+    public void updateInitial(long start, long delta);
 
     /**
-     * @return Checks the minimum update counter task from queue.
+     * Flushes pending update counters closing all possible gaps.
+     *
+     * @return Even-length array of pairs [start, end] for each gap.
      */
-    private Item peek() {
-        return queue.isEmpty() ? null : queue.first();
+    public GridLongList finalizeUpdateCounters();
 
-    }
+    /** */
+    public @Nullable byte[] getBytes();
 
     /**
-     * @param item Adds update task to priority queue.
+     * @return {@code True} if counter has no missed updates.
      */
-    private void offer(Item item) {
-        queue.add(item);
-    }
+    public boolean sequential();
 
     /**
-     * Flushes pending update counters closing all possible gaps.
-     *
-     * @return Even-length array of pairs [start, end] for each gap.
+     * @return {@code True} if counter has not seen any update.
      */
-    public synchronized GridLongList finalizeUpdateCounters() {
-        Item item = poll();
-
-        GridLongList gaps = null;
-
-        while (item != null) {
-            if (gaps == null)
-                gaps = new GridLongList((queue.size() + 1) * 2);
-
-            long start = cntr.get() + 1;
-            long end = item.start;
-
-            gaps.add(start);
-            gaps.add(end);
-
-            // Close pending ranges.
-            update(item.start + item.delta);
-
-            item = poll();
-        }
-
-        return gaps;
-    }
+    public boolean empty();
 
     /**
-     * Update counter task. Update from start value by delta value.
+     * @return Iterator for pairs [start, delta] for each out-of-order update in the update counter sequence.
      */
-    private static class Item implements Comparable<Item> {
-        /** */
-        private final long start;
-
-        /** */
-        private final long delta;
-
-        /**
-         * @param start Start value.
-         * @param delta Delta value.
-         */
-        private Item(long start, long delta) {
-            this.start = start;
-            this.delta = delta;
-        }
-
-        /** {@inheritDoc} */
-        @Override public int compareTo(@NotNull Item o) {
-            return Long.compare(this.start, o.start);
-        }
-    }
+    @Override public Iterator<long[]> iterator();
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/WalStateManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/WalStateManager.java
index 9d962db..335caf3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/WalStateManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/WalStateManager.java
@@ -417,7 +417,7 @@ public class WalStateManager extends GridCacheSharedManagerAdapter {
                     if (hasNonEmptyOwning)
                         break;
 
-                    if (locPart.updateCounter() > 0) {
+                    if (!locPart.isEmpty()) {
                         hasNonEmptyOwning = true;
 
                         break;
@@ -435,9 +435,8 @@ public class WalStateManager extends GridCacheSharedManagerAdapter {
                     ", grpId=" + grp.groupId() + ", hasOwning=" + hasOwning + ", hasMoving=" + hasMoving +
                     ", WALState=" + grp.walEnabled() + ", parts=" + parts);
 
-            if (hasOwning && !grp.localWalEnabled()) {
+            if (hasOwning && !grp.localWalEnabled())
                 grpsToEnableWal.add(grp.groupId());
-            }
             else if (hasMoving && !hasOwning && grp.localWalEnabled()) {
                 grpsToDisableWal.add(grp.groupId());
 
@@ -498,10 +497,13 @@ public class WalStateManager extends GridCacheSharedManagerAdapter {
                 tmpDisabledWal = null;
             }
 
+            // Pending updates in groups with disabled WAL are not protected from crash.
+            // Need to trigger checkpoint for attempt to persist them.
             CheckpointFuture cpFut = triggerCheckpoint("wal-local-state-changed-rebalance-finished-" + topVer);
 
             assert cpFut != null;
 
+            // It's safe to switch partitions to owning state only if checkpoint was successfully finished.
             cpFut.finishFuture().listen(new IgniteInClosureX<IgniteInternalFuture>() {
                 @Override public void applyx(IgniteInternalFuture future) {
                     for (Integer grpId0 : session0.disabledGrps) {
@@ -511,12 +513,12 @@ public class WalStateManager extends GridCacheSharedManagerAdapter {
                             grp.topology().ownMoving(topVer);
                         else if (log.isDebugEnabled())
                             log.debug("Cache group was destroyed before checkpoint finished, [grpId=" + grpId0 + ']');
-
                     }
 
                     if (log.isDebugEnabled())
                         log.debug("Refresh partitions due to rebalance finished");
 
+                    // Trigger exchange for switching to ideal assignment when all nodes are ready.
                     cctx.exchange().refreshPartitions();
                 }
             });
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 3e68306..22e3dd5 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
@@ -915,12 +915,11 @@ public abstract class GridDistributedTxRemoteAdapter extends IgniteTxAdapter
                 TxCounters counters = txCounters(false);
 
                 if (counters != null)
-                    cctx.tm().txHandler().applyPartitionsUpdatesCounters(counters.updateCounters());
+                    cctx.tm().txHandler().applyPartitionsUpdatesCounters(counters.updateCounters(), true, false);
 
                 state(ROLLED_BACK);
 
                 cctx.mvccCaching().onTxFinished(this, false);
-
             }
         }
         catch (IgniteCheckedException | RuntimeException | Error e) {
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 a1eb01c..584ede2 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
@@ -47,6 +47,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.C1;
 import org.apache.ignite.internal.util.typedef.CI1;
 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.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteClosure;
@@ -98,8 +99,33 @@ public class GridDhtCacheEntry extends GridDistributedCacheEntry {
     /** {@inheritDoc} */
     @Override protected long nextPartitionCounter(AffinityTopologyVersion topVer,
         boolean primary,
+        boolean init,
         @Nullable Long primaryCntr) {
-        return locPart.nextUpdateCounter(cctx.cacheId(), topVer, primary, primaryCntr);
+        try {
+            return locPart.nextUpdateCounter(cctx.cacheId(), topVer, primary, init, primaryCntr);
+        }
+        catch (Throwable t) {
+            log.error("Failed to update counter for atomic cache [" +
+                ", initial=" + init +
+                ", primaryCntr=" + primaryCntr +
+                ", part=" + locPart + ']', t);
+
+            throw t;
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override  protected long nextPartitionCounter(IgniteInternalTx tx, @Nullable Long primaryCntr) {
+        try {
+            return locPart.nextUpdateCounter(cctx.cacheId(), tx, primaryCntr);
+        }
+        catch (Throwable t) {
+            log.error("Failed to update counter for tx cache [" +
+                ", primaryCntr=" + primaryCntr +
+                ", part=" + locPart + ", tx=" + CU.txString(tx) + ']', t);
+
+            throw t;
+        }
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
index b00ad56..dc29ebe 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishFuture.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.processors.cache.distributed.dht;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
 import java.util.UUID;
@@ -38,7 +37,6 @@ import org.apache.ignite.internal.processors.cache.distributed.GridDistributedTx
 import org.apache.ignite.internal.processors.cache.mvcc.MvccFuture;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
 import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
-import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -437,11 +435,6 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
 
             add(fut); // Append new future.
 
-            Collection<Long> updCntrs = new ArrayList<>(dhtMapping.entries().size());
-
-            for (IgniteTxEntry e : dhtMapping.entries())
-                updCntrs.add(e.updateCounter());
-
             GridDhtTxFinishRequest req = new GridDhtTxFinishRequest(
                 tx.nearNodeId(),
                 futId,
@@ -465,7 +458,7 @@ public final class GridDhtTxFinishFuture<K, V> extends GridCacheCompoundIdentity
                 tx.subjectId(),
                 tx.taskNameHash(),
                 tx.activeCachesDeploymentEnabled(),
-                updCntrs,
+                null,
                 false,
                 false,
                 mvccSnapshot,
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
index 04f411d..9498e32 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxFinishRequest.java
@@ -257,13 +257,6 @@ public class GridDhtTxFinishRequest extends GridDistributedTxFinishRequest {
             waitRemoteTxs,
             mvccSnapshot,
             updCntrs);
-
-        if (updateIdxs != null && !updateIdxs.isEmpty()) {
-            partUpdateCnt = new GridLongList(updateIdxs.size());
-
-            for (Long idx : updateIdxs)
-                partUpdateCnt.add(idx);
-        }
     }
 
     /**
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 fb485a5..83edb29 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
@@ -71,6 +71,7 @@ import org.apache.ignite.internal.processors.cache.mvcc.txlog.TxState;
 import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
 import org.apache.ignite.internal.processors.cache.transactions.IgniteTxEntry;
 import org.apache.ignite.internal.processors.cache.transactions.IgniteTxKey;
+import org.apache.ignite.internal.processors.cache.transactions.TxCounters;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.dr.GridDrType;
 import org.apache.ignite.internal.processors.timeout.GridTimeoutObjectAdapter;
@@ -1258,6 +1259,8 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
      *
      */
     private void prepare0() {
+        boolean error = false;
+
         try {
             if (tx.serializable() && tx.optimistic()) {
                 IgniteCheckedException err0;
@@ -1297,10 +1300,24 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
             // We are holding transaction-level locks for entries here, so we can get next write version.
             tx.writeVersion(cctx.versions().next(tx.topologyVersion()));
 
+            TxCounters counters = tx.txCounters(true);
+
             // Assign keys to primary nodes.
             if (!F.isEmpty(req.writes())) {
-                for (IgniteTxEntry write : req.writes())
-                    map(tx.entry(write.txKey()));
+                for (IgniteTxEntry write : req.writes()) {
+                    IgniteTxEntry entry = tx.entry(write.txKey());
+
+                    assert entry != null && entry.cached() != null : entry;
+
+                    // Counter shouldn't be reserved for mvcc, local cache entries, NOOP operations and NOOP transforms.
+                    if (!entry.cached().isLocal() && entry.op() != NOOP &&
+                        !(entry.op() == TRANSFORM &&
+                            (entry.entryProcessorCalculatedValue() == null || // Possible for txs over cachestore
+                                entry.entryProcessorCalculatedValue().get1() == NOOP)))
+                        counters.incrementUpdateCounter(entry.cacheId(), entry.cached().partition());
+
+                    map(entry);
+                }
             }
 
             if (!F.isEmpty(req.reads())) {
@@ -1312,6 +1329,12 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
                 return;
 
             if (last) {
+                if (!tx.txState().mvccEnabled()) {
+                    /** For MVCC counters are assigned on enlisting. */
+                    /** See usage of {@link TxCounters#incrementUpdateCounter(int, int)} ) */
+                    tx.calculatePartitionUpdateCounters();
+                }
+
                 recheckOnePhaseCommit();
 
                 if (tx.onePhaseCommit())
@@ -1320,8 +1343,14 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
                 sendPrepareRequests();
             }
         }
+        catch (Throwable t) {
+            error = true;
+
+            throw t;
+        }
         finally {
-            markInitialized();
+            if (!error) // Prevent marking future as initialized on error.
+                markInitialized();
         }
     }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/PartitionUpdateCountersMessage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/PartitionUpdateCountersMessage.java
index f30d51d..ba7b590 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/PartitionUpdateCountersMessage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/PartitionUpdateCountersMessage.java
@@ -19,9 +19,11 @@ package org.apache.ignite.internal.processors.cache.distributed.dht;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Map;
 import org.apache.ignite.internal.GridDirectTransient;
 import org.apache.ignite.internal.IgniteCodeGeneratingFail;
 import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.plugin.extensions.communication.Message;
 import org.apache.ignite.plugin.extensions.communication.MessageReader;
 import org.apache.ignite.plugin.extensions.communication.MessageWriter;
@@ -47,6 +49,10 @@ public class PartitionUpdateCountersMessage implements Message {
     @GridDirectTransient
     private int size;
 
+    /** Used for assigning counters to cache entries during tx finish. */
+    @GridDirectTransient
+    private Map<Integer, Long> counters;
+
     /** */
     public PartitionUpdateCountersMessage() {
         // No-op.
@@ -145,6 +151,24 @@ public class PartitionUpdateCountersMessage implements Message {
     }
 
     /**
+     * Calculate next counter for partition.
+     *
+     * @param partId Partition id.
+     *
+     * @return Next counter for partition.
+     */
+    public Long nextCounter(int partId) {
+        if (counters == null) {
+            counters = U.newHashMap(size);
+
+            for (int i = 0; i < size; i++)
+                counters.put(partition(i), initialCounter(i));
+        }
+
+        return counters.computeIfPresent(partId, (key, cntr) -> cntr + 1);
+    }
+
+    /**
      * Clears message.
      */
     public void clear() {
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 93602db..803681f 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
@@ -1025,8 +1025,10 @@ public class GridDhtPartitionDemander {
             try {
                 cached = cctx.cache().entryEx(entry.key(), topVer);
 
-                if (log.isTraceEnabled())
-                    log.trace("Rebalancing key [key=" + entry.key() + ", part=" + p + ", node=" + from.id() + ']');
+                if (log.isTraceEnabled()) {
+                    log.trace("Rebalancing key [key=" + entry.key() + ", part=" + p + ", fromNode=" +
+                        from.id() + ", grpId=" + grp.groupId() + ']');
+                }
 
                 if (preloadPred == null || preloadPred.apply(entry)) {
                     if (cached.initialValue(
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 7f52856..a216a16 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
@@ -445,6 +445,15 @@ class GridDhtPartitionSupplier {
                 log.info("Finished supplying rebalancing [" + supplyRoutineInfo(topicId, nodeId, demandMsg) + "]");
         }
         catch (Throwable t) {
+            if (iter != null && !iter.isClosed()) {
+                try {
+                    iter.close();
+                }
+                catch (IgniteCheckedException e) {
+                    t.addSuppressed(e);
+                }
+            }
+
             if (grp.shared().kernalContext().isStopping())
                 return;
 
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 0ec1b7c..b247cd2 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
@@ -297,7 +297,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
     @GridToStringExclude
     private volatile IgniteDhtPartitionHistorySuppliersMap partHistSuppliers = new IgniteDhtPartitionHistorySuppliersMap();
 
-    /** */
+    /** Reserved max available history for calculation of history supplier on coordinator. */
     private volatile Map<Integer, Map<Integer, Long>> partHistReserved;
 
     /** */
@@ -354,6 +354,9 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
     /** Discovery lag / Clocks discrepancy, calculated on coordinator when all single messages are received. */
     private T2<Long, UUID> discoveryLag;
 
+    /** TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11799 */
+    private Map<Integer, Set<Integer>> clearingPartitions;
+
     /**
      * @param cctx Cache context.
      * @param busyLock Busy lock.
@@ -1053,6 +1056,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
                             clientTop.fullUpdateCounters(),
                             Collections.emptySet(),
                             null,
+                            null,
                             null);
                     }
                     finally {
@@ -1436,6 +1440,8 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
             cctx.exchange().exchangerBlockingSectionEnd();
         }
 
+        clearingPartitions = new HashMap();
+
         timeBag.finishGlobalStage("WAL history reservation");
 
         // Skipping wait on local join is available when all cluster nodes have the same protocol.
@@ -1674,7 +1680,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
             cctx.exchange().exchangerBlockingSectionBegin();
 
             try {
-                // This avoids unnessesary waiting for rollback.
+                // This avoids unnecessary waiting for rollback.
                 partReleaseFut.get(curTimeout > 0 && !txRolledBack ?
                         Math.min(curTimeout, waitTimeout) : waitTimeout, TimeUnit.MILLISECONDS);
 
@@ -3119,10 +3125,10 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
                 continue;
 
             if (localReserved != null) {
-                Long localCntr = localReserved.get(p);
+                Long localHistCntr = localReserved.get(p);
 
-                if (localCntr != null && localCntr <= minCntr && maxCntrObj.nodes.contains(cctx.localNodeId())) {
-                    partHistSuppliers.put(cctx.localNodeId(), top.groupId(), p, localCntr);
+                if (localHistCntr != null && localHistCntr <= minCntr && maxCntrObj.nodes.contains(cctx.localNodeId())) {
+                    partHistSuppliers.put(cctx.localNodeId(), top.groupId(), p, localHistCntr);
 
                     haveHistory.add(p);
 
@@ -3153,7 +3159,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
 
         top.globalPartSizes(partSizes);
 
-        Map<UUID, Set<Integer>> partitionsToRebalance = top.resetOwners(ownersByUpdCounters, haveHistory);
+        Map<UUID, Set<Integer>> partitionsToRebalance = top.resetOwners(ownersByUpdCounters, haveHistory, this);
 
         for (Map.Entry<UUID, Set<Integer>> e : partitionsToRebalance.entrySet()) {
             UUID nodeId = e.getKey();
@@ -3457,13 +3463,8 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
                         && ((SnapshotDiscoveryMessage)discoveryCustomMessage).needAssignPartitions())
                     assignPartitionsStates();
             }
-            else {
-                if (exchCtx.events().hasServerJoin())
-                    assignPartitionsStates();
-
-                if (exchCtx.events().hasServerLeft())
-                    detectLostPartitions(resTopVer);
-            }
+            else if (exchCtx.events().hasServerJoin())
+                assignPartitionsStates();
 
             // Recalculate new affinity based on partitions availability.
             if (!exchCtx.mergeExchanges() && forceAffReassignment) {
@@ -3488,6 +3489,12 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
             GridDhtPartitionsFullMessage msg = createPartitionsMessage(true,
                 minVer.compareToIgnoreTimestamp(PARTIAL_COUNTERS_MAP_SINCE) >= 0);
 
+            // Lost partition detection should be done after full message is prepared otherwise in case of IGNORE policy
+            // lost partitions will be moved to OWNING state and after what send to other nodes resulting in
+            // wrong lost state calculation (another possibility to consider - calculate lost state only on coordinator).
+            if (firstDiscoEvt.type() != EVT_DISCOVERY_CUSTOM_EVT && exchCtx.events().hasServerLeft())
+                detectLostPartitions(resTopVer);
+
             if (exchCtx.mergeExchanges()) {
                 assert !centralizedAff;
 
@@ -3776,7 +3783,7 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
         int parallelismLvl = U.availableThreadCount(cctx.kernalContext(), GridIoPolicy.SYSTEM_POOL, 2);
 
         try {
-            U.<CacheGroupContext, Void>doInParallel(
+            U.<CacheGroupContext, Void>doInParallelUninterruptibly(
                 parallelismLvl,
                 cctx.kernalContext().getSystemExecutorService(),
                 nonLocalCacheGroups(),
@@ -4220,7 +4227,8 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
                             cntrMap,
                             msg.partsToReload(cctx.localNodeId(), grpId),
                             partsSizes.getOrDefault(grpId, Collections.emptyMap()),
-                            null);
+                            null,
+                            this);
                     }
                     else {
                         GridDhtPartitionTopology top = cctx.exchange().clientTopology(grpId, events().discoveryCache());
@@ -4233,7 +4241,8 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
                             cntrMap,
                             Collections.emptySet(),
                             null,
-                            null);
+                            null,
+                            this);
                     }
 
                     return null;
@@ -5018,6 +5027,40 @@ public class GridDhtPartitionsExchangeFuture extends GridDhtTopologyFutureAdapte
     }
 
     /**
+     * If partition is clearing or already cleared we need full rebalance even if supplier is exists.
+     * (it still could be used by other demanders)
+     *
+     * @param grp Group.
+     * @param part Partition.
+     */
+    public boolean isClearingPartition(CacheGroupContext grp, int part) {
+        if (!grp.persistenceEnabled())
+            return false;
+
+        synchronized (mux) {
+            if (clearingPartitions == null)
+                return false;
+
+            Set<Integer> parts = clearingPartitions.get(grp.groupId());
+
+            return parts != null && parts.contains(part);
+        }
+    }
+
+    /**
+     * @param grp Group.
+     * @param part Partition.
+     */
+    public void addClearingPartition(CacheGroupContext grp, int part) {
+        if (!grp.persistenceEnabled())
+            return;
+
+        synchronized (mux) {
+            clearingPartitions.computeIfAbsent(grp.groupId(), k -> new HashSet()).add(part);
+        }
+    }
+
+    /**
      *
      */
     private static class FinishState {
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 ff420a7..8ecb147 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
@@ -253,6 +253,10 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                 if (part.state() == RENTING) {
                     if (part.reserve()) {
                         part.moving();
+
+                        if (exchFut != null)
+                            exchFut.addClearingPartition(grp, part.id());
+
                         part.clearAsync();
 
                         part.release();
@@ -277,7 +281,8 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                         histSupplier = ctx.discovery().node(nodeId);
                 }
 
-                if (histSupplier != null) {
+                // Clearing partition should always be fully reloaded.
+                if (histSupplier != null && !exchFut.isClearingPartition(grp, p)) {
                     assert grp.persistenceEnabled();
                     assert remoteOwners(p, topVer).contains(histSupplier) : remoteOwners(p, topVer);
 
@@ -291,6 +296,7 @@ public class GridDhtPreloader extends GridCachePreloaderAdapter {
                         );
                     }
 
+                    // TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11790
                     msg.partitions().addHistorical(p, part.initialUpdateCounter(), countersMap.updateCounter(p), partitions);
                 }
                 else {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloaderAssignments.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloaderAssignments.java
index 6f98889..8783c73 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloaderAssignments.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPreloaderAssignments.java
@@ -81,7 +81,6 @@ public class GridDhtPreloaderAssignments extends ConcurrentHashMap<ClusterNode,
 
     /** {@inheritDoc} */
     @Override public String toString() {
-        return S.toString(GridDhtPreloaderAssignments.class, this, "exchId", exchangeId,
-            "super", super.toString());
+        return S.toString(GridDhtPreloaderAssignments.class, this, "super", super.toString());
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtDemandedPartitionsMap.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtDemandedPartitionsMap.java
index 9fe3c64..c1109b3 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtDemandedPartitionsMap.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/IgniteDhtDemandedPartitionsMap.java
@@ -22,6 +22,7 @@ import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.jetbrains.annotations.Nullable;
 
@@ -36,6 +37,7 @@ public class IgniteDhtDemandedPartitionsMap implements Serializable {
     private CachePartitionPartialCountersMap historical;
 
     /** Set of partitions that will be preloaded from all it's current data. */
+    @GridToStringInclude
     private Set<Integer> full;
 
     public IgniteDhtDemandedPartitionsMap(
@@ -47,6 +49,7 @@ public class IgniteDhtDemandedPartitionsMap implements Serializable {
     }
 
     public IgniteDhtDemandedPartitionsMap() {
+        // No-op.
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridClientPartitionTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridClientPartitionTopology.java
index 0298d31..8a8d7d0 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridClientPartitionTopology.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridClientPartitionTopology.java
@@ -729,7 +729,8 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
         @Nullable CachePartitionFullCountersMap cntrMap,
         Set<Integer> partsToReload,
         @Nullable Map<Integer, Long> partSizes,
-        @Nullable AffinityTopologyVersion msgTopVer) {
+        @Nullable AffinityTopologyVersion msgTopVer,
+        @Nullable GridDhtPartitionsExchangeFuture exchFut) {
         if (log.isDebugEnabled())
             log.debug("Updating full partition map [exchVer=" + exchangeVer + ", parts=" + fullMapString() + ']');
 
@@ -1162,7 +1163,9 @@ public class GridClientPartitionTopology implements GridDhtPartitionTopology {
     }
 
     /** {@inheritDoc} */
-    @Override public Map<UUID, Set<Integer>> resetOwners(Map<Integer, Set<UUID>> ownersByUpdCounters, Set<Integer> haveHistory) {
+    @Override public Map<UUID, Set<Integer>> resetOwners(Map<Integer, Set<UUID>> ownersByUpdCounters,
+        Set<Integer> haveHistory,
+        GridDhtPartitionsExchangeFuture exchFut) {
         Map<UUID, Set<Integer>> result = new HashMap<>();
 
         lock.writeLock().lock();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java
index c1f742e..a131a21 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java
@@ -53,12 +53,15 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridReservabl
 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.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.transactions.IgniteInternalTx;
+import org.apache.ignite.internal.processors.cache.transactions.TxCounters;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
 import org.apache.ignite.internal.util.GridLongList;
 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.LT;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteInClosure;
@@ -218,7 +221,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
 
             // Log partition creation for further crash recovery purposes.
             if (grp.walEnabled() && !recovery)
-                ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, state(), updateCounter()));
+                ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, state(), 0));
 
             // Inject row cache cleaner on store creation
             // Used in case the cache with enabled SqlOnheapCache is single cache at the cache group
@@ -396,9 +399,21 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
-     *
+     * TODO FIXME Get rid of deferred delete queue https://issues.apache.org/jira/browse/IGNITE-11704
      */
     public void cleanupRemoveQueue() {
+        if (state() == MOVING) {
+            if (rmvQueue.sizex() >= rmvQueueMaxSize) {
+                LT.warn(log, "Deletion queue cleanup for moving partition was delayed until rebalance is finished. " +
+                    "[grpId=" + this.grp.groupId() +
+                    ", partId=" + id() +
+                    ", grpParts=" + this.grp.affinity().partitions() +
+                    ", maxRmvQueueSize=" + rmvQueueMaxSize + ']');
+            }
+
+            return;
+        }
+
         while (rmvQueue.sizex() >= rmvQueueMaxSize) {
             RemovedEntryHolder item = rmvQueue.pollFirst();
 
@@ -535,7 +550,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
                 this.state.compareAndSet(state0, setPartState(state0, toState));
 
                 try {
-                    ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, toState, updateCounter()));
+                    ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, toState, 0));
                 }
                 catch (IgniteCheckedException e) {
                     U.error(log, "Error while writing to log", e);
@@ -560,7 +575,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
 
                 if (update) {
                     try {
-                        ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, toState, updateCounter()));
+                        ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, toState, 0));
                     }
                     catch (IgniteCheckedException e) {
                         U.error(log, "Failed to log partition state change to WAL.", e);
@@ -717,7 +732,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
             }
         }
 
-        ctx.evict().evictPartitionAsync(grp,this);
+        ctx.evict().evictPartitionAsync(grp, this);
     }
 
     /**
@@ -970,40 +985,82 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
+     * Returns new update counter for primary node or passed counter for backup node.
+     * <p>
+     * Used for non-tx cases.
+     * <p>
+     * Counter generation/update logic is delegated to counter implementation.
+     *
      * @param cacheId ID of cache initiated counter update.
      * @param topVer Topology version for current operation.
+     * @param init {@code True} if initial update.
      * @return Next update index.
      */
-    public long nextUpdateCounter(int cacheId, AffinityTopologyVersion topVer, boolean primary, @Nullable Long primaryCntr) {
-        long nextCntr = store.nextUpdateCounter();
+    public long nextUpdateCounter(int cacheId, AffinityTopologyVersion topVer, boolean primary, boolean init,
+        @Nullable Long primaryCntr) {
+        long nextCntr;
+
+        if (primaryCntr == null) // Primary node.
+            nextCntr = store.nextUpdateCounter();
+        else {
+            assert !init : "Initial update must generate a counter for partition " + this;
+
+            // Backup.
+            assert primaryCntr != 0;
+
+            store.updateCounter(nextCntr = primaryCntr);
+        }
 
         if (grp.sharedGroup())
-            grp.onPartitionCounterUpdate(cacheId, id, primaryCntr != null ? primaryCntr : nextCntr, topVer, primary);
+            grp.onPartitionCounterUpdate(cacheId, id, nextCntr, topVer, primary);
 
-        // This is first update in partition, we should log partition state information for further crash recovery.
-        if (nextCntr == 1) {
-            if (grp.persistenceEnabled() && grp.walEnabled())
-                try {
-                    ctx.wal().log(new PartitionMetaStateRecord(grp.groupId(), id, state(), 0));
-                }
-                catch (IgniteCheckedException e) {
-                    U.error(log, "Failed to log partition state snapshot to WAL.", e);
+        return nextCntr;
+    }
 
-                    ctx.kernalContext().failure().process(new FailureContext(FailureType.CRITICAL_ERROR, e));
-                }
+    /**
+     * Used for transactions.
+     *
+     * @param cacheId Cache id.
+     * @param tx Tx.
+     * @param primaryCntr Primary counter.
+     */
+    public long nextUpdateCounter(int cacheId, IgniteInternalTx tx, @Nullable Long primaryCntr) {
+        Long nextCntr;
+
+        if (primaryCntr != null)
+            nextCntr = primaryCntr;
+        else {
+            TxCounters txCounters = tx.txCounters(false);
+
+            assert txCounters != null : "Must have counters for tx [nearXidVer=" + tx.nearXidVersion() + ']';
+
+            // Null must never be returned on primary node.
+            nextCntr = txCounters.generateNextCounter(cacheId, id());
+
+            assert nextCntr != null : this;
         }
 
+        if (grp.sharedGroup())
+            grp.onPartitionCounterUpdate(cacheId, id, nextCntr, tx.topologyVersion(), tx.local());
+
         return nextCntr;
     }
 
     /**
-     * @return Current update counter.
+     * @return Current update counter (LWM).
      */
     public long updateCounter() {
         return store.updateCounter();
     }
 
     /**
+     * @return Current reserved counter (HWM).
+     */
+    public long reservedCounter() {
+        return store.reservedCounter();
+    }
+
+    /**
      * @param val Update counter value.
      */
     public void updateCounter(long val) {
@@ -1018,14 +1075,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     }
 
     /**
-     * @param val Initial update counter value.
-     */
-    public void initialUpdateCounter(long val) {
-        store.updateInitialCounter(val);
-    }
-
-    /**
-     * Updates MVCC cache update counter on primary node.
+     * Increments cache update counter on primary node.
      *
      * @param delta Value to be added to update counter.
      * @return Update counter value before update.
@@ -1040,8 +1090,15 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
      * @param start Start position
      * @param delta Delta.
      */
-    public void updateCounter(long start, long delta) {
-         store.updateCounter(start, delta);
+    public boolean updateCounter(long start, long delta) {
+        return store.updateCounter(start, delta);
+    }
+
+    /**
+     * Reset partition counters.
+     */
+    public void resetCounters() {
+        store.resetUpdateCounter();
     }
 
     /**
@@ -1086,7 +1143,8 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
                 try {
                     CacheDataRow row = it0.next();
 
-                    // Do not clear fresh rows in case of single partition clearing.
+                    // Do not clear fresh rows in case of partition reloading.
+                    // This is required because updates are possible to moving partition which is currently cleared.
                     if (row.version().compareTo(clearVer) >= 0 && (state() == MOVING && clear))
                         continue;
 
@@ -1256,7 +1314,8 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
             "state", state(),
             "reservations", reservations(),
             "empty", isEmpty(),
-            "createTime", U.format(createTime));
+            "createTime", U.format(createTime),
+            "cntr", dataStore().partUpdateCounter());
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopology.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopology.java
index 7b9c3c3..f907bf2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopology.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopology.java
@@ -292,6 +292,7 @@ public interface GridDhtPartitionTopology {
      * @param partsToReload Set of partitions that need to be reloaded.
      * @param msgTopVer Topology version from incoming message. This value is not null only for case message is not
      *      related to exchange. Value should be not less than previous 'Topology version from exchange'.
+     * @param exchFut Future which is not null for initial partition update on exchange.
      * @return {@code True} if local state was changed.
      */
     public boolean update(
@@ -300,7 +301,9 @@ public interface GridDhtPartitionTopology {
         @Nullable CachePartitionFullCountersMap cntrMap,
         Set<Integer> partsToReload,
         @Nullable Map<Integer, Long> partSizes,
-        @Nullable AffinityTopologyVersion msgTopVer);
+        @Nullable AffinityTopologyVersion msgTopVer,
+        @Nullable GridDhtPartitionsExchangeFuture exchFut
+    );
 
     /**
      * @param exchId Exchange ID.
@@ -359,7 +362,7 @@ public interface GridDhtPartitionTopology {
     public CachePartitionFullCountersMap fullUpdateCounters();
 
     /**
-     * @param skipZeros {@code True} for adding zero counter to map.
+     * @param skipZeros {@code True} to exclude zero counters from map.
      * @return Partition update counters.
      */
     public CachePartitionPartialCountersMap localUpdateCounters(boolean skipZeros);
@@ -420,13 +423,16 @@ public interface GridDhtPartitionTopology {
     /**
      * Calculates nodes and partitions which have non-actual state and must be rebalanced.
      * State of all current owners that aren't contained in the given {@code ownersByUpdCounters} will be reset to MOVING.
+     * Called on coordinator during assignment of partition states.
      *
      * @param ownersByUpdCounters Map (partition, set of node IDs that have most actual state about partition
      *                            (update counter is maximal) and should hold OWNING state for such partition).
      * @param haveHistory Set of partitions which have WAL history to rebalance.
+     * @param exchFut Exchange future for operation.
      * @return Map (nodeId, set of partitions that should be rebalanced <b>fully</b> by this node).
      */
-    public Map<UUID, Set<Integer>> resetOwners(Map<Integer, Set<UUID>> ownersByUpdCounters, Set<Integer> haveHistory);
+    public Map<UUID, Set<Integer>> resetOwners(Map<Integer, Set<UUID>> ownersByUpdCounters, Set<Integer> haveHistory,
+        GridDhtPartitionsExchangeFuture exchFut);
 
     /**
      * Callback on exchange done.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopologyImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopologyImpl.java
index 026a54f..b6b84d5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopologyImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtPartitionTopologyImpl.java
@@ -42,6 +42,7 @@ import org.apache.ignite.events.EventType;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.IgniteInterruptedCheckedException;
 import org.apache.ignite.internal.managers.discovery.DiscoCache;
+import org.apache.ignite.internal.pagemem.wal.record.RollbackRecord;
 import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheGroupContext;
@@ -428,7 +429,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                     }
                 }
                 else
-                    createPartitions(affVer, affAssignment, updateSeq);
+                    createPartitions(affVer, affAssignment, updateSeq, exchFut);
             }
             else {
                 // If preloader is disabled, then we simply clear out
@@ -478,8 +479,10 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
      * @param affVer Affinity version.
      * @param aff Affinity assignments.
      * @param updateSeq Update sequence.
+     * @param exchFut
      */
-    private void createPartitions(AffinityTopologyVersion affVer, List<List<ClusterNode>> aff, long updateSeq) {
+    private void createPartitions(AffinityTopologyVersion affVer, List<List<ClusterNode>> aff, long updateSeq,
+        GridDhtPartitionsExchangeFuture exchFut) {
         if (!grp.affinityNode())
             return;
 
@@ -493,8 +496,13 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                 if (localNode(p, aff)) {
                     // This will make sure that all non-existing partitions
                     // will be created in MOVING state.
+                    boolean existing = locParts.get(p) != null;
+
                     GridDhtLocalPartition locPart = getOrCreatePartition(p);
 
+                    if (existing && locPart.state() == MOVING && !locPart.isEmpty())
+                        exchFut.addClearingPartition(grp, p);
+
                     updateSeq = updateLocal(p, locPart.state(), updateSeq, affVer);
                 }
             }
@@ -758,7 +766,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                         if (partitionLocalNode(p, topVer)) {
                             // Prepare partition to rebalance if it's not happened on full map update phase.
                             if (locPart == null || locPart.state() == RENTING || locPart.state() == EVICTED)
-                                locPart = rebalancePartition(p, false);
+                                locPart = rebalancePartition(p, true, exchFut);
 
                             GridDhtPartitionState state = locPart.state();
 
@@ -772,6 +780,9 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                                             log.debug("Will not own partition (there are owners to rebalance from) " +
                                                 "[grp=" + grp.cacheOrGroupName() + ", p=" + p + ", owners = " + owners + ']');
                                     }
+
+                                    if (exchFut.isClearingPartition(grp, p))
+                                        locPart.clearAsync();
                                 }
                                 else
                                     updateSeq = updateLocal(p, locPart.state(), updateSeq, topVer);
@@ -1359,8 +1370,9 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
      */
     private boolean shouldOverridePartitionMap(GridDhtPartitionMap currentMap, GridDhtPartitionMap newMap) {
         return newMap != null &&
-                (newMap.topologyVersion().compareTo(currentMap.topologyVersion()) > 0 ||
-                 newMap.topologyVersion().compareTo(currentMap.topologyVersion()) == 0 && newMap.updateSequence() > currentMap.updateSequence());
+            (newMap.topologyVersion().compareTo(currentMap.topologyVersion()) > 0 ||
+                newMap.topologyVersion().compareTo(currentMap.topologyVersion()) == 0 &&
+                    newMap.updateSequence() > currentMap.updateSequence());
     }
 
     /** {@inheritDoc} */
@@ -1370,7 +1382,8 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
         @Nullable CachePartitionFullCountersMap incomeCntrMap,
         Set<Integer> partsToReload,
         @Nullable Map<Integer, Long> partSizes,
-        @Nullable AffinityTopologyVersion msgTopVer) {
+        @Nullable AffinityTopologyVersion msgTopVer,
+        @Nullable GridDhtPartitionsExchangeFuture exchFut) {
         if (log.isDebugEnabled()) {
             log.debug("Updating full partition map " +
                 "[grp=" + grp.cacheOrGroupName() + ", exchVer=" + exchangeVer + ", fullMap=" + fullMapString() + ']');
@@ -1408,17 +1421,21 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                             long updCntr = incomeCntrMap.updateCounter(part.id());
                             long curCntr = part.updateCounter();
 
-                            if (updCntr != 0 && updCntr > curCntr) {
+                            // Avoid zero counter update to empty partition to prevent lazy init.
+                            if (updCntr != 0 || curCntr != 0) {
                                 part.updateCounter(updCntr);
 
-                                if (log.isDebugEnabled())
-                                    log.debug("Partition update counter has updated [grp=" + grp.cacheOrGroupName() + ", p=" + part.id()
-                                        + ", state=" + part.state() + ", prevCntr=" + curCntr + ", nextCntr=" + updCntr + "]");
+                                if (updCntr > curCntr) {
+                                    if (log.isDebugEnabled())
+                                        log.debug("Partition update counter has updated [grp=" + grp.cacheOrGroupName() + ", p=" + part.id()
+                                            + ", state=" + part.state() + ", prevCntr=" + curCntr + ", nextCntr=" + updCntr + "]");
+                                }
                             }
                         }
                     }
                 }
 
+                // TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11800
                 if (exchangeVer != null) {
                     // Ignore if exchange already finished or new exchange started.
                     if (readyTopVer.compareTo(exchangeVer) > 0 || lastTopChangeVer.compareTo(exchangeVer) > 0) {
@@ -1557,6 +1574,9 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                     nodeMap != null &&
                     grp.persistenceEnabled() &&
                     readyTopVer.initialized()) {
+
+                    assert exchFut != null;
+
                     for (Map.Entry<Integer, GridDhtPartitionState> e : nodeMap.entrySet()) {
                         int p = e.getKey();
                         GridDhtPartitionState state = e.getValue();
@@ -1575,9 +1595,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                             }
                         }
                         else if (state == MOVING) {
-                            boolean haveHistory = !partsToReload.contains(p);
-
-                            rebalancePartition(p, haveHistory);
+                            rebalancePartition(p, partsToReload.contains(p), exchFut);
 
                             changed = true;
                         }
@@ -1698,12 +1716,21 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                         continue;
 
                     long updCntr = cntrMap.updateCounter(part.id());
+                    long locUpdCntr = part.updateCounter();
 
-                    if (updCntr > part.updateCounter())
+                    if (updCntr != 0 || locUpdCntr != 0) { // Avoid creation of empty partition.
                         part.updateCounter(updCntr);
-                    else if (part.updateCounter() > 0) {
+
+                        if (updCntr > locUpdCntr) {
+                            if (log.isDebugEnabled())
+                                log.debug("Partition update counter has updated [grp=" + grp.cacheOrGroupName() + ", p=" + part.id()
+                                    + ", state=" + part.state() + ", prevCntr=" + locUpdCntr + ", nextCntr=" + updCntr + "]");
+                        }
+                    }
+
+                    if (locUpdCntr > updCntr) {
                         cntrMap.initialUpdateCounter(part.id(), part.initialUpdateCounter());
-                        cntrMap.updateCounter(part.id(), part.updateCounter());
+                        cntrMap.updateCounter(part.id(), locUpdCntr);
                     }
                 }
                 finally {
@@ -2105,7 +2132,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                 if (recentlyLost != null) {
                     U.warn(log, "Detected lost partitions [grp=" + grp.cacheOrGroupName()
                         + ", parts=" + S.compact(recentlyLost)
-                        + ", plc=" + plc + "]");
+                        + ", plc=" + plc + ", topVer=" + resTopVer + "]");
                 }
 
                 if (lostParts != null && plc != PartitionLossPolicy.IGNORE)
@@ -2147,11 +2174,8 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                             if (marked) {
                                 updateLocal(locPart.id(), locPart.state(), updSeq, resTopVer);
 
-                                long updateCntr = locPart.updateCounter();
-
-                                //Set update counters to 0, for full rebalance.
-                                locPart.updateCounter(updateCntr, -updateCntr);
-                                locPart.initialUpdateCounter(0);
+                                // Reset counters to zero for triggering full rebalance.
+                                locPart.resetCounters();
                             }
                         }
                     }
@@ -2188,7 +2212,9 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
     }
 
     /** {@inheritDoc} */
-    @Override public Map<UUID, Set<Integer>> resetOwners(Map<Integer, Set<UUID>> ownersByUpdCounters, Set<Integer> haveHistory) {
+    @Override public Map<UUID, Set<Integer>> resetOwners(Map<Integer, Set<UUID>> ownersByUpdCounters,
+        Set<Integer> haveHistory,
+        GridDhtPartitionsExchangeFuture exchFut) {
         Map<UUID, Set<Integer>> result = new HashMap<>();
 
         ctx.database().checkpointReadLock();
@@ -2208,10 +2234,9 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                         continue;
 
                     if (!newOwners.contains(ctx.localNodeId())) {
-                        rebalancePartition(part, haveHistory.contains(part));
+                        rebalancePartition(part, !haveHistory.contains(part), exchFut);
 
-                        result.computeIfAbsent(ctx.localNodeId(), n -> new HashSet<>());
-                        result.get(ctx.localNodeId()).add(part);
+                        result.computeIfAbsent(ctx.localNodeId(), n -> new HashSet<>()).add(part);
                     }
                 }
 
@@ -2237,8 +2262,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                             if (partMap.nodeId().equals(ctx.localNodeId()))
                                 updateSeq.setIfGreater(partMap.updateSequence());
 
-                            result.computeIfAbsent(remoteNodeId, n -> new HashSet<>());
-                            result.get(remoteNodeId).add(part);
+                            result.computeIfAbsent(remoteNodeId, n -> new HashSet<>()).add(part);
                         }
                     }
                 }
@@ -2247,6 +2271,10 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                     UUID nodeId = entry.getKey();
                     Set<Integer> rebalancedParts = entry.getValue();
 
+                    // Add to wait groups to ensure late assignment switch after all partitions are rebalanced.
+                    for (Integer part : rebalancedParts)
+                        ctx.cache().context().affinity().addToWaitGroup(groupId(), part, nodeId, topologyVersionFuture().initialVersion());
+
                     if (!rebalancedParts.isEmpty()) {
                         Set<Integer> historical = rebalancedParts.stream()
                             .filter(haveHistory::contains)
@@ -2281,10 +2309,11 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
      * Prevents ongoing renting if required.
      *
      * @param p Partition id.
-     * @param haveHistory If {@code true} there is WAL history to rebalance partition,
-     *                    in other case partition will be cleared for full rebalance.
+     * @param clear If {@code true} partition have to be cleared before rebalance.
+     *              Required in case of full state transfer to handle removals on supplier.
+     * @param exchFut Future related to partition state change.
      */
-    private GridDhtLocalPartition rebalancePartition(int p, boolean haveHistory) {
+    private GridDhtLocalPartition rebalancePartition(int p, boolean clear, GridDhtPartitionsExchangeFuture exchFut) {
         GridDhtLocalPartition part = getOrCreatePartition(p);
 
         // Prevent renting.
@@ -2303,8 +2332,11 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
         if (part.state() != MOVING)
             part.moving();
 
-        if (!haveHistory)
+        if (clear) {
+            exchFut.addClearingPartition(grp, part.id());
+
             part.clearAsync();
+        }
 
         assert part.state() == MOVING : part;
 
@@ -2656,9 +2688,6 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
      * Pre-processes partition update counters before exchange.
      */
     @Override public void finalizeUpdateCounters() {
-        if (!grp.mvccEnabled())
-            return;
-
         // It is need to acquire checkpoint lock before topology lock acquiring.
         ctx.database().checkpointReadLock();
 
@@ -2680,6 +2709,25 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
                         GridLongList gaps = part.finalizeUpdateCounters();
 
                         if (gaps != null) {
+                            for (int j = 0; j < gaps.size() / 2; j++) {
+                                long gapStart = gaps.get(j * 2);
+                                long gapStop = gaps.get(j * 2 + 1);
+
+                                if (part.group().persistenceEnabled() &&
+                                    part.group().walEnabled() &&
+                                    !part.group().mvccEnabled()) {
+                                    RollbackRecord rec = new RollbackRecord(part.group().groupId(), part.id(),
+                                        gapStart - 1, gapStop - gapStart + 1);
+
+                                    try {
+                                        ctx.wal().log(rec);
+                                    }
+                                    catch (IgniteCheckedException e) {
+                                        throw new IgniteException(e);
+                                    }
+                                }
+                            }
+
                             for (GridCacheContext ctx0 : grp.caches())
                                 ctx0.continuousQueries().closeBackupUpdateCountersGaps(ctx0, part.id(), topVer, gaps);
                         }
@@ -2964,7 +3012,7 @@ public class GridDhtPartitionTopologyImpl implements GridDhtPartitionTopology {
      * Checks consistency after all operations.
      */
     private void consistencyCheck() {
-        // no-op
+        // No-op.
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/PartitionsEvictManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/PartitionsEvictManager.java
index 404e194..c458d3e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/PartitionsEvictManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/PartitionsEvictManager.java
@@ -70,7 +70,7 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
     private volatile boolean stop;
 
     /** Check stop eviction context. */
-    private final EvictionContext sharedEvictionContext = () -> stop;
+    private final EvictionContext sharedEvictionCtx = () -> stop;
 
     /** Number of maximum concurrent operations. */
     private volatile int threads;
@@ -94,13 +94,13 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
      *
      * @param grp Group context.
      */
-    public void onCacheGroupStopped(CacheGroupContext  grp){
-        GroupEvictionContext groupEvictionContext = evictionGroupsMap.remove(grp.groupId());
+    public void onCacheGroupStopped(CacheGroupContext grp){
+        GroupEvictionContext grpEvictionCtx = evictionGroupsMap.remove(grp.groupId());
 
-        if (groupEvictionContext != null){
-            groupEvictionContext.stop();
+        if (grpEvictionCtx != null){
+            grpEvictionCtx.stop();
 
-            groupEvictionContext.awaitFinishAll();
+            grpEvictionCtx.awaitFinishAll();
         }
     }
 
@@ -111,23 +111,23 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
      * @param part Partition to evict.
      */
     public void evictPartitionAsync(CacheGroupContext grp, GridDhtLocalPartition part) {
-        GroupEvictionContext groupEvictionContext = evictionGroupsMap.computeIfAbsent(
+        GroupEvictionContext grpEvictionCtx = evictionGroupsMap.computeIfAbsent(
             grp.groupId(), (k) -> new GroupEvictionContext(grp));
 
         // Check node stop.
-        if (groupEvictionContext.shouldStop())
+        if (grpEvictionCtx.shouldStop())
             return;
 
         int bucket;
 
         synchronized (mux) {
-            if (!groupEvictionContext.partIds.add(part.id()))
+            if (!grpEvictionCtx.partIds.add(part.id()))
                 return;
 
-            bucket = evictionQueue.offer(new PartitionEvictionTask(part, groupEvictionContext));
+            bucket = evictionQueue.offer(new PartitionEvictionTask(part, grpEvictionCtx));
         }
 
-        groupEvictionContext.totalTasks.incrementAndGet();
+        grpEvictionCtx.totalTasks.incrementAndGet();
 
         if (log.isDebugEnabled())
             log.debug("Partition has been scheduled for eviction [grp=" + grp.cacheOrGroupName()
@@ -143,7 +143,7 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
      */
     private void scheduleNextPartitionEviction(int bucket) {
         // Check node stop.
-        if (sharedEvictionContext.shouldStop())
+        if (sharedEvictionCtx.shouldStop())
             return;
 
         synchronized (mux) {
@@ -178,17 +178,17 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
                     // Print current eviction progress.
                     showProgress();
 
-                    GroupEvictionContext groupEvictionContext = evictionTask.groupEvictionCtx;
+                    GroupEvictionContext grpEvictionCtx = evictionTask.grpEvictionCtx;
 
                     // Check that group or node stopping.
-                    if (groupEvictionContext.shouldStop())
+                    if (grpEvictionCtx.shouldStop())
                         continue;
 
                     // Get permit for this task.
                     permits--;
 
                     // Register task future, may need if group or node will be stopped.
-                    groupEvictionContext.taskScheduled(evictionTask);
+                    grpEvictionCtx.taskScheduled(evictionTask);
 
                     evictionTask.finishFut.listen(f -> {
                         synchronized (mux) {
@@ -294,7 +294,7 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
 
         /** {@inheritDoc} */
         @Override public boolean shouldStop() {
-            return stop || sharedEvictionContext.shouldStop();
+            return stop || sharedEvictionCtx.shouldStop();
         }
 
         /**
@@ -384,35 +384,35 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
         private final long size;
 
         /** Eviction context. */
-        private final GroupEvictionContext groupEvictionCtx;
+        private final GroupEvictionContext grpEvictionCtx;
 
         /** */
         private final GridFutureAdapter<?> finishFut = new GridFutureAdapter<>();
 
         /**
          * @param part Partition.
-         * @param groupEvictionCtx Eviction context.
+         * @param grpEvictionCtx Eviction context.
          */
         private PartitionEvictionTask(
             GridDhtLocalPartition part,
-            GroupEvictionContext groupEvictionCtx
+            GroupEvictionContext grpEvictionCtx
         ) {
             this.part = part;
-            this.groupEvictionCtx = groupEvictionCtx;
+            this.grpEvictionCtx = grpEvictionCtx;
 
             size = part.fullSize();
         }
 
         /** {@inheritDoc} */
         @Override public void run() {
-            if (groupEvictionCtx.shouldStop()) {
+            if (grpEvictionCtx.shouldStop()) {
                 finishFut.onDone();
 
                 return;
             }
 
             try {
-                boolean success = part.tryClear(groupEvictionCtx);
+                boolean success = part.tryClear(grpEvictionCtx);
 
                 if (success) {
                     if (part.state() == GridDhtPartitionState.EVICTED && part.markForDestroy())
@@ -425,7 +425,7 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
 
                 // Re-offer partition if clear was unsuccessful due to partition reservation.
                 if (!success)
-                    evictPartitionAsync(groupEvictionCtx.grp, part);
+                    evictPartitionAsync(grpEvictionCtx.grp, part);
             }
             catch (Throwable ex) {
                 finishFut.onDone(ex);
@@ -435,9 +435,8 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
                         false,
                         true);
                 }
-                else{
+                else
                     LT.error(log, ex, "Partition eviction failed, this can cause grid hang.");
-                }
             }
         }
     }
@@ -523,9 +522,8 @@ public class PartitionsEvictManager extends GridCacheSharedManagerAdapter {
         int size(){
             int size = 0;
 
-            for (Queue<PartitionEvictionTask> queue : buckets) {
+            for (Queue<PartitionEvictionTask> queue : buckets)
                 size += queue.size();
-            }
 
             return size;
         }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
index 913e9bc..13b03fe 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFutureAdapter.java
@@ -147,7 +147,11 @@ public abstract class GridNearOptimisticTxPrepareFutureAdapter extends GridNearT
             }
 
             if (topFut.isDone()) {
-                topVer = topFut.topologyVersion();
+                if ((topVer = topFut.topologyVersion()) == null && topFut.error() != null) {
+                    onDone(topFut.error()); // Prevent stack overflow if topFut has error.
+
+                    return;
+                }
 
                 if (remap)
                     tx.onRemap(topVer);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
index b3b43b9..797221e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
@@ -3831,13 +3831,24 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements GridTimeou
             return chainFinishFuture(finishFut, true, true, false);
 
         if (!fastFinish) {
-            final IgniteInternalFuture<?> prepareFut = prepareNearTxLocal();
+            IgniteInternalFuture<?> prepareFut;
+            try {
+                prepareFut = prepareNearTxLocal();
+            }
+            catch (Throwable t) {
+                prepareFut = prepFut;
+
+                // Properly finish prepFut in case of unchecked error.
+                assert prepareFut != null; // Prep future must be set.
+
+                ((GridNearTxPrepareFutureAdapter)prepFut).onDone(t);
+            }
 
             prepareFut.listen(new CI1<IgniteInternalFuture<?>>() {
                 @Override public void apply(IgniteInternalFuture<?> f) {
                     try {
                         // Make sure that here are no exceptions.
-                        prepareFut.get();
+                        f.get();
 
                         fut.finish(true, true, false);
                     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
index 92f06a3..746b94a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRow.java
@@ -20,6 +20,9 @@ package org.apache.ignite.internal.processors.cache.persistence;
 import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccUpdateVersionAware;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 
 /**
@@ -55,4 +58,9 @@ public interface CacheDataRow extends MvccUpdateVersionAware, CacheSearchRow, St
      * @param key Key.
      */
     public void key(KeyCacheObject key);
+
+    /** {@inheritDoc} */
+    @Override public default IOVersions<? extends AbstractDataPageIO> ioVersions() {
+        return DataPageIO.VERSIONS;
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
index 3e3dc5c..b551f47 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/CacheDataRowAdapter.java
@@ -233,7 +233,7 @@ public class CacheDataRowAdapter implements CacheDataRow {
         IoStatisticsHolder statHolder,
         boolean readCacheId,
         RowData rowData,
-        IncompleteObject<?> incomplete,
+        @Nullable IncompleteObject<?> incomplete,
         boolean skipVer
     ) throws IgniteCheckedException {
         assert link != 0 : "link";
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index 498ccf1..3c40cdf 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -111,6 +111,7 @@ import org.apache.ignite.internal.pagemem.wal.record.MetastoreDataRecord;
 import org.apache.ignite.internal.pagemem.wal.record.MvccDataEntry;
 import org.apache.ignite.internal.pagemem.wal.record.MvccTxRecord;
 import org.apache.ignite.internal.pagemem.wal.record.PageSnapshot;
+import org.apache.ignite.internal.pagemem.wal.record.RollbackRecord;
 import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
 import org.apache.ignite.internal.pagemem.wal.record.WalRecordCacheGroupAware;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PageDeltaRecord;
@@ -142,7 +143,6 @@ import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemor
 import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionAllocationMap;
-import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionRecoverState;
 import org.apache.ignite.internal.processors.cache.persistence.snapshot.IgniteCacheSnapshotManager;
 import org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotOperation;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
@@ -2730,9 +2730,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
         Semaphore semaphore = new Semaphore(semaphorePertmits(exec));
 
+        Map<GroupPartitionId, Integer> partitionRecoveryStates = new HashMap<>();
+
         WALIterator it = cctx.wal().replay(status.startPtr, recordTypePredicate);
 
-        RestoreLogicalState restoreLogicalState = new RestoreLogicalState(status, it, lastArchivedSegment, cacheGroupsPredicate);
+        RestoreLogicalState restoreLogicalState =
+            new RestoreLogicalState(status, it, lastArchivedSegment, cacheGroupsPredicate, partitionRecoveryStates);
 
         try {
             while (it.hasNextX()) {
@@ -2742,6 +2745,36 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     break;
 
                 switch (rec.type()) {
+                    case CHECKPOINT_RECORD: // Calculate initial partition states
+                        CheckpointRecord cpRec = (CheckpointRecord)rec;
+
+                        for (Map.Entry<Integer, CacheState> entry : cpRec.cacheGroupStates().entrySet()) {
+                            CacheState cacheState = entry.getValue();
+
+                            for (int i = 0; i < cacheState.size(); i++) {
+                                int partId = cacheState.partitionByIndex(i);
+                                byte state = cacheState.stateByIndex(i);
+
+                                partitionRecoveryStates.put(new GroupPartitionId(entry.getKey(), partId), (int)state);
+                            }
+                        }
+
+                        break;
+
+                    case ROLLBACK_TX_RECORD:
+                        RollbackRecord rbRec = (RollbackRecord)rec;
+
+                        CacheGroupContext ctx = cctx.cache().cacheGroup(rbRec.groupId());
+
+                        if (ctx != null && !ctx.isLocal()) {
+                            ctx.topology().forceCreatePartition(rbRec.partitionId());
+
+                            ctx.offheap().onPartitionInitialCounterUpdated(rbRec.partitionId(), rbRec.start(),
+                                rbRec.range());
+                        }
+
+                        break;
+
                     case MVCC_DATA_RECORD:
                     case DATA_RECORD:
                     case ENCRYPTED_DATA_RECORD:
@@ -2773,7 +2806,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                                 }
 
                                 applied.incrementAndGet();
-                            }, cacheId, dataEntry.partitionId(), exec, semaphore);
+                            }, cacheDesc.groupId(), dataEntry.partitionId(), exec, semaphore);
                         }
 
                         break;
@@ -2794,11 +2827,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                             metaStateRecord.groupId(), metaStateRecord.partitionId()
                         );
 
-                        PartitionRecoverState state = new PartitionRecoverState(
-                            (int)metaStateRecord.state(), metaStateRecord.updateCounter()
-                        );
-
-                        restoreLogicalState.partitionRecoveryStates.put(groupPartitionId, state);
+                        restoreLogicalState.partitionRecoveryStates.put(groupPartitionId, (int)metaStateRecord.state());
 
                         break;
 
@@ -2925,7 +2954,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 }
 
                 if (dataEntry.partitionCounter() != 0)
-                    cacheCtx.offheap().onPartitionInitialCounterUpdated(partId, dataEntry.partitionCounter());
+                    cacheCtx.offheap().onPartitionInitialCounterUpdated(partId, dataEntry.partitionCounter() - 1, 1);
 
                 break;
 
@@ -2944,7 +2973,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     cacheCtx.offheap().remove(cacheCtx, dataEntry.key(), partId, locPart);
 
                 if (dataEntry.partitionCounter() != 0)
-                    cacheCtx.offheap().onPartitionInitialCounterUpdated(partId, dataEntry.partitionCounter());
+                    cacheCtx.offheap().onPartitionInitialCounterUpdated(partId, dataEntry.partitionCounter() - 1, 1);
 
                 break;
 
@@ -5347,13 +5376,13 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         assert pageStore != null : "Persistent cache should have initialize page store manager.";
 
         for (int p = 0; p < grp.affinity().partitions(); p++) {
-            if (grp.topology().localPartition(p) != null) {
-                GridDhtLocalPartition part = grp.topology().localPartition(p);
+            GridDhtLocalPartition part = grp.topology().localPartition(p);
 
+            if (part != null) {
                 log.info("Partition [grp=" + grp.cacheOrGroupName()
                     + ", id=" + p
                     + ", state=" + part.state()
-                    + ", counter=" + part.updateCounter()
+                    + ", counter=" + part.dataStore().partUpdateCounter()
                     + ", size=" + part.fullSize() + "]");
 
                 continue;
@@ -5379,17 +5408,17 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 try {
                     PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.forPage(pageAddr);
 
-                    GridDhtPartitionState partitionState = fromOrdinal(io.getPartitionState(pageAddr));
+                    GridDhtPartitionState partState = fromOrdinal(io.getPartitionState(pageAddr));
 
-                    String state = partitionState != null ? partitionState.toString() : "N/A";
+                    String state = partState != null ? partState.toString() : "N/A";
 
-                    long updateCounter = io.getUpdateCounter(pageAddr);
+                    long updateCntr = io.getUpdateCounter(pageAddr);
                     long size = io.getSize(pageAddr);
 
                     log.info("Partition [grp=" + grp.cacheOrGroupName()
                             + ", id=" + p
                             + ", state=" + state
-                            + ", counter=" + updateCounter
+                            + ", counter=" + updateCntr
                             + ", size=" + size + "]");
                 }
                 finally {
@@ -5453,11 +5482,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /**
-     * @return WAL records predicate that passes only logical and mixed WAL records.
+     * @return WAL records predicate that passes only logical and mixed WAL records +
+     * CP record (used for restoring initial partition states).
      */
     private IgniteBiPredicate<WALRecord.RecordType, WALPointer> logicalRecords() {
         return (type, ptr) -> type.purpose() == WALRecord.RecordPurpose.LOGICAL
-            || type.purpose() == WALRecord.RecordPurpose.MIXED;
+            || type.purpose() == WALRecord.RecordPurpose.MIXED || type == CHECKPOINT_RECORD;
     }
 
     /**
@@ -5670,19 +5700,23 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
      */
     public class RestoreLogicalState extends RestoreStateContext {
         /** States of partitions recovered during applying logical updates. */
-        private final Map<GroupPartitionId, PartitionRecoverState> partitionRecoveryStates = new HashMap<>();
+        private final Map<GroupPartitionId, Integer> partitionRecoveryStates;
 
         /**
          * @param lastArchivedSegment Last archived segment index.
+         * @param partitionRecoveryStates Initial partition recovery states.
          */
-        public RestoreLogicalState(CheckpointStatus status, WALIterator iterator, long lastArchivedSegment, IgnitePredicate<Integer> cacheGroupsPredicate) {
+        public RestoreLogicalState(CheckpointStatus status, WALIterator iterator, long lastArchivedSegment,
+            IgnitePredicate<Integer> cacheGroupsPredicate, Map<GroupPartitionId, Integer> partitionRecoveryStates) {
             super(status, iterator, lastArchivedSegment, cacheGroupsPredicate);
+
+            this.partitionRecoveryStates = partitionRecoveryStates;
         }
 
         /**
          * @return Map of restored partition states for cache groups.
          */
-        public Map<GroupPartitionId, PartitionRecoverState> partitionRecoveryStates() {
+        public Map<GroupPartitionId, Integer> partitionRecoveryStates() {
             return Collections.unmodifiableMap(partitionRecoveryStates);
         }
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
index 84a23d3..942a713 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.processors.cache.persistence;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -31,6 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import javax.cache.processor.EntryProcessor;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
+import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.failure.FailureContext;
 import org.apache.ignite.failure.FailureType;
 import org.apache.ignite.internal.pagemem.FullPageId;
@@ -45,9 +47,10 @@ import org.apache.ignite.internal.pagemem.wal.WALPointer;
 import org.apache.ignite.internal.pagemem.wal.record.DataEntry;
 import org.apache.ignite.internal.pagemem.wal.record.DataRecord;
 import org.apache.ignite.internal.pagemem.wal.record.PageSnapshot;
+import org.apache.ignite.internal.pagemem.wal.record.RollbackRecord;
 import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageInitRecord;
-import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdatePartitionDataRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdatePartitionDataRecordV2;
 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.CacheEntryPredicate;
@@ -59,6 +62,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheMvccEntryInfo;
 import org.apache.ignite.internal.processors.cache.GridCacheTtlManager;
 import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManagerImpl;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.PartitionUpdateCounter;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.CachePartitionPartialCountersMap;
 import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.IgniteHistoricalIterator;
 import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition;
@@ -66,12 +70,15 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.topology.Grid
 import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccVersion;
 import org.apache.ignite.internal.processors.cache.persistence.freelist.CacheFreeListImpl;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.AbstractFreeList;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.SimpleDataRow;
 import org.apache.ignite.internal.processors.cache.persistence.migration.UpgradePendingTreeToPerPartitionTask;
 import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryEx;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.GroupPartitionId;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.PagesAllocationRange;
 import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionAllocationMap;
-import org.apache.ignite.internal.processors.cache.persistence.partstate.PartitionRecoverState;
+import org.apache.ignite.internal.processors.cache.persistence.partstorage.PartitionMetaStorage;
+import org.apache.ignite.internal.processors.cache.persistence.partstorage.PartitionMetaStorageImpl;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageMetaIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionCountersIO;
@@ -267,14 +274,18 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         if (rowStore0 != null) {
             ((CacheFreeListImpl)rowStore0.freeList()).saveMetadata();
 
+            PartitionMetaStorage<SimpleDataRow> partStore = store.partStorage();
+
             long updCntr = store.updateCounter();
             long size = store.fullSize();
             long rmvId = globalRemoveId().get();
 
+            byte[] updCntrsBytes = store.partUpdateCounter().getBytes();
+
             PageMemoryEx pageMem = (PageMemoryEx)grp.dataRegion().pageMemory();
             IgniteWriteAheadLogManager wal = this.ctx.wal();
 
-            if (size > 0 || updCntr > 0) {
+            if (size > 0 || updCntr > 0 || !store.partUpdateCounter().sequential()) {
                 GridDhtPartitionState state = null;
 
                 // localPartition will not acquire writeLock here because create=false.
@@ -313,7 +324,46 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                     boolean changed = false;
 
                     try {
-                        PagePartitionMetaIO io = PageIO.getPageIO(partMetaPageAddr);
+                        PagePartitionMetaIOV2 io = PageIO.getPageIO(partMetaPageAddr);
+
+                        long link = io.getGapsLink(partMetaPageAddr);
+
+                        if (updCntrsBytes == null && link != 0) {
+                            partStore.removeDataRowByLink(link, grp.statisticsHolderData());
+
+                            io.setGapsLink(partMetaPageAddr, (link = 0));
+
+                            changed = true;
+                        }
+                        else if (updCntrsBytes != null && link == 0) {
+                            SimpleDataRow row = new SimpleDataRow(store.partId(), updCntrsBytes);
+
+                            partStore.insertDataRow(row, grp.statisticsHolderData());
+
+                            io.setGapsLink(partMetaPageAddr, (link = row.link()));
+
+                            changed = true;
+                        }
+                        else if (updCntrsBytes != null && link != 0) {
+                            byte[] prev = partStore.readRow(link);
+
+                            assert prev != null : "Read null gaps using link=" + link;
+
+                            if (!Arrays.equals(prev, updCntrsBytes)) {
+                                partStore.removeDataRowByLink(link, grp.statisticsHolderData());
+
+                                SimpleDataRow row = new SimpleDataRow(store.partId(), updCntrsBytes);
+
+                                partStore.insertDataRow(row, grp.statisticsHolderData());
+
+                                io.setGapsLink(partMetaPageAddr, (link = row.link()));
+
+                                changed = true;
+                            }
+                        }
+
+                        if (changed)
+                            partStore.saveMetadata();
 
                         changed |= io.setUpdateCounter(partMetaPageAddr, updCntr);
                         changed |= io.setGlobalRemoveId(partMetaPageAddr, rmvId);
@@ -386,7 +436,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                             pageCnt = io.getCandidatePageCount(partMetaPageAddr);
 
                         if (changed && PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, partMetaId, partMetaPage, wal, null))
-                            wal.log(new MetaPageUpdatePartitionDataRecord(
+                            wal.log(new MetaPageUpdatePartitionDataRecordV2(
                                 grpId,
                                 partMetaId,
                                 updCntr,
@@ -394,7 +444,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                                 (int)size, // TODO: Partition size may be long
                                 cntrsPageId,
                                 state == null ? -1 : (byte)state.ordinal(),
-                                pageCnt
+                                pageCnt,
+                                link
                             ));
                     }
                     finally {
@@ -413,7 +464,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
-    @Override public long restorePartitionStates(Map<GroupPartitionId, PartitionRecoverState> partitionRecoveryStates) throws IgniteCheckedException {
+    @Override public long restorePartitionStates(Map<GroupPartitionId, Integer> partitionRecoveryStates) throws IgniteCheckedException {
         if (grp.isLocal() || !grp.affinityNode() || !grp.dataRegion().config().isPersistenceEnabled())
             return 0;
 
@@ -425,7 +476,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         PageMemoryEx pageMem = (PageMemoryEx)grp.dataRegion().pageMemory();
 
         for (int p = 0; p < grp.affinity().partitions(); p++) {
-            PartitionRecoverState recoverState = partitionRecoveryStates.get(new GroupPartitionId(grp.groupId(), p));
+            Integer recoverState = partitionRecoveryStates.get(new GroupPartitionId(grp.groupId(), p));
 
             if (ctx.pageStore().exists(grp.groupId(), p)) {
                 ctx.pageStore().ensure(grp.groupId(), p);
@@ -446,7 +497,8 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                 GridDhtLocalPartition part = grp.topology().forceCreatePartition(p);
 
-                onPartitionInitialCounterUpdated(p, 0);
+                // Triggers initialization of existing(having datafile) partition before acquiring cp read lock.
+                part.dataStore().init();
 
                 ctx.database().checkpointReadLock();
 
@@ -463,17 +515,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                             PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.forPage(pageAddr);
 
                             if (recoverState != null) {
-                                io.setPartitionState(pageAddr, (byte) recoverState.stateId());
+                                io.setPartitionState(pageAddr, (byte) recoverState.intValue());
 
-                                changed = updateState(part, recoverState.stateId());
-
-                                if (recoverState.stateId() == GridDhtPartitionState.OWNING.ordinal()
-                                    || (recoverState.stateId() == GridDhtPartitionState.MOVING.ordinal()
-                                    && part.initialUpdateCounter() < recoverState.updateCounter())) {
-                                    part.initialUpdateCounter(recoverState.updateCounter());
-
-                                    changed = true;
-                                }
+                                changed = updateState(part, recoverState);
 
                                 if (log.isDebugEnabled())
                                     log.debug("Restored partition state (from WAL) " +
@@ -503,12 +547,10 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                     ctx.database().checkpointReadUnlock();
                 }
             }
-            else if (recoverState != null) {
+            else if (recoverState != null && recoverState >= 0) { // Pre-create partition if having valid state.
                 GridDhtLocalPartition part = grp.topology().forceCreatePartition(p);
 
-                onPartitionInitialCounterUpdated(p, recoverState.updateCounter());
-
-                updateState(part, recoverState.stateId());
+                updateState(part, recoverState);
 
                 processed++;
 
@@ -816,15 +858,12 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /** {@inheritDoc} */
-    @Override public void onPartitionInitialCounterUpdated(int part, long cntr) {
+    @Override public void onPartitionInitialCounterUpdated(int part, long start, long delta) {
         CacheDataStore store = partDataStores.get(part);
 
         assert store != null;
 
-        long oldCnt = store.initialUpdateCounter();
-
-        if (oldCnt < cntr)
-            store.updateInitialCounter(cntr);
+        store.updateInitialCounter(start, delta);
     }
 
     /** {@inheritDoc} */
@@ -911,6 +950,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                 return new Metas(
                     new RootPage(new FullPageId(metastoreRoot, grpId), allocated),
                     new RootPage(new FullPageId(reuseListRoot, grpId), allocated),
+                    null,
                     null);
             }
             finally {
@@ -951,7 +991,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
         WALIterator it = grp.shared().wal().replay(minPtr);
 
-        WALHistoricalIterator iterator = new WALHistoricalIterator(grp, partCntrs, it);
+        WALHistoricalIterator iterator = new WALHistoricalIterator(log, grp, partCntrs, it);
 
         // Add historical partitions which are unabled to reserve to missing set.
         missing.addAll(iterator.missingParts);
@@ -1024,7 +1064,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         for (CacheDataStore store : partDataStores.values()) {
             assert store instanceof GridCacheDataStore;
 
-            CacheFreeListImpl freeList = ((GridCacheDataStore)store).freeList;
+            AbstractFreeList freeList = ((GridCacheDataStore)store).freeList;
 
             if (freeList == null)
                 continue;
@@ -1046,7 +1086,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         for (CacheDataStore store : partDataStores.values()) {
             assert store instanceof GridCacheDataStore;
 
-            CacheFreeListImpl freeList = ((GridCacheDataStore)store).freeList;
+            AbstractFreeList freeList = ((GridCacheDataStore)store).freeList;
 
             if (freeList == null)
                 continue;
@@ -1094,6 +1134,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         /** */
         private static final long serialVersionUID = 0L;
 
+        /** Logger. */
+        private IgniteLogger log;
+
         /** Cache context. */
         private final CacheGroupContext grp;
 
@@ -1118,24 +1161,34 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         /** */
         private DataEntry next;
 
-        /** Flag indicates that partition belongs to current {@link #next} is finished and no longer needs to rebalance. */
-        private boolean reachedPartitionEnd;
+        /**
+         * Rebalanced counters in the range from initialUpdateCntr to updateCntr.
+         * Invariant: initUpdCntr[idx] + rebalancedCntrs[idx] = updateCntr[idx]
+         */
+        private long[] rebalancedCntrs;
 
-        /** Flag indicates that update counters for requested partitions have been reached and done.
-         *  It means that no further iteration is needed. */
-        private boolean doneAllPartitions;
+        /** A partition what will be finished on next iteration. */
+        private int donePart = -1;
 
         /**
+         * @param log Logger.
          * @param grp Cache context.
          * @param walIt WAL iterator.
          */
-        private WALHistoricalIterator(CacheGroupContext grp, CachePartitionPartialCountersMap partMap, WALIterator walIt) {
+        private WALHistoricalIterator(IgniteLogger log, CacheGroupContext grp, CachePartitionPartialCountersMap partMap,
+            WALIterator walIt) {
+            this.log = log;
             this.grp = grp;
             this.partMap = partMap;
             this.walIt = walIt;
 
             cacheIds = grp.cacheIds();
 
+            rebalancedCntrs = new long[partMap.size()];
+
+            for (int i = 0; i < rebalancedCntrs.length; i++)
+                rebalancedCntrs[i] = partMap.initialUpdateCounterAt(i);
+
             reservePartitions();
 
             advance();
@@ -1194,13 +1247,19 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
             CacheDataRow val = new DataEntryRow(next);
 
-            if (reachedPartitionEnd) {
-                doneParts.add(next.partitionId());
+            if (donePart != -1) {
+                int pIdx = partMap.partitionIndex(donePart);
+
+                if (log.isDebugEnabled()) {
+                    log.debug("Partition done [grpId=" + grp.groupId() +
+                        ", partId=" + donePart +
+                        ", from=" + partMap.initialUpdateCounterAt(pIdx) +
+                        ", to=" + partMap.updateCounterAt(pIdx) + ']');
+                }
 
-                reachedPartitionEnd = false;
+                doneParts.add(donePart);
 
-                if (doneParts.size() == partMap.size())
-                    doneAllPartitions = true;
+                donePart = -1;
             }
 
             advance();
@@ -1259,10 +1318,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         private void advance() {
             next = null;
 
-            if (doneAllPartitions)
-                return;
-
-            while (true) {
+            outer: while (doneParts.size() != partMap.size()) {
                 if (entryIt != null) {
                     while (entryIt.hasNext()) {
                         DataEntry entry = entryIt.next();
@@ -1277,8 +1333,10 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                             long to = partMap.updateCounterAt(idx);
 
                             if (entry.partitionCounter() > from && entry.partitionCounter() <= to) {
-                                if (entry.partitionCounter() == to)
-                                    reachedPartitionEnd = true;
+                                // Partition will be marked as done for current entry on next iteration.
+                                if (++rebalancedCntrs[idx] == to ||
+                                    entry.partitionCounter() == to && grp.hasAtomicCaches())
+                                    donePart = entry.partitionId();
 
                                 next = entry;
 
@@ -1290,6 +1348,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                 entryIt = null;
 
+                // Search for next DataEntry while applying rollback counters.
                 while (walIt.hasNext()) {
                     IgniteBiTuple<WALPointer, WALRecord> rec = walIt.next();
 
@@ -1297,14 +1356,38 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         DataRecord data = (DataRecord)rec.get2();
 
                         entryIt = data.writeEntries().iterator();
+
                         // Move on to the next valid data entry.
+                        continue outer;
+                    }
+                    else if (rec.get2() instanceof RollbackRecord) {
+                        RollbackRecord rbRec = (RollbackRecord)rec.get2();
 
-                        break;
+                        if (grp.groupId() == rbRec.groupId()) {
+                            int idx = partMap.partitionIndex(rbRec.partitionId());
+
+                            if (idx < 0 || missingParts.contains(idx))
+                                continue;
+
+                            long from = partMap.initialUpdateCounterAt(idx);
+                            long to = partMap.updateCounterAt(idx);
+
+                            rebalancedCntrs[idx] += rbRec.overlap(from, to);
+
+                            if (rebalancedCntrs[idx] == partMap.updateCounterAt(idx)) {
+                                if (log.isDebugEnabled()) {
+                                    log.debug("Partition done [partId=" + donePart +
+                                        " from=" + from + " to=" + to + ']');
+                                }
+
+                                doneParts.add(rbRec.partitionId()); // Add to done set immediately.
+                            }
+                        }
                     }
                 }
 
-                if (entryIt == null)
-                    return;
+                assert entryIt != null || doneParts.size() == partMap.size() :
+                    "Reached end of WAL but not all partitions are done";
             }
         }
     }
@@ -1440,14 +1523,19 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         @GridToStringInclude
         private final RootPage pendingTreeRoot;
 
+        /** */
+        @GridToStringInclude
+        private final RootPage partMetastoreReuseListRoot;
+
         /**
          * @param treeRoot Metadata storage root.
          * @param reuseListRoot Reuse list root.
          */
-        Metas(RootPage treeRoot, RootPage reuseListRoot, RootPage pendingTreeRoot) {
+        Metas(RootPage treeRoot, RootPage reuseListRoot, RootPage pendingTreeRoot, RootPage partMetastoreReuseListRoot) {
             this.treeRoot = treeRoot;
             this.reuseListRoot = reuseListRoot;
             this.pendingTreeRoot = pendingTreeRoot;
+            this.partMetastoreReuseListRoot = partMetastoreReuseListRoot;
         }
 
         /** {@inheritDoc} */
@@ -1467,13 +1555,13 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         private String name;
 
         /** */
-        private volatile CacheFreeListImpl freeList;
+        private volatile AbstractFreeList<CacheDataRow> freeList;
 
         /** */
         private PendingEntriesTree pendingTree;
 
         /** */
-        private volatile CacheDataStore delegate;
+        private volatile CacheDataStoreImpl delegate;
 
         /**
          * Cache id which should be throttled.
@@ -1488,6 +1576,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         private volatile long nextStoreCleanTime;
 
         /** */
+        private PartitionMetaStorage<SimpleDataRow> partStorage;
+
+        /** */
         private final boolean exists;
 
         /** */
@@ -1512,7 +1603,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
          * @throws IgniteCheckedException If failed.
          */
         private CacheDataStore init0(boolean checkExists) throws IgniteCheckedException {
-            CacheDataStore delegate0 = delegate;
+            CacheDataStoreImpl delegate0 = delegate;
 
             if (delegate0 != null)
                 return delegate0;
@@ -1532,7 +1623,9 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                     if (PageIdUtils.partId(metas.reuseListRoot.pageId().pageId()) != partId ||
                         PageIdUtils.partId(metas.treeRoot.pageId().pageId()) != partId ||
-                        PageIdUtils.partId(metas.pendingTreeRoot.pageId().pageId()) != partId) {
+                        PageIdUtils.partId(metas.pendingTreeRoot.pageId().pageId()) != partId ||
+                        PageIdUtils.partId(metas.partMetastoreReuseListRoot.pageId().pageId()) != partId
+                        ) {
                         throw new IgniteCheckedException("Invalid meta root allocated [" +
                             "cacheOrGroupName=" + grp.cacheOrGroupName() +
                             ", partId=" + partId +
@@ -1558,6 +1651,25 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         }
                     };
 
+                    RootPage partMetastoreReuseListRoot = metas.partMetastoreReuseListRoot;
+
+                    partStorage = new PartitionMetaStorageImpl<SimpleDataRow>(
+                        grp.groupId(),
+                        grp.cacheOrGroupName() + "-partstore-" + partId,
+                        grp.dataRegion().memoryMetrics(),
+                        grp.dataRegion(),
+                        freeList,
+                        ctx.wal(),
+                        partMetastoreReuseListRoot.pageId().pageId(),
+                        partMetastoreReuseListRoot.isAllocated()) {
+                        /** {@inheritDoc} */
+                        @Override protected long allocatePageNoReuse() throws IgniteCheckedException {
+                            assert grp.shared().database().checkpointLockIsHeldByThread();
+
+                            return pageMem.allocatePage(grpId, partId, PageIdAllocator.FLAG_DATA);
+                        }
+                    };
+
                     CacheDataRowStore rowStore = new CacheDataRowStore(grp, freeList, partId);
 
                     RootPage treeRoot = metas.treeRoot;
@@ -1642,14 +1754,18 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
 
                         try {
                             if (PageIO.getType(pageAddr) != 0) {
-                                PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.latest();
+                                PagePartitionMetaIOV2 io = (PagePartitionMetaIOV2)PagePartitionMetaIO.VERSIONS.latest();
 
                                 Map<Integer, Long> cacheSizes = null;
 
                                 if (grp.sharedGroup())
                                     cacheSizes = readSharedGroupCacheSizes(pageMem, grpId, io.getCountersPageId(pageAddr));
 
-                                delegate0.init(io.getSize(pageAddr), io.getUpdateCounter(pageAddr), cacheSizes);
+                                long link = io.getGapsLink(pageAddr);
+
+                                byte[] data = link == 0 ? null : partStorage.readRow(link);
+
+                                delegate0.restoreState(io.getSize(pageAddr), io.getUpdateCounter(pageAddr), cacheSizes, data);
 
                                 globalRemoveId().setIfGreater(io.getGlobalRemoveId(pageAddr));
                             }
@@ -1704,28 +1820,32 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
             try {
                 boolean allocated = false;
                 boolean pendingTreeAllocated = false;
+                boolean partMetastoreReuseListAllocated = false;
 
                 long pageAddr = pageMem.writeLock(grpId, partMetaId, partMetaPage);
                 try {
-                    long treeRoot, reuseListRoot, pendingTreeRoot;
+                    long treeRoot, reuseListRoot, pendingTreeRoot, partMetaStoreReuseListRoot;
 
                     // Initialize new page.
                     if (PageIO.getType(pageAddr) != PageIO.T_PART_META) {
-                        PagePartitionMetaIO io = PagePartitionMetaIO.VERSIONS.latest();
+                        PagePartitionMetaIOV2 io = (PagePartitionMetaIOV2)PagePartitionMetaIO.VERSIONS.latest();
 
                         io.initNewPage(pageAddr, partMetaId, pageMem.realPageSize(grpId));
 
                         treeRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
                         reuseListRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
                         pendingTreeRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
+                        partMetaStoreReuseListRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
 
                         assert PageIdUtils.flag(treeRoot) == PageMemory.FLAG_DATA;
                         assert PageIdUtils.flag(reuseListRoot) == PageMemory.FLAG_DATA;
                         assert PageIdUtils.flag(pendingTreeRoot) == PageMemory.FLAG_DATA;
+                        assert PageIdUtils.flag(partMetaStoreReuseListRoot) == PageMemory.FLAG_DATA;
 
                         io.setTreeRoot(pageAddr, treeRoot);
                         io.setReuseListRoot(pageAddr, reuseListRoot);
                         io.setPendingTreeRoot(pageAddr, pendingTreeRoot);
+                        io.setPartitionMetaStoreReuseListRoot(pageAddr, partMetaStoreReuseListRoot);
 
                         if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, partMetaId, partMetaPage, wal, null)) {
                             wal.log(new PageSnapshot(new FullPageId(partMetaId, grpId), pageAddr,
@@ -1740,14 +1860,14 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         treeRoot = io.getTreeRoot(pageAddr);
                         reuseListRoot = io.getReuseListRoot(pageAddr);
 
-                        int pageVersion = PagePartitionMetaIO.getVersion(pageAddr);
+                        int pageVer = PagePartitionMetaIO.getVersion(pageAddr);
 
-                        if (pageVersion < 2) {
-                            assert pageVersion == 1;
+                        if (pageVer < 2) {
+                            assert pageVer == 1;
 
                             if (log.isDebugEnabled())
                                 log.info("Upgrade partition meta page version: [part=" + partId +
-                                    ", grpId=" + grpId + ", oldVer=" + pageVersion +
+                                    ", grpId=" + grpId + ", oldVer=" + pageVer +
                                     ", newVer=" + io.getVersion()
                                 );
 
@@ -1756,8 +1876,10 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                             ((PagePartitionMetaIOV2)io).upgradePage(pageAddr);
 
                             pendingTreeRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
+                            partMetaStoreReuseListRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
 
                             io.setPendingTreeRoot(pageAddr, pendingTreeRoot);
+                            io.setPartitionMetaStoreReuseListRoot(pageAddr, partMetaStoreReuseListRoot);
 
                             if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, partMetaId, partMetaPage, wal,
                                 null)) {
@@ -1765,10 +1887,24 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                                     pageMem.pageSize(), pageMem.realPageSize(grpId)));
                             }
 
-                            pendingTreeAllocated = true;
+                            pendingTreeAllocated = partMetastoreReuseListAllocated = true;
                         }
-                        else
+                        else {
                             pendingTreeRoot = io.getPendingTreeRoot(pageAddr);
+                            partMetaStoreReuseListRoot = io.getPartitionMetaStoreReuseListRoot(pageAddr);
+
+                            if (partMetaStoreReuseListRoot == 0) {
+                                partMetaStoreReuseListRoot = pageMem.allocatePage(grpId, partId, PageMemory.FLAG_DATA);
+
+                                if (PageHandler.isWalDeltaRecordNeeded(pageMem, grpId, partMetaId, partMetaPage, wal,
+                                    null)) {
+                                    wal.log(new PageSnapshot(new FullPageId(partMetaId, grpId), pageAddr,
+                                        pageMem.pageSize(), pageMem.realPageSize(grpId)));
+                                }
+
+                                partMetastoreReuseListAllocated = true;
+                            }
+                        }
 
                         if (PageIdUtils.flag(treeRoot) != PageMemory.FLAG_DATA)
                             throw new StorageException("Wrong tree root page id flag: treeRoot="
@@ -1781,15 +1917,21 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
                         if (PageIdUtils.flag(pendingTreeRoot) != PageMemory.FLAG_DATA)
                             throw new StorageException("Wrong pending tree root page id flag: reuseListRoot="
                                 + U.hexLong(reuseListRoot) + ", part=" + partId + ", grpId=" + grpId);
+
+                        if (PageIdUtils.flag(partMetaStoreReuseListRoot) != PageMemory.FLAG_DATA)
+                            throw new StorageException("Wrong partition meta store list root page id flag: partMetaStoreReuseListRoot="
+                                + U.hexLong(partMetaStoreReuseListRoot) + ", part=" + partId + ", grpId=" + grpId);
                     }
 
                     return new Metas(
                         new RootPage(new FullPageId(treeRoot, grpId), allocated),
                         new RootPage(new FullPageId(reuseListRoot, grpId), allocated),
-                        new RootPage(new FullPageId(pendingTreeRoot, grpId), allocated || pendingTreeAllocated));
+                        new RootPage(new FullPageId(pendingTreeRoot, grpId), allocated || pendingTreeAllocated),
+                        new RootPage(new FullPageId(partMetaStoreReuseListRoot, grpId), allocated || partMetastoreReuseListAllocated));
                 }
                 finally {
-                    pageMem.writeUnlock(grpId, partMetaId, partMetaPage, null, allocated || pendingTreeAllocated);
+                    pageMem.writeUnlock(grpId, partMetaId, partMetaPage, null,
+                        allocated || pendingTreeAllocated || partMetastoreReuseListAllocated);
                 }
             }
             finally {
@@ -1798,6 +1940,16 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
+        @Override public boolean init() {
+            try {
+                return init0(true) != null;
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        /** {@inheritDoc} */
         @Override public int partId() {
             return partId;
         }
@@ -1888,10 +2040,34 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public long getAndIncrementUpdateCounter(long delta) {
+        @Override public long reservedCounter() {
+            try {
+                CacheDataStore delegate0 = init0(true);
+
+                return delegate0 == null ? 0 : delegate0.reservedCounter();
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public PartitionUpdateCounter partUpdateCounter() {
             try {
                 CacheDataStore delegate0 = init0(true);
 
+                return delegate0 == null ? null : delegate0.partUpdateCounter();
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override public long getAndIncrementUpdateCounter(long delta) {
+            try {
+                CacheDataStore delegate0 = init0(false);
+
                 return delegate0 == null ? 0 : delegate0.getAndIncrementUpdateCounter(delta);
             }
             catch (IgniteCheckedException e) {
@@ -1900,8 +2076,18 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public void init(long size, long updCntr, @Nullable Map<Integer, Long> cacheSizes) {
-            throw new IllegalStateException("Should be never called.");
+        @Override public long reserve(long delta) {
+            try {
+                CacheDataStore delegate0 = init0(false);
+
+                if (delegate0 == null)
+                    throw new IllegalStateException("Should be never called.");
+
+                return delegate0.reserve(delta);
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
         }
 
         /** {@inheritDoc} */
@@ -1918,12 +2104,11 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public void updateCounter(long start, long delta) {
+        @Override public boolean updateCounter(long start, long delta) {
             try {
                 CacheDataStore delegate0 = init0(false);
 
-                if (delegate0 != null)
-                    delegate0.updateCounter(start, delta);
+                return delegate0 != null && delegate0.updateCounter(start, delta);
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -1947,7 +2132,10 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
             try {
                 CacheDataStore delegate0 = init0(false);
 
-                return delegate0 == null ? 0 : delegate0.nextUpdateCounter();
+                if (delegate0 == null)
+                    throw new IllegalStateException("Should be never called.");
+
+                return delegate0.nextUpdateCounter();
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -1967,12 +2155,14 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
         }
 
         /** {@inheritDoc} */
-        @Override public void updateInitialCounter(long cntr) {
+        @Override public void updateInitialCounter(long start, long delta) {
             try {
                 CacheDataStore delegate0 = init0(true);
 
-                if (delegate0 != null)
-                    delegate0.updateInitialCounter(cntr);
+                if (delegate0 == null)
+                    throw new IllegalStateException("Should be never called.");
+
+                delegate0.updateInitialCounter(start, delta);
             }
             catch (IgniteCheckedException e) {
                 throw new IgniteException(e);
@@ -2495,6 +2685,25 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
             if (delegate0 != null)
                 delegate0.preload();
         }
+
+        /** {@inheritDoc} */
+        @Override public void resetUpdateCounter() {
+            try {
+                CacheDataStore delegate0 = init0(true);
+
+                if (delegate0 == null)
+                    return;
+
+                delegate0.resetUpdateCounter();
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
+
+        @Override public PartitionMetaStorage partStorage() {
+            return partStorage;
+        }
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
index 91fd207..9a3d9d2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.processors.cache.persistence;
 
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.CacheObjectContext;
@@ -26,6 +27,7 @@ import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList
 import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
 import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
 import org.apache.ignite.internal.stat.IoStatisticsHolder;
+import org.apache.ignite.internal.util.typedef.internal.U;
 
 /**
  * Data store for H2 rows.
@@ -109,6 +111,10 @@ public class RowStore {
                 ctx.database().checkpointReadUnlock();
             }
         }
+
+        assert row.key().partition() == PageIdUtils.partId(row.link()) :
+            "Constructed a link with invalid partition ID [partId=" + row.key().partition() +
+                ", link=" + U.hexLong(row.link()) + ']';
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
index 133f0a1..d8fe3a4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
@@ -18,6 +18,8 @@
 package org.apache.ignite.internal.processors.cache.persistence;
 
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
 
 /**
  * Simple interface for data, store in some RowStore.
@@ -49,4 +51,9 @@ public interface Storable {
      * which is entirely available on the very first page followed by the row link.
      */
     public int headerSize();
+
+    /**
+     * @return I/O for handling this storable.
+     */
+    public IOVersions<? extends AbstractDataPageIO> ioVersions();
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointEntry.java
index c26e0a3..e7bcb05 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/checkpoint/CheckpointEntry.java
@@ -20,7 +20,6 @@ package org.apache.ignite.internal.processors.cache.persistence.checkpoint;
 import java.lang.ref.SoftReference;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
@@ -275,7 +274,7 @@ public class CheckpointEntry {
             if (stateRec == null)
                 return Collections.emptyMap();
 
-            Map<Integer, GroupState> grpStates = new HashMap<>(stateRec.size());
+            Map<Integer, GroupState> grpStates = U.newHashMap(stateRec.size());
 
             for (Integer grpId : stateRec.keySet()) {
                 CacheState recState = stateRec.get(grpId);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
index 958fb31..5742b74 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java
@@ -34,7 +34,6 @@ import org.apache.ignite.internal.processors.cache.persistence.Storable;
 import org.apache.ignite.internal.processors.cache.persistence.evict.PageEvictionTracker;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPagePayload;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.LongListReuseBag;
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseBag;
@@ -484,23 +483,32 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
 
                 long pageId = 0L;
 
-                for (int b = remaining < MIN_SIZE_FOR_DATA_PAGE ? bucket(remaining, false) + 1 : REUSE_BUCKET; b < BUCKETS; b++) {
-                    pageId = takeEmptyPage(b, ioVersions(), statHolder);
+                if (remaining < MIN_SIZE_FOR_DATA_PAGE) {
+                    for (int b = bucket(remaining, false) + 1; b < BUCKETS - 1; b++) {
+                        pageId = takeEmptyPage(b, row.ioVersions(), statHolder);
 
-                    if (pageId != 0L)
-                        break;
+                        if (pageId != 0L)
+                            break;
+                    }
+                }
+
+                if (pageId == 0L) { // Handle reuse bucket.
+                    if (reuseList == this)
+                        pageId = takeEmptyPage(REUSE_BUCKET, row.ioVersions(), statHolder);
+                    else
+                        pageId = reuseList.takeRecycledPage();
                 }
 
-                AbstractDataPageIO<T> initIo = null;
+                AbstractDataPageIO initIo = null;
 
                 if (pageId == 0L) {
                     pageId = allocateDataPage(row.partition());
 
-                    initIo = ioVersions().latest();
+                    initIo = row.ioVersions().latest();
                 }
-                else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA)
-                    pageId = initReusedPage(pageId, row.partition(), statHolder);
-                else
+                else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) // Page is taken from reuse bucket.
+                    pageId = initReusedPage(row, pageId, row.partition(), statHolder);
+                else // Page is taken from free space bucket. For in-memory mode partition must be changed.
                     pageId = PageIdUtils.changePartitionId(pageId, (row.partition()));
 
                 written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder);
@@ -525,7 +533,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
      *
      * @see PagesList#initReusedPage(long, long, long, int, byte, PageIO)
      */
-    private long initReusedPage(long reusedPageId, int partId,
+    private long initReusedPage(T row, long reusedPageId, int partId,
         IoStatisticsHolder statHolder) throws IgniteCheckedException {
         long reusedPage = acquirePage(reusedPageId, statHolder);
         try {
@@ -535,7 +543,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
 
             try {
                 return initReusedPage(reusedPageId, reusedPage, reusedPageAddr,
-                    partId, PageIdAllocator.FLAG_DATA, ioVersions().latest());
+                    partId, PageIdAllocator.FLAG_DATA, row.ioVersions().latest());
             }
             finally {
                 writeUnlock(reusedPageId, reusedPage, reusedPageAddr, true);
@@ -694,11 +702,6 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
         }
     }
 
-    /**
-     * @return IOVersions.
-     */
-    public abstract IOVersions<? extends AbstractDataPageIO<T>> ioVersions();
-
     /** {@inheritDoc} */
     @Override public String toString() {
         return "FreeList [name=" + name + ']';
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeListImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeListImpl.java
index 625c0b1..c1a58de 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeListImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/CacheFreeListImpl.java
@@ -23,9 +23,6 @@ import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
 import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPageIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
 import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
 import org.apache.ignite.internal.stat.IoStatisticsHolder;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -51,17 +48,12 @@ public class CacheFreeListImpl extends AbstractFreeList<CacheDataRow> {
     }
 
     /** {@inheritDoc} */
-    @Override public IOVersions<? extends AbstractDataPageIO<CacheDataRow>> ioVersions() {
-        return DataPageIO.VERSIONS;
-    }
-
-    /** {@inheritDoc} */
     @Override public void insertDataRow(CacheDataRow row, IoStatisticsHolder statHolder) throws IgniteCheckedException {
         super.insertDataRow(row, statHolder);
 
         assert row.key().partition() == PageIdUtils.partId(row.link()) :
             "Constructed a link with invalid partition ID [partId=" + row.key().partition() +
-            ", link=" + U.hexLong(row.link()) + ']';
+                ", link=" + U.hexLong(row.link()) + ']';
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java
index e5cb3a6..1855578 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/PagesList.java
@@ -660,7 +660,7 @@ public abstract class PagesList extends DataStructure {
      * @throws IgniteCheckedException If failed.
      */
     protected final void put(
-        ReuseBag bag,
+        @Nullable ReuseBag bag,
         final long dataId,
         final long dataPage,
         final long dataAddr,
@@ -669,10 +669,13 @@ public abstract class PagesList extends DataStructure {
         throws IgniteCheckedException {
         assert bag == null ^ dataAddr == 0L;
 
+        if (bag != null && bag.isEmpty()) // Skip allocating stripe for empty bag.
+            return;
+
         for (int lockAttempt = 0; ;) {
             Stripe stripe = getPageForPut(bucket, bag);
 
-            // No need to continue if bag has been utilized at getPageForPut.
+            // No need to continue if bag has been utilized at getPageForPut (free page can be used for pagelist).
             if (bag != null && bag.isEmpty())
                 return;
 
@@ -936,7 +939,6 @@ public abstract class PagesList extends DataStructure {
                 int idx = io.addPage(prevAddr, nextId, pageSize());
 
                 if (idx == -1) { // Attempt to add page failed: the node page is full.
-
                     final long nextPage = acquirePage(nextId, statHolder);
 
                     try {
@@ -1095,7 +1097,7 @@ public abstract class PagesList extends DataStructure {
      * @return Removed page ID.
      * @throws IgniteCheckedException If failed.
      */
-    protected final long takeEmptyPage(int bucket, @Nullable IOVersions initIoVers,
+    protected long takeEmptyPage(int bucket, @Nullable IOVersions initIoVers,
         IoStatisticsHolder statHolder) throws IgniteCheckedException {
         for (int lockAttempt = 0; ;) {
             Stripe stripe = getPageForTake(bucket);
@@ -1278,6 +1280,8 @@ public abstract class PagesList extends DataStructure {
     }
 
     /**
+     * Removes data page from bucket, merges bucket list if needed.
+     *
      * @param dataId Data page ID.
      * @param dataPage Data page pointer.
      * @param dataAddr Data page address.
@@ -1642,7 +1646,7 @@ public abstract class PagesList extends DataStructure {
         public volatile long tailId;
 
         /** */
-        volatile boolean empty;
+        public volatile boolean empty;
 
         /**
          * @param tailId Tail ID.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/SimpleDataRow.java
similarity index 51%
copy from modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
copy to modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/SimpleDataRow.java
index 2d7b0a6..6061ff7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/SimpleDataRow.java
@@ -15,60 +15,68 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.cache.persistence.metastorage;
+package org.apache.ignite.internal.processors.cache.persistence.freelist;
 
 import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.pagemem.PageIdAllocator;
 import org.apache.ignite.internal.processors.cache.persistence.Storable;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.SimpleDataPageIO;
+import org.apache.ignite.internal.util.tostring.GridToStringExclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
 
 /**
- *
+ * Implementation of {@link Storable} which holds byte array of variable length.
  */
-public class MetastorageDataRow implements MetastorageSearchRow, Storable {
+public class SimpleDataRow implements Storable {
     /** */
     private long link;
 
     /** */
-    private String key;
+    private final int part;
 
     /** */
-    private byte[] value;
+    @GridToStringExclude
+    private final byte[] val;
 
-    /** */
-    public MetastorageDataRow(long link, String key, byte[] value) {
+    /**
+     * @param link Link.
+     * @param part Partition.
+     * @param val Value.
+     */
+    public SimpleDataRow(long link, int part, byte[] val) {
         this.link = link;
-        this.key = key;
-        this.value = value;
-    }
-
-    /** */
-    public MetastorageDataRow(String key, byte[] value) {
-        this.key = key;
-        this.value = value;
+        this.part = part;
+        this.val = val;
     }
 
     /**
-     * @return Key.
+     * @param part Partition.
+     * @param val Value.
      */
-    @Override public String key() {
-        return key;
+    public SimpleDataRow(int part, byte[] val) {
+        this.part = part;
+        this.val = val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void link(long link) {
+        this.link = link;
     }
 
     /** {@inheritDoc} */
-    @Override
-    public int hash() {
-        return key.hashCode();
+    @Override public long link() {
+        return link;
     }
 
     /** {@inheritDoc} */
-    @Override
-    public int partition() {
-        return MetaStorage.PRESERVE_LEGACY_METASTORAGE_PARTITION_ID ? PageIdAllocator.OLD_METASTORE_PARTITION: PageIdAllocator.METASTORE_PARTITION;
+    @Override public int partition() {
+        return part;
     }
 
     /** {@inheritDoc} */
     @Override public int size() throws IgniteCheckedException {
-        return 4 + value().length;
+        return 2 /** Fragment size */ + 2 /** Row size */ + value().length;
     }
 
     /** {@inheritDoc} */
@@ -77,27 +85,17 @@ public class MetastorageDataRow implements MetastorageSearchRow, Storable {
     }
 
     /** {@inheritDoc} */
-    @Override
-    public void link(long link) {
-        this.link = link;
+    public byte[] value() {
+        return val;
     }
 
     /** {@inheritDoc} */
-    @Override
-    public long link() {
-        return link;
-    }
-
-    /**
-     * @return Value.
-     */
-    public byte[] value() {
-        return value;
+    @Override public IOVersions<? extends AbstractDataPageIO> ioVersions() {
+        return SimpleDataPageIO.VERSIONS;
     }
 
     /** {@inheritDoc} */
-    @Override
-    public String toString() {
-        return "key=" + key;
+    @Override public String toString() {
+        return S.toString(SimpleDataRow.class, this, "len", val.length);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java
index cfab287..331da92 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetaStorage.java
@@ -50,7 +50,6 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageInitRecord;
 import org.apache.ignite.internal.processors.cache.CacheGroupContext;
 import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
-import org.apache.ignite.internal.processors.cache.IncompleteObject;
 import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
 import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl;
 import org.apache.ignite.internal.processors.cache.persistence.DbCheckpointListener;
@@ -58,15 +57,10 @@ import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabase
 import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
 import org.apache.ignite.internal.processors.cache.persistence.RootPage;
 import org.apache.ignite.internal.processors.cache.persistence.StorageException;
-import org.apache.ignite.internal.processors.cache.persistence.freelist.AbstractFreeList;
 import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryEx;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPagePayload;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.persistence.partstorage.PartitionMetaStorageImpl;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PagePartitionMetaIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.io.SimpleDataPageIO;
-import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
 import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
 import org.apache.ignite.internal.processors.failure.FailureProcessor;
 import org.apache.ignite.internal.util.lang.GridCursor;
@@ -79,8 +73,6 @@ import org.jetbrains.annotations.NotNull;
 
 import static org.apache.ignite.internal.pagemem.PageIdAllocator.FLAG_DATA;
 import static org.apache.ignite.internal.pagemem.PageIdAllocator.OLD_METASTORE_PARTITION;
-import static org.apache.ignite.internal.pagemem.PageIdUtils.itemId;
-import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
 
 /**
  * General purpose key-value local-only storage.
@@ -104,7 +96,6 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
     /** Temporary metastorage buffer size (file). */
     private static final int TEMPORARY_METASTORAGE_BUFFER_SIZE = 1024 * 1024;
 
-
     /** */
     private final IgniteWriteAheadLogManager wal;
 
@@ -136,7 +127,7 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
     private RootPage reuseListRoot;
 
     /** */
-    private FreeListImpl freeList;
+    private PartitionMetaStorageImpl<MetastorageDataRow> partStorage;
 
     /** */
     private SortedMap<String, byte[]> lastUpdates;
@@ -245,14 +236,18 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
             getOrAllocateMetas(partId = PageIdAllocator.METASTORE_PARTITION);
 
         if (!empty) {
-            freeList = new FreeListImpl(METASTORAGE_CACHE_ID, "metastorage",
+            partStorage = new PartitionMetaStorageImpl<MetastorageDataRow>(METASTORAGE_CACHE_ID, "metastorage",
                 regionMetrics, dataRegion, null, wal, reuseListRoot.pageId().pageId(),
-                reuseListRoot.isAllocated());
+                reuseListRoot.isAllocated()) {
+                @Override protected long allocatePageNoReuse() throws IgniteCheckedException {
+                    return pageMem.allocatePage(grpId, partId, FLAG_DATA);
+                }
+            };
 
-            MetastorageRowStore rowStore = new MetastorageRowStore(freeList, db);
+            MetastorageRowStore rowStore = new MetastorageRowStore(partStorage, db);
 
             tree = new MetastorageTree(METASTORAGE_CACHE_ID, dataRegion.pageMemory(), wal, rmvId,
-                freeList, rowStore, treeRoot.pageId().pageId(), treeRoot.isAllocated(), failureProcessor, partId);
+                partStorage, rowStore, treeRoot.pageId().pageId(), treeRoot.isAllocated(), failureProcessor, partId);
 
             if (!readOnly)
                 ((GridCacheDatabaseSharedManager)db).addCheckpointListener(this);
@@ -578,14 +573,14 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
         Executor executor = ctx.executor();
 
         if (executor == null) {
-            freeList.saveMetadata();
+            partStorage.saveMetadata();
 
             saveStoreMetadata();
         }
         else {
             executor.execute(() -> {
                 try {
-                    freeList.saveMetadata();
+                    partStorage.saveMetadata();
                 }
                 catch (IgniteCheckedException e) {
                     throw new IgniteException(e);
@@ -605,7 +600,7 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
 
     /** {@inheritDoc} */
     @Override public void beforeCheckpointBegin(Context ctx) throws IgniteCheckedException {
-        freeList.saveMetadata();
+        partStorage.saveMetadata();
     }
 
     /** {@inheritDoc} */
@@ -663,108 +658,6 @@ public class MetaStorage implements DbCheckpointListener, ReadWriteMetastorage {
         }
     }
 
-    /** */
-    public class FreeListImpl extends AbstractFreeList<MetastorageDataRow> {
-        /** {@inheritDoc} */
-        FreeListImpl(int cacheId, String name, DataRegionMetricsImpl regionMetrics, DataRegion dataRegion,
-            ReuseList reuseList,
-            IgniteWriteAheadLogManager wal, long metaPageId, boolean initNew) throws IgniteCheckedException {
-            super(cacheId, name, regionMetrics, dataRegion, reuseList, wal, metaPageId, initNew);
-        }
-
-        /** {@inheritDoc} */
-        @Override public IOVersions<? extends AbstractDataPageIO<MetastorageDataRow>> ioVersions() {
-            return SimpleDataPageIO.VERSIONS;
-        }
-
-        /** {@inheritDoc} */
-        @Override protected long allocatePageNoReuse() throws IgniteCheckedException {
-            return pageMem.allocatePage(grpId, partId, FLAG_DATA);
-        }
-
-        /**
-         * Read row from data pages.
-         */
-        final MetastorageDataRow readRow(String key, long link)
-            throws IgniteCheckedException {
-            assert link != 0 : "link";
-
-            long nextLink = link;
-            IncompleteObject incomplete = null;
-            int size = 0;
-
-            boolean first = true;
-
-            do {
-                final long pageId = pageId(nextLink);
-
-                final long page = pageMem.acquirePage(grpId, pageId);
-
-                try {
-                    long pageAddr = pageMem.readLock(grpId, pageId, page); // Non-empty data page must not be recycled.
-
-                    assert pageAddr != 0L : nextLink;
-
-                    try {
-                        SimpleDataPageIO io = (SimpleDataPageIO)ioVersions().forPage(pageAddr);
-
-                        //MetaStorage never encrypted so realPageSize == pageSize.
-                        DataPagePayload data = io.readPayload(pageAddr, itemId(nextLink), pageMem.pageSize());
-
-                        nextLink = data.nextLink();
-
-                        if (first) {
-                            if (nextLink == 0) {
-                                // Fast path for a single page row.
-                                return new MetastorageDataRow(link, key, SimpleDataPageIO.readPayload(pageAddr + data.offset()));
-                            }
-
-                            first = false;
-                        }
-
-                        ByteBuffer buf = pageMem.pageBuffer(pageAddr);
-
-                        buf.position(data.offset());
-                        buf.limit(data.offset() + data.payloadSize());
-
-                        if (size == 0) {
-                            if (buf.remaining() >= 4 && incomplete == null) {
-                                // Just read size.
-                                size = buf.getInt();
-                                incomplete = new IncompleteObject(new byte[size]);
-                            }
-                            else {
-                                if (incomplete == null)
-                                    incomplete = new IncompleteObject(new byte[4]);
-
-                                incomplete.readData(buf);
-
-                                if (incomplete.isReady()) {
-                                    size = ByteBuffer.wrap(incomplete.data()).order(buf.order()).getInt();
-                                    incomplete = new IncompleteObject(new byte[size]);
-                                }
-                            }
-                        }
-
-                        if (size != 0 && buf.remaining() > 0)
-                            incomplete.readData(buf);
-                    }
-                    finally {
-                        pageMem.readUnlock(grpId, pageId, page);
-                    }
-                }
-                finally {
-                    pageMem.releasePage(grpId, pageId, page);
-                }
-            }
-            while (nextLink != 0);
-
-            assert incomplete.isReady();
-
-            return new MetastorageDataRow(link, key, incomplete.data());
-        }
-    }
-
     /**
      * Temporary storage internal
      */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
index 2d7b0a6..a84924a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageDataRow.java
@@ -17,34 +17,28 @@
 
 package org.apache.ignite.internal.processors.cache.persistence.metastorage;
 
-import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.pagemem.PageIdAllocator;
-import org.apache.ignite.internal.processors.cache.persistence.Storable;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.SimpleDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
 
 /**
  *
  */
-public class MetastorageDataRow implements MetastorageSearchRow, Storable {
-    /** */
-    private long link;
-
+public class MetastorageDataRow extends SimpleDataRow implements MetastorageSearchRow {
     /** */
     private String key;
 
     /** */
-    private byte[] value;
-
-    /** */
-    public MetastorageDataRow(long link, String key, byte[] value) {
-        this.link = link;
+    public MetastorageDataRow(long link, String key, byte[] val) {
+        super(link, MetaStorage.PRESERVE_LEGACY_METASTORAGE_PARTITION_ID ?
+            PageIdAllocator.OLD_METASTORE_PARTITION: PageIdAllocator.METASTORE_PARTITION, val);
         this.key = key;
-        this.value = value;
     }
 
     /** */
-    public MetastorageDataRow(String key, byte[] value) {
-        this.key = key;
-        this.value = value;
+    public MetastorageDataRow(String key, byte[] val) {
+        this(0, key, val);
     }
 
     /**
@@ -55,49 +49,17 @@ public class MetastorageDataRow implements MetastorageSearchRow, Storable {
     }
 
     /** {@inheritDoc} */
-    @Override
-    public int hash() {
+    @Override public int hash() {
         return key.hashCode();
     }
 
     /** {@inheritDoc} */
-    @Override
-    public int partition() {
-        return MetaStorage.PRESERVE_LEGACY_METASTORAGE_PARTITION_ID ? PageIdAllocator.OLD_METASTORE_PARTITION: PageIdAllocator.METASTORE_PARTITION;
-    }
-
-    /** {@inheritDoc} */
-    @Override public int size() throws IgniteCheckedException {
-        return 4 + value().length;
-    }
-
-    /** {@inheritDoc} */
-    @Override public int headerSize() {
-        return 0;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void link(long link) {
-        this.link = link;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public long link() {
-        return link;
-    }
-
-    /**
-     * @return Value.
-     */
-    public byte[] value() {
-        return value;
+    @Override public IOVersions<? extends AbstractDataPageIO> ioVersions() {
+        return MetastoreDataPageIO.VERSIONS;
     }
 
     /** {@inheritDoc} */
-    @Override
-    public String toString() {
+    @Override public String toString() {
         return "key=" + key;
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageRowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageRowStore.java
index c482a94..ccc55dc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageRowStore.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastorageRowStore.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.processors.cache.persistence.metastorage;
 
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.processors.cache.persistence.IgniteCacheDatabaseSharedManager;
-import org.apache.ignite.internal.processors.cache.persistence.freelist.FreeList;
+import org.apache.ignite.internal.processors.cache.persistence.partstorage.PartitionMetaStorage;
 import org.apache.ignite.internal.stat.IoStatisticsHolderNoOp;
 
 /**
@@ -27,14 +27,14 @@ import org.apache.ignite.internal.stat.IoStatisticsHolderNoOp;
  */
 public class MetastorageRowStore {
     /** */
-    private final FreeList freeList;
+    private final PartitionMetaStorage<MetastorageDataRow> partStorage;
 
     /** */
     protected final IgniteCacheDatabaseSharedManager db;
 
     /** */
-    public MetastorageRowStore(FreeList freeList, IgniteCacheDatabaseSharedManager db) {
-        this.freeList = freeList;
+    public MetastorageRowStore(PartitionMetaStorage<MetastorageDataRow> partStorage, IgniteCacheDatabaseSharedManager db) {
+        this.partStorage = partStorage;
         this.db = db;
     }
 
@@ -43,7 +43,7 @@ public class MetastorageRowStore {
      * @return Data row.
      */
     public MetastorageDataRow dataRow(String key, long link) throws IgniteCheckedException {
-        return ((MetaStorage.FreeListImpl)freeList).readRow(key, link);
+        return new MetastorageDataRow(link, key, partStorage.readRow(link));
     }
 
     /**
@@ -55,7 +55,7 @@ public class MetastorageRowStore {
         db.checkpointReadLock();
 
         try {
-            freeList.removeDataRowByLink(link, IoStatisticsHolderNoOp.INSTANCE);
+            partStorage.removeDataRowByLink(link, IoStatisticsHolderNoOp.INSTANCE);
         }
         finally {
             db.checkpointReadUnlock();
@@ -70,28 +70,10 @@ public class MetastorageRowStore {
         db.checkpointReadLock();
 
         try {
-            freeList.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE);
+            partStorage.insertDataRow(row, IoStatisticsHolderNoOp.INSTANCE);
         }
         finally {
             db.checkpointReadUnlock();
         }
     }
-
-    /**
-     * @param link Row link.
-     * @param row New row data.
-     * @return {@code True} if was able to update row.
-     * @throws IgniteCheckedException If failed.
-     */
-    public boolean updateRow(long link, MetastorageDataRow row) throws IgniteCheckedException {
-        return freeList.updateDataRow(link, row, IoStatisticsHolderNoOp.INSTANCE);
-    }
-
-    /**
-     * @return Free list.
-     */
-    public FreeList freeList() {
-        return freeList;
-    }
-
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastoreDataPageIO.java
similarity index 52%
copy from modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
copy to modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastoreDataPageIO.java
index 133f0a1..0ac0915 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/metastorage/MetastoreDataPageIO.java
@@ -15,38 +15,23 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.cache.persistence;
+package org.apache.ignite.internal.processors.cache.persistence.metastorage;
 
-import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.IOVersions;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.SimpleDataPageIO;
 
 /**
- * Simple interface for data, store in some RowStore.
  */
-public interface Storable {
-    /**
-     * @param link Link for this row.
-     */
-    public void link(long link);
-
-    /**
-     * @return Link for this row.
-     */
-    public long link();
-
-    /**
-     * @return Partition.
-     */
-    public int partition();
-
-    /**
-     * @return Row size in page.
-     * @throws IgniteCheckedException If failed.
-     */
-    public int size() throws IgniteCheckedException;
+public class MetastoreDataPageIO extends SimpleDataPageIO {
+    /** */
+    public static final IOVersions<MetastoreDataPageIO> VERSIONS = new IOVersions<>(
+        new MetastoreDataPageIO(1)
+    );
 
     /**
-     * @return Row header size in page. Header is indivisible part of row
-     * which is entirely available on the very first page followed by the row link.
+     * @param ver Version.
      */
-    public int headerSize();
+    public MetastoreDataPageIO(int ver) {
+        super(T_DATA_METASTORAGE, ver);
+    }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorage.java
similarity index 51%
copy from modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
copy to modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorage.java
index 133f0a1..0dbd5f4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/Storable.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorage.java
@@ -15,38 +15,37 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.cache.persistence;
+package org.apache.ignite.internal.processors.cache.persistence.partstorage;
 
 import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.persistence.Storable;
+import org.apache.ignite.internal.stat.IoStatisticsHolder;
 
 /**
- * Simple interface for data, store in some RowStore.
+ * Provides a way to associate any {@link Storable} implementation as partition metadata.
  */
-public interface Storable {
+public interface PartitionMetaStorage<T extends Storable> {
     /**
-     * @param link Link for this row.
-     */
-    public void link(long link);
-
-    /**
-     * @return Link for this row.
+     * Read row data by link as byte array.
+     * @param link Link.
+     * @throws IgniteCheckedException If failed.
      */
-    public long link();
+    public byte[] readRow(long link) throws IgniteCheckedException;
 
     /**
-     * @return Partition.
+     * @param row Row.
+     * @param statHolder Stat holder.
      */
-    public int partition();
+    public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException;
 
     /**
-     * @return Row size in page.
+     * @param link Row link.
      * @throws IgniteCheckedException If failed.
      */
-    public int size() throws IgniteCheckedException;
+    public void removeDataRowByLink(long link, IoStatisticsHolder statHolder) throws IgniteCheckedException;
 
     /**
-     * @return Row header size in page. Header is indivisible part of row
-     * which is entirely available on the very first page followed by the row link.
+     * Saves storage metadata.
      */
-    public int headerSize();
+    public void saveMetadata() throws IgniteCheckedException;
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.java
new file mode 100644
index 0000000..f078f2c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/partstorage/PartitionMetaStorageImpl.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.persistence.partstorage;
+
+import java.nio.ByteBuffer;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.IncompleteObject;
+import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
+import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl;
+import org.apache.ignite.internal.processors.cache.persistence.Storable;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.AbstractFreeList;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.DataPagePayload;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
+import org.apache.ignite.internal.processors.cache.persistence.tree.reuse.ReuseList;
+
+import static org.apache.ignite.internal.pagemem.PageIdUtils.itemId;
+import static org.apache.ignite.internal.pagemem.PageIdUtils.pageId;
+
+/**
+ */
+public class PartitionMetaStorageImpl<T extends Storable> extends AbstractFreeList<T> implements PartitionMetaStorage<T> {
+    /**
+     * @param cacheId Cache id.
+     * @param name Name.
+     * @param memMetrics Mem metrics.
+     * @param memPlc Mem policy.
+     * @param reuseList Reuse list.
+     * @param wal Wal.
+     * @param metaPageId Meta page id.
+     * @param initNew Initialize new.
+     */
+    public PartitionMetaStorageImpl(int cacheId, String name,
+        DataRegionMetricsImpl memMetrics,
+        DataRegion memPlc,
+        ReuseList reuseList,
+        IgniteWriteAheadLogManager wal, long metaPageId, boolean initNew) throws IgniteCheckedException {
+        super(cacheId, name, memMetrics, memPlc, reuseList, wal, metaPageId, initNew);
+    }
+
+    /**
+     * Read row as byte array from data pages.
+     */
+    @Override public final byte[] readRow(long link) throws IgniteCheckedException {
+        assert link != 0 : "link";
+
+        long nextLink = link;
+        IncompleteObject incomplete = null;
+        int size = 0;
+
+        boolean first = true;
+
+        do {
+            final long pageId = pageId(nextLink);
+
+            final long page = pageMem.acquirePage(grpId, pageId);
+
+            try {
+                long pageAddr = pageMem.readLock(grpId, pageId, page); // Non-empty data page must not be recycled.
+
+                assert pageAddr != 0L : nextLink;
+
+                try {
+                    AbstractDataPageIO io = PageIO.getPageIO(pageAddr);
+
+                    //MetaStorage never encrypted so realPageSize == pageSize.
+                    DataPagePayload data = io.readPayload(pageAddr, itemId(nextLink), pageMem.pageSize());
+
+                    nextLink = data.nextLink();
+
+                    if (first) {
+                        if (nextLink == 0) {
+                            long payloadAddr = pageAddr + data.offset();
+
+                            // Fast path for a single page row.
+                            return PageUtils.getBytes(payloadAddr, 4, PageUtils.getInt(payloadAddr, 0));
+                        }
+
+                        first = false;
+                    }
+
+                    ByteBuffer buf = pageMem.pageBuffer(pageAddr);
+
+                    buf.position(data.offset());
+                    buf.limit(data.offset() + data.payloadSize());
+
+                    if (size == 0) {
+                        if (buf.remaining() >= 4 && incomplete == null) {
+                            // Just read size.
+                            size = buf.getInt();
+                            incomplete = new IncompleteObject(new byte[size]);
+                        }
+                        else {
+                            if (incomplete == null)
+                                incomplete = new IncompleteObject(new byte[4]);
+
+                            incomplete.readData(buf);
+
+                            if (incomplete.isReady()) {
+                                size = ByteBuffer.wrap(incomplete.data()).order(buf.order()).getInt();
+                                incomplete = new IncompleteObject(new byte[size]);
+                            }
+                        }
+                    }
+
+                    if (size != 0 && buf.remaining() > 0)
+                        incomplete.readData(buf);
+                }
+                finally {
+                    pageMem.readUnlock(grpId, pageId, page);
+                }
+            }
+            finally {
+                pageMem.releasePage(grpId, pageId, page);
+            }
+        }
+        while (nextLink != 0);
+
+        assert incomplete.isReady();
+
+        return incomplete.data();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
index 54d9816..c131a1e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
@@ -3775,6 +3775,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
                     break;
 
                 case NOOP:
+                case IN_PLACE:
                     return;
 
                 default:
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
index 78752bb..a2e03e9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java
@@ -39,7 +39,6 @@ import static org.apache.ignite.internal.util.GridUnsafe.bufferAddress;
  * Data pages IO.
  */
 public abstract class AbstractDataPageIO<T extends Storable> extends PageIO implements CompactablePageIO {
-
     /** */
     private static final int SHOW_ITEM = 0b0001;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
index dcf1acd..fafea22 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PageIO.java
@@ -31,6 +31,7 @@ import org.apache.ignite.internal.processors.cache.persistence.IndexStorageImpl;
 import org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListMetaIO;
 import org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListNodeIO;
 import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetastorageTree;
+import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetastoreDataPageIO;
 import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler;
 import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageLockListener;
 import org.apache.ignite.internal.processors.cache.tree.CacheIdAwareDataInnerIO;
@@ -251,6 +252,9 @@ public abstract class PageIO {
     /** */
     public static final short T_TX_LOG_INNER = 31;
 
+    /** */
+    public static final short T_DATA_PART = 32;
+
     /** Index for payload == 1. */
     public static final short T_H2_EX_REF_LEAF_START = 10_000;
 
@@ -667,6 +671,9 @@ public abstract class PageIO {
                 return (Q)TrackingPageIO.VERSIONS.forVersion(ver);
 
             case T_DATA_METASTORAGE:
+                return (Q)MetastoreDataPageIO.VERSIONS.forVersion(ver);
+
+            case T_DATA_PART:
                 return (Q)SimpleDataPageIO.VERSIONS.forVersion(ver);
 
             default:
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIO.java
index 6a81057..c86a64a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIO.java
@@ -194,6 +194,40 @@ public class PagePartitionMetaIO extends PageMetaIO {
             "this PagePartitionMetaIO version: ver=" + getVersion());
     }
 
+    /**
+     * @param pageAddr Page address.
+     */
+    public long getPartitionMetaStoreReuseListRoot(long pageAddr) {
+        throw new UnsupportedOperationException("Partition metastore is not supported by " +
+            "this PagePartitionMetaIO version: ver=" + getVersion());
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param listRoot List root.
+     */
+    public void setPartitionMetaStoreReuseListRoot(long pageAddr, long listRoot) {
+        throw new UnsupportedOperationException("Partition metastore is not supported by " +
+            "this PagePartitionMetaIO version: ver=" + getVersion());
+    }
+
+    /**
+     * @param pageAddr Page address.
+     */
+    public long getGapsLink(long pageAddr) {
+        throw new UnsupportedOperationException("Gaps link is not supported by " +
+            "this PagePartitionMetaIO version: ver=" + getVersion());
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param link Link.
+     */
+    public boolean setGapsLink(long pageAddr, long link) {
+        throw new UnsupportedOperationException("Gaps link is not supported by " +
+            "this PagePartitionMetaIO version: ver=" + getVersion());
+    }
+
     /** {@inheritDoc} */
     @Override protected void printPage(long pageAddr, int pageSize, GridStringBuilder sb) throws IgniteCheckedException {
         super.printPage(pageAddr, pageSize, sb);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIOV2.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIOV2.java
index 220ff83..37b7243 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIOV2.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/PagePartitionMetaIOV2.java
@@ -31,6 +31,12 @@ public class PagePartitionMetaIOV2 extends PagePartitionMetaIO {
     /** */
     private static final int PENDING_TREE_ROOT_OFF = PagePartitionMetaIO.END_OF_PARTITION_PAGE_META;
 
+    /** */
+    private static final int PART_META_REUSE_LIST_ROOT_OFF = PENDING_TREE_ROOT_OFF + 8;
+
+    /** */
+    private static final int GAPS_LINK = PART_META_REUSE_LIST_ROOT_OFF + 8;
+
     /**
      * @param ver Version.
      */
@@ -43,6 +49,8 @@ public class PagePartitionMetaIOV2 extends PagePartitionMetaIO {
         super.initNewPage(pageAddr, pageId, pageSize);
 
         setPendingTreeRoot(pageAddr, 0L);
+        setPartitionMetaStoreReuseListRoot(pageAddr, 0L);
+        setGapsLink(pageAddr, 0);
     }
 
     /** {@inheritDoc} */
@@ -51,8 +59,44 @@ public class PagePartitionMetaIOV2 extends PagePartitionMetaIO {
     }
 
     /** {@inheritDoc} */
-    @Override public void setPendingTreeRoot(long pageAddr, long treeRoot) {
-        PageUtils.putLong(pageAddr, PENDING_TREE_ROOT_OFF, treeRoot);
+    @Override public void setPendingTreeRoot(long pageAddr, long listRoot) {
+        PageUtils.putLong(pageAddr, PENDING_TREE_ROOT_OFF, listRoot);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     */
+    @Override public long getPartitionMetaStoreReuseListRoot(long pageAddr) {
+        return PageUtils.getLong(pageAddr, PART_META_REUSE_LIST_ROOT_OFF);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param listRoot List root.
+     */
+    @Override public void setPartitionMetaStoreReuseListRoot(long pageAddr, long listRoot) {
+        PageUtils.putLong(pageAddr, PART_META_REUSE_LIST_ROOT_OFF, listRoot);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @return Partition size.
+     */
+    public long getGapsLink(long pageAddr) {
+        return PageUtils.getLong(pageAddr, GAPS_LINK);
+    }
+
+    /**
+     * @param pageAddr Page address.
+     * @param link Link.
+     */
+    public boolean setGapsLink(long pageAddr, long link) {
+        if (getGapsLink(pageAddr) == link)
+            return false;
+
+        PageUtils.putLong(pageAddr, GAPS_LINK, link);
+
+        return true;
     }
 
     /** {@inheritDoc} */
@@ -72,6 +116,7 @@ public class PagePartitionMetaIOV2 extends PagePartitionMetaIO {
         sb.a(",\n\tglobalRemoveId=").a(getGlobalRemoveId(pageAddr));
         sb.a(",\n\tpartitionState=").a(state).a("(").a(GridDhtPartitionState.fromOrdinal(state)).a(")");
         sb.a(",\n\tcountersPageId=").a(getCountersPageId(pageAddr));
+        sb.a(",\n\tcntrUpdDataPageId=").a(getGapsLink(pageAddr));
         sb.a("\n]");
     }
 
@@ -86,5 +131,7 @@ public class PagePartitionMetaIOV2 extends PagePartitionMetaIO {
 
         PageIO.setVersion(pageAddr, getVersion());
         setPendingTreeRoot(pageAddr, 0);
+        setPartitionMetaStoreReuseListRoot(pageAddr, 0);
+        setGapsLink(pageAddr, 0);
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
index 14489b7..716f8be 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/SimpleDataPageIO.java
@@ -20,13 +20,13 @@ package org.apache.ignite.internal.processors.cache.persistence.tree.io;
 import java.nio.ByteBuffer;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.internal.pagemem.PageUtils;
-import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetastorageDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.freelist.SimpleDataRow;
 import org.apache.ignite.internal.util.GridStringBuilder;
 
 /**
- * Data pages IO for Metastorage.
+ * Data pages IO for writing binary arrays.
  */
-public class SimpleDataPageIO extends AbstractDataPageIO<MetastorageDataRow> {
+public class SimpleDataPageIO extends AbstractDataPageIO<SimpleDataRow> {
     /** */
     public static final IOVersions<SimpleDataPageIO> VERSIONS = new IOVersions<>(
         new SimpleDataPageIO(1)
@@ -36,13 +36,21 @@ public class SimpleDataPageIO extends AbstractDataPageIO<MetastorageDataRow> {
      * @param ver Page format version.
      */
     public SimpleDataPageIO(int ver) {
-        super(T_DATA_METASTORAGE, ver);
+        super(T_DATA_PART, ver);
+    }
+
+    /**
+     * Constructor is intended for extending types.
+     * @param type IO type.
+     * @param ver Page format version.
+     */
+    public SimpleDataPageIO(int type, int ver) {
+        super(type, ver);
     }
 
     /** {@inheritDoc} */
-    @Override
-    protected void writeFragmentData(
-        final MetastorageDataRow row,
+    @Override protected void writeFragmentData(
+        final SimpleDataRow row,
         final ByteBuffer buf,
         final int rowOff,
         final int payloadSize
@@ -65,7 +73,7 @@ public class SimpleDataPageIO extends AbstractDataPageIO<MetastorageDataRow> {
     }
 
     /** */
-    private int writeSizeFragment(final MetastorageDataRow row, final ByteBuffer buf, final int rowOff,
+    private int writeSizeFragment(final SimpleDataRow row, final ByteBuffer buf, final int rowOff,
         final int payloadSize) {
         final int size = 4;
 
@@ -89,12 +97,11 @@ public class SimpleDataPageIO extends AbstractDataPageIO<MetastorageDataRow> {
     }
 
     /** {@inheritDoc} */
-    @Override
-    protected void writeRowData(
+    @Override protected void writeRowData(
         long pageAddr,
         int dataOff,
         int payloadSize,
-        MetastorageDataRow row,
+        SimpleDataRow row,
         boolean newRow
     ) throws IgniteCheckedException {
         long addr = pageAddr + dataOff;
@@ -106,12 +113,6 @@ public class SimpleDataPageIO extends AbstractDataPageIO<MetastorageDataRow> {
         PageUtils.putBytes(addr, 6, row.value());
     }
 
-    public static byte[] readPayload(long link) {
-        int size = PageUtils.getInt(link, 0);
-
-        return PageUtils.getBytes(link, 4, size);
-    }
-
     /** {@inheritDoc} */
     @Override protected void printPage(long addr, int pageSize, GridStringBuilder sb) throws IgniteCheckedException {
         sb.a("SimpleDataPageIO [\n");
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java
index 5ab1bf3..ff980af 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java
@@ -26,6 +26,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.InitNewPageRecord;
 import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
 import org.apache.ignite.internal.stat.IoStatisticsHolder;
 import org.apache.ignite.internal.util.GridUnsafe;
+import org.jetbrains.annotations.Nullable;
 
 import static java.lang.Boolean.FALSE;
 import static java.lang.Boolean.TRUE;
@@ -463,7 +464,7 @@ public abstract class PageHandler<X, R> {
         long pageId,
         long page,
         IgniteWriteAheadLogManager wal,
-        Boolean walPlc) {
+        @Nullable Boolean walPlc) {
         // If the page is clean, then it is either newly allocated or just after checkpoint.
         // In both cases we have to write full page contents to WAL.
         return wal != null && !wal.isAlwaysWriteFullPages() && walPlc != TRUE && !wal.disabled(cacheId) &&
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
index 86924c6..2831d87 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/FileWriteAheadLogManager.java
@@ -917,7 +917,7 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
         if (hnd != null)
             end = hnd.position();
 
-        return new RecordsIterator(
+        RecordsIterator iter = new RecordsIterator(
             cctx,
             walArchiveDir,
             walWorkDir,
@@ -932,6 +932,17 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
             segmentAware,
             segmentRouter,
             lockedSegmentFileInputFactory);
+
+        try {
+            iter.init(); // Make sure iterator is closed on any error.
+        }
+        catch (Throwable t) {
+            iter.close();
+
+            throw t;
+        }
+
+        return iter;
     }
 
     /** {@inheritDoc} */
@@ -2673,10 +2684,6 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
             this.decompressor = decompressor;
             this.segmentRouter = segmentRouter;
             this.segmentAware = segmentAware;
-
-            init();
-
-            advance();
         }
 
         /** {@inheritDoc} */
@@ -2760,6 +2767,8 @@ public class FileWriteAheadLogManager extends GridCacheSharedManagerAdapter impl
 
             if (log.isDebugEnabled())
                 log.debug("Initialized WAL cursor [start=" + start + ", end=" + end + ", curWalSegmIdx=" + curWalSegmIdx + ']');
+
+            advance();
         }
 
         /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
index 65f0aae..b6d20dc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/record/RecordTypes.java
@@ -60,6 +60,7 @@ public final class RecordTypes {
         DELTA_TYPE_SET.add(WALRecord.RecordType.PAGES_LIST_REMOVE_PAGE);
         DELTA_TYPE_SET.add(WALRecord.RecordType.META_PAGE_INIT);
         DELTA_TYPE_SET.add(WALRecord.RecordType.PARTITION_META_PAGE_UPDATE_COUNTERS);
+        DELTA_TYPE_SET.add(WALRecord.RecordType.PARTITION_META_PAGE_UPDATE_COUNTERS_V2);
         DELTA_TYPE_SET.add(WALRecord.RecordType.TRACKING_PAGE_DELTA);
         DELTA_TYPE_SET.add(WALRecord.RecordType.META_PAGE_UPDATE_LAST_SUCCESSFUL_SNAPSHOT_ID);
         DELTA_TYPE_SET.add(WALRecord.RecordType.META_PAGE_UPDATE_LAST_SUCCESSFUL_FULL_SNAPSHOT_ID);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
index 705cc38..ec68972 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV1Serializer.java
@@ -70,6 +70,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateLastSuc
 import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateLastSuccessfulSnapshotId;
 import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdateNextSnapshotId;
 import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdatePartitionDataRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.MetaPageUpdatePartitionDataRecordV2;
 import org.apache.ignite.internal.pagemem.wal.record.delta.NewRootInitRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PageListMetaResetCountRecord;
 import org.apache.ignite.internal.pagemem.wal.record.delta.PagesListAddPageRecord;
@@ -374,6 +375,10 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
                 return /*cache ID*/4 + /*page ID*/8 + /*upd cntr*/8 + /*rmv id*/8 + /*part size*/4 + /*counters page id*/8 + /*state*/ 1
                         + /*allocatedIdxCandidate*/ 4;
 
+            case PARTITION_META_PAGE_UPDATE_COUNTERS_V2:
+                return /*cache ID*/4 + /*page ID*/8 + /*upd cntr*/8 + /*rmv id*/8 + /*part size*/4 + /*counters page id*/8 + /*state*/ 1
+                    + /*allocatedIdxCandidate*/ 4 + /*link*/ 8;
+
             case MEMORY_RECOVERY:
                 return 8;
 
@@ -597,17 +602,12 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
                 break;
 
             case PARTITION_META_PAGE_UPDATE_COUNTERS:
-                cacheId = in.readInt();
-                pageId = in.readLong();
+                res = new MetaPageUpdatePartitionDataRecord(in);
 
-                long updCntr = in.readLong();
-                long rmvId = in.readLong();
-                int partSize = in.readInt();
-                long countersPageId = in.readLong();
-                byte state = in.readByte();
-                int allocatedIdxCandidate = in.readInt();
+                break;
 
-                res = new MetaPageUpdatePartitionDataRecord(cacheId, pageId, updCntr, rmvId, partSize, countersPageId, state, allocatedIdxCandidate);
+            case PARTITION_META_PAGE_UPDATE_COUNTERS_V2:
+                res = new MetaPageUpdatePartitionDataRecordV2(in);
 
                 break;
 
@@ -950,7 +950,7 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
                 int dstIdx = in.readUnsignedShort();
                 long srcPageId = in.readLong();
                 int srcIdx = in.readUnsignedShort();
-                rmvId = in.readLong();
+                long rmvId = in.readLong();
 
                 res = new InnerReplaceRecord<>(cacheId, pageId, dstIdx, srcPageId, srcIdx, rmvId);
 
@@ -1112,7 +1112,7 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
 
                 partId = in.readInt();
 
-                state = in.readByte();
+                byte state = in.readByte();
 
                 long updateCntr = in.readLong();
 
@@ -1201,17 +1201,8 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
                 break;
 
             case PARTITION_META_PAGE_UPDATE_COUNTERS:
-                MetaPageUpdatePartitionDataRecord partDataRec = (MetaPageUpdatePartitionDataRecord)rec;
-
-                buf.putInt(partDataRec.groupId());
-                buf.putLong(partDataRec.pageId());
-
-                buf.putLong(partDataRec.updateCounter());
-                buf.putLong(partDataRec.globalRemoveId());
-                buf.putInt(partDataRec.partitionSize());
-                buf.putLong(partDataRec.countersPageId());
-                buf.put(partDataRec.state());
-                buf.putInt(partDataRec.allocatedIndexCandidate());
+            case PARTITION_META_PAGE_UPDATE_COUNTERS_V2:
+                ((MetaPageUpdatePartitionDataRecord)rec).toBytes(buf);
 
                 break;
 
@@ -1690,7 +1681,7 @@ public class RecordDataV1Serializer implements RecordDataSerializer {
 
                 buf.put(partMetaStateRecord.state());
 
-                buf.putLong(partMetaStateRecord.updateCounter());
+                buf.putLong(0);
 
                 break;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV2Serializer.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV2Serializer.java
index ec45a06..3063a25 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV2Serializer.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/wal/serializer/RecordDataV2Serializer.java
@@ -38,6 +38,7 @@ import org.apache.ignite.internal.pagemem.wal.record.MvccDataEntry;
 import org.apache.ignite.internal.pagemem.wal.record.MvccDataRecord;
 import org.apache.ignite.internal.pagemem.wal.record.MvccTxRecord;
 import org.apache.ignite.internal.pagemem.wal.record.PageSnapshot;
+import org.apache.ignite.internal.pagemem.wal.record.RollbackRecord;
 import org.apache.ignite.internal.pagemem.wal.record.SnapshotRecord;
 import org.apache.ignite.internal.pagemem.wal.record.TxRecord;
 import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
@@ -111,6 +112,9 @@ public class RecordDataV2Serializer extends RecordDataV1Serializer {
             case MVCC_TX_RECORD:
                 return txRecordSerializer.size((MvccTxRecord)rec);
 
+            case ROLLBACK_TX_RECORD:
+                return 4 + 4 + 8 + 8;
+
             default:
                 return super.plainSize(rec);
         }
@@ -206,6 +210,14 @@ public class RecordDataV2Serializer extends RecordDataV1Serializer {
             case MVCC_TX_RECORD:
                 return txRecordSerializer.readMvccTx(in);
 
+            case ROLLBACK_TX_RECORD:
+                int grpId = in.readInt();
+                int partId = in.readInt();
+                long start = in.readLong();
+                long range = in.readLong();
+
+                return new RollbackRecord(grpId, partId, start, range);
+
             default:
                 return super.readPlainRecord(type, in, encrypted, recordSize);
         }
@@ -288,6 +300,16 @@ public class RecordDataV2Serializer extends RecordDataV1Serializer {
 
                 break;
 
+            case ROLLBACK_TX_RECORD:
+                RollbackRecord rb = (RollbackRecord)rec;
+
+                buf.putInt(rb.groupId());
+                buf.putInt(rb.partitionId());
+                buf.putLong(rb.start());
+                buf.putLong(rb.range());
+
+                break;
+
             default:
                 super.writePlainRecord(rec, buf);
         }
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 c707c72..82400f2 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
@@ -114,9 +114,10 @@ public class CacheContinuousQueryEventBuffer {
     }
 
     /**
+     * @param backup {@code True} if backup context.
      * @return Initial partition counter.
      */
-    protected long currentPartitionCounter() {
+    protected long currentPartitionCounter(boolean backup) {
         return 0;
     }
 
@@ -153,7 +154,7 @@ public class CacheContinuousQueryEventBuffer {
         Object res = null;
 
         for (;;) {
-            batch = initBatch(entry.topologyVersion());
+            batch = initBatch(entry.topologyVersion(), backup);
 
             if (batch == null || cntr < batch.startCntr) {
                 if (backup) {
@@ -185,7 +186,7 @@ public class CacheContinuousQueryEventBuffer {
 
                 res = processPending(res, batch, backup);
 
-                batch0 = initBatch(entry.topologyVersion());
+                batch0 = initBatch(entry.topologyVersion(), backup);
             }
             while (batch != batch0);
         }
@@ -195,16 +196,17 @@ public class CacheContinuousQueryEventBuffer {
 
     /**
      * @param topVer Current event topology version.
+     * @param backup {@code True} if backup entry.
      * @return Current batch.
      */
-    @Nullable private Batch initBatch(AffinityTopologyVersion topVer) {
+    private Batch initBatch(AffinityTopologyVersion topVer, boolean backup) {
         Batch batch = curBatch.get();
 
         if (batch != null)
             return batch;
 
         for (;;) {
-            long curCntr = currentPartitionCounter();
+            long curCntr = currentPartitionCounter(backup);
 
             if (curCntr == -1)
                 return null;
@@ -483,4 +485,4 @@ public class CacheContinuousQueryEventBuffer {
             return res;
         }
     }
-}
\ No newline at end of file
+}
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 7f9b918..d8737bd 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
@@ -54,6 +54,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheAdapter;
 import org.apache.ignite.internal.processors.cache.GridCacheAffinityManager;
 import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheDeploymentManager;
+import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.GridDhtAtomicAbstractUpdateFuture;
 import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition;
 import org.apache.ignite.internal.processors.cache.query.CacheQueryType;
@@ -1106,13 +1107,14 @@ public class CacheContinuousQueryHandler<K, V> implements GridContinuousHandler
 
         if (buf == null) {
             buf = new CacheContinuousQueryEventBuffer(part) {
-                @Override protected long currentPartitionCounter() {
+                @Override protected long currentPartitionCounter(boolean backup) {
                     GridDhtLocalPartition locPart = cctx.topology().localPartition(part, null, false);
 
                     if (locPart == null)
                         return -1L;
 
-                    return locPart.updateCounter();
+                    // Use HWM for primary, LWM for backup.
+                    return backup ? locPart.updateCounter() : locPart.reservedCounter();
                 }
             };
 
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 7d0f988..a105a2f 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
@@ -40,6 +40,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.distributed.IgniteExternalizableExpiryPolicy;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.lang.GridAbsClosureX;
 import org.apache.ignite.internal.util.lang.GridPeerDeployAware;
 import org.apache.ignite.internal.util.tostring.GridToStringBuilder;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -212,6 +213,11 @@ public class IgniteTxEntry implements GridPeerDeployAware, Message {
     /** */
     private GridCacheVersion serReadVer;
 
+    /** */
+    @GridDirectTransient
+    @GridToStringExclude
+    private transient @Nullable GridAbsClosureX cqNotifyC;
+
     /**
      * Required by {@link Externalizable}
      */
@@ -1262,6 +1268,19 @@ public class IgniteTxEntry implements GridPeerDeployAware, Message {
             key.getClass() : val != null ? val.getClass() : getClass();
     }
 
+    /**
+     */
+    public GridAbsClosureX cqNotifyClosure() {
+        return cqNotifyC;
+    }
+
+    /**
+     * @param clo Clo.
+     */
+    public void cqNotifyClosure(GridAbsClosureX clo) {
+        cqNotifyC = clo;
+    }
+
     /** {@inheritDoc} */
     @Override public ClassLoader classLoader() {
         return deployClass().getClassLoader();
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 719bf04..787fb97 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
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.UUID;
 import javax.cache.processor.EntryProcessor;
 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.failure.FailureContext;
@@ -30,6 +31,7 @@ import org.apache.ignite.failure.FailureType;
 import org.apache.ignite.internal.IgniteFutureTimeoutCheckedException;
 import org.apache.ignite.internal.IgniteInternalFuture;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
+import org.apache.ignite.internal.pagemem.wal.record.RollbackRecord;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection;
 import org.apache.ignite.internal.processors.cache.CacheObject;
@@ -1352,8 +1354,14 @@ public class IgniteTxHandler {
 
         if (dhtTx != null)
             finish(nodeId, dhtTx, req);
-        else
-            applyPartitionsUpdatesCounters(req.updateCounters());
+        else {
+            try {
+                applyPartitionsUpdatesCounters(req.updateCounters(), !req.commit(), false);
+            }
+            catch (IgniteCheckedException e) {
+                throw new IgniteException(e);
+            }
+        }
 
         if (nearTx != null)
             finish(nodeId, nearTx, req);
@@ -1422,9 +1430,6 @@ public class IgniteTxHandler {
                 // Complete remote candidates.
                 tx.doneRemote(req.baseVersion(), null, null, null);
 
-                tx.setPartitionUpdateCounters(
-                    req.partUpdateCounters() != null ? req.partUpdateCounters().array() : null);
-
                 tx.commitRemoteTx();
             }
             else {
@@ -1698,7 +1703,7 @@ public class IgniteTxHandler {
                     if (log.isDebugEnabled())
                         log.debug("Attempt to start a completed transaction (will ignore): " + tx);
 
-                    applyPartitionsUpdatesCounters(req.updateCounters());
+                    applyPartitionsUpdatesCounters(req.updateCounters(), true, false);
 
                     return null;
                 }
@@ -1710,7 +1715,7 @@ public class IgniteTxHandler {
 
                     ctx.tm().uncommitTx(tx);
 
-                    applyPartitionsUpdatesCounters(req.updateCounters());
+                    applyPartitionsUpdatesCounters(req.updateCounters(), true, false);
 
                     return null;
                 }
@@ -1720,8 +1725,13 @@ public class IgniteTxHandler {
                 tx.transactionNodes(req.transactionNodes());
             }
 
-            if (req.updateCounters() != null)
-                tx.txCounters(true).updateCounters(req.updateCounters());
+            TxCounters txCounters = null;
+
+            if (req.updateCounters() != null) {
+                txCounters = tx.txCounters(true);
+
+                txCounters.updateCounters(req.updateCounters());
+            }
 
             if (!tx.isSystemInvalidate()) {
                 int idx = 0;
@@ -1739,6 +1749,13 @@ public class IgniteTxHandler {
                         try {
                             tx.addWrite(entry, ctx.deploy().globalLoader());
 
+                            if (txCounters != null) {
+                                Long cntr = txCounters.generateNextCounter(entry.cacheId(), entry.cached().partition());
+
+                                if (cntr != null) // Counter is null if entry is no-op.
+                                    entry.updateCounter(cntr);
+                            }
+
                             if (isNearEnabled(cacheCtx) && req.invalidateNearEntry(idx))
                                 invalidateNearEntry(cacheCtx, entry.key(), req.version());
 
@@ -2198,7 +2215,12 @@ public class IgniteTxHandler {
      * @param req Request.
      */
     private void processPartitionCountersRequest(UUID nodeId, PartitionCountersNeighborcastRequest req) {
-        applyPartitionsUpdatesCounters(req.updateCounters());
+        try {
+            applyPartitionsUpdatesCounters(req.updateCounters(), true, false);
+        }
+        catch (IgniteCheckedException e) {
+            throw new IgniteException(e);
+        }
 
         try {
             ctx.io().send(nodeId, new PartitionCountersNeighborcastResponse(req.futId()), SYSTEM_POOL);
@@ -2230,21 +2252,32 @@ public class IgniteTxHandler {
     }
 
     /**
-     * Applies partition counter updates for mvcc transactions.
+     * @param counters Counters.
+     */
+    public void applyPartitionsUpdatesCounters(Iterable<PartitionUpdateCountersMessage> counters)
+        throws IgniteCheckedException {
+        applyPartitionsUpdatesCounters(counters, false, false);
+    }
+
+    /**
+     * Applies partition counter updates for transactions.
      *
      * @param counters Counter values to be updated.
+     * @param rollback {@code True} if applied from rollbacks.
      */
-    public void applyPartitionsUpdatesCounters(Iterable<PartitionUpdateCountersMessage> counters) {
+    public void applyPartitionsUpdatesCounters(Iterable<PartitionUpdateCountersMessage> counters,
+        boolean rollback,
+        boolean rollbackOnPrimary) throws IgniteCheckedException {
         if (counters == null)
             return;
 
         for (PartitionUpdateCountersMessage counter : counters) {
             GridCacheContext ctx0 = ctx.cacheContext(counter.cacheId());
 
-            assert ctx0.mvccEnabled();
-
             GridDhtPartitionTopology top = ctx0.topology();
 
+            AffinityTopologyVersion topVer = top.readyTopologyVersion();
+
             assert top != null;
 
             for (int i = 0; i < counter.size(); i++) {
@@ -2255,8 +2288,29 @@ public class IgniteTxHandler {
 
                     if (part != null && part.reserve()) {
                         try {
-                            if (part.state() != GridDhtPartitionState.RENTING)
-                                part.updateCounter(counter.initialCounter(i), counter.updatesCount(i));
+                            if (part.state() != GridDhtPartitionState.RENTING) { // Check is actual only for backup node.
+                                long start = counter.initialCounter(i);
+                                long delta = counter.updatesCount(i);
+
+                                boolean updated = part.updateCounter(start, delta);
+
+                                // Need to log rolled back range for logical recovery.
+                                if (updated && rollback) {
+                                    if (part.group().persistenceEnabled() &&
+                                        part.group().walEnabled() &&
+                                        !part.group().mvccEnabled()) {
+                                        RollbackRecord rec = new RollbackRecord(part.group().groupId(), part.id(),
+                                            start, delta);
+
+                                        ctx.wal().log(rec);
+                                    }
+
+                                    for (int cntr = 1; cntr <= delta; cntr++) {
+                                        ctx0.continuousQueries().skipUpdateCounter(null, part.id(), start + cntr,
+                                            topVer, rollbackOnPrimary);
+                                    }
+                                }
+                            }
                             else
                                 invalid = true;
                         }
@@ -2272,7 +2326,7 @@ public class IgniteTxHandler {
                 }
 
                 if (invalid && log.isDebugEnabled())
-                    log.debug("Received partition update counters message for invalid partition: " +
+                    log.debug("Received partition update counters message for invalid partition, ignoring: " +
                         "[cacheId=" + counter.cacheId() + ", part=" + counter.partition(i) + "]");
             }
         }
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 26350b6..55376f9 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
@@ -57,7 +57,6 @@ import org.apache.ignite.internal.processors.cache.IgniteCacheExpiryPolicy;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage;
 import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition;
-import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState;
 import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionTopology;
 import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
 import org.apache.ignite.internal.processors.cache.store.CacheStoreManager;
@@ -95,6 +94,8 @@ import static org.apache.ignite.internal.processors.cache.GridCacheOperation.REA
 import static org.apache.ignite.internal.processors.cache.GridCacheOperation.RELOAD;
 import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
 import static org.apache.ignite.internal.processors.cache.GridCacheOperation.UPDATE;
+import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.LOST;
+import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.OWNING;
 import static org.apache.ignite.internal.processors.dr.GridDrType.DR_NONE;
 import static org.apache.ignite.internal.processors.dr.GridDrType.DR_PRIMARY;
 import static org.apache.ignite.transactions.TransactionState.COMMITTED;
@@ -431,7 +432,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
         try {
             cctx.tm().prepareTx(this, entries);
 
-            calculatePartitionUpdateCounters();
+            if (txState().mvccEnabled())
+                calculatePartitionUpdateCounters();
         }
         catch (IgniteCheckedException e) {
             throw e;
@@ -451,7 +453,7 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
      * pair (init, delta) values, where init - initial update counter, and delta - updates count made
      * by current transaction for a given partition.
      */
-    private void calculatePartitionUpdateCounters() {
+    public void calculatePartitionUpdateCounters() {
         TxCounters counters = txCounters(false);
 
         if (counters != null && F.isEmpty(counters.updateCounters())) {
@@ -468,9 +470,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
                     continue;
 
                 PartitionUpdateCountersMessage msg = new PartitionUpdateCountersMessage(cacheId, partToCntrs.size());
-                GridCacheContext ctx0 = cctx.cacheContext(cacheId);
 
-                assert ctx0 != null && ctx0.mvccEnabled();
+                GridCacheContext ctx0 = cctx.cacheContext(cacheId);
 
                 GridDhtPartitionTopology top = ctx0.topology();
 
@@ -490,7 +491,11 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
 
                         GridDhtLocalPartition part = top.localPartition(p);
 
-                        assert part != null && part.state() == GridDhtPartitionState.OWNING;
+                        // LOST state is possible if tx is started over LOST partition.
+                        assert part != null && (part.state() == OWNING || part.state() == LOST):
+                            part == null ?
+                                "map=" + top.partitionMap(false) + ", lost=" + top.lostPartitions() :
+                                "part=" + part.toString();
 
                         msg.add(p, part.getAndIncrementUpdateCounter(cntr), cntr);
                     }
@@ -577,6 +582,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
 
                 AffinityTopologyVersion topVer = topologyVersion();
 
+                TxCounters txCounters = txCounters(false);
+
                 /*
                  * Commit to cache. Note that for 'near' transaction we loop through all the entries.
                  */
@@ -893,7 +900,15 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
                             txEntry.cached(entryEx(cacheCtx, txEntry.txKey(), topologyVersion()));
                         }
                     }
+                }
+
+                if (!txState.mvccEnabled() && txCounters != null) {
+                    cctx.tm().txHandler().applyPartitionsUpdatesCounters(txCounters.updateCounters());
 
+                    for (IgniteTxEntry entry : commitEntries) {
+                        if (entry.cqNotifyClosure() != null)
+                            entry.cqNotifyClosure().applyx();
+                    }
                 }
 
                 // Apply cache sizes only for primary nodes. Update counters were applied on prepare state.
@@ -1072,6 +1087,13 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
         }
 
         if (DONE_FLAG_UPD.compareAndSet(this, 0, 1)) {
+            if (!txState.mvccEnabled()) {
+                TxCounters txCounters = txCounters(false);
+
+                if (txCounters != null)
+                    cctx.tm().txHandler().applyPartitionsUpdatesCounters(txCounters.updateCounters(), true, true);
+            }
+
             cctx.tm().rollbackTx(this, clearThreadMap, skipCompletedVersions());
 
             cctx.mvccCaching().onTxFinished(this, false);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
index c88c863..6f2d594 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxManager.java
@@ -2128,8 +2128,8 @@ public class IgniteTxManager extends GridCacheSharedManagerAdapter {
 
         if (commit)
             tx.commitAsync().listen(new CommitListener(tx));
-        else if (tx.mvccSnapshot() != null && !tx.local())
-            // remote (backup) mvcc transaction sends partition counters to other backup transaction
+        else if (!tx.local())
+            // remote (backup) transaction sends partition counters to other backup transaction on recovery rollback
             // in order to keep counters consistent
             neighborcastPartitionCountersAndRollback(tx);
         else
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxCounters.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxCounters.java
index 515aeef..15c92f9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxCounters.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/TxCounters.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import org.apache.ignite.internal.processors.cache.distributed.dht.PartitionUpdateCountersMessage;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -37,7 +38,7 @@ public class TxCounters {
     private final Map<Integer, Map<Integer, AtomicLong>> updCntrsAcc = new HashMap<>();
 
     /** Final update counters for cache partitions in the end of transaction */
-    private volatile Collection<PartitionUpdateCountersMessage> updCntrs;
+    private volatile Map<Integer, PartitionUpdateCountersMessage> updCntrs;
 
     /** Counter tracking number of entries locked by tx. */
     private final AtomicInteger lockCntr = new AtomicInteger();
@@ -68,14 +69,17 @@ public class TxCounters {
      * @param updCntrs Final update counters.
      */
     public void updateCounters(Collection<PartitionUpdateCountersMessage> updCntrs) {
-        this.updCntrs = updCntrs;
+        this.updCntrs = U.newHashMap(updCntrs.size());
+
+        for (PartitionUpdateCountersMessage cntr : updCntrs)
+            this.updCntrs.put(cntr.cacheId(), cntr);
     }
 
     /**
      * @return Final update counters.
      */
     @Nullable public Collection<PartitionUpdateCountersMessage> updateCounters() {
-        return updCntrs;
+        return updCntrs == null ? null : updCntrs.values();
     }
 
     /**
@@ -145,4 +149,19 @@ public class TxCounters {
     public int lockCounter() {
         return lockCntr.get();
     }
+
+    /**
+     * @param cacheId Cache id.
+     * @param partId Partition id.
+     *
+     * @return Counter or {@code null} if cache partition has not updates.
+     */
+    public Long generateNextCounter(int cacheId, int partId) {
+        PartitionUpdateCountersMessage msg = updCntrs.get(cacheId);
+
+        if (msg == null)
+            return null;
+
+        return msg.nextCounter(partId);
+    }
 }
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 f05e0e5..17d4be0 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
@@ -44,6 +44,7 @@ import org.apache.ignite.internal.GridKernalContext;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.IgniteFeatures;
 import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.internal.NodeStoppingException;
 import org.apache.ignite.internal.cluster.ClusterGroupAdapter;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
 import org.apache.ignite.internal.cluster.DistributedBaselineConfiguration;
@@ -385,7 +386,7 @@ public class GridClusterStateProcessor extends GridProcessorAdapter implements I
         GridChangeGlobalStateFuture fut = this.stateChangeFut.get();
 
         if (fut != null)
-            fut.onDone(new IgniteCheckedException("Failed to wait for cluster state change, node is stopping."));
+            fut.onDone(new NodeStoppingException("Failed to wait for cluster state change, node is stopping."));
 
         super.onKernalStop(cancel);
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/stat/IoStatisticsHolder.java b/modules/core/src/main/java/org/apache/ignite/internal/stat/IoStatisticsHolder.java
index 0c36820..5b7a57f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/stat/IoStatisticsHolder.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/stat/IoStatisticsHolder.java
@@ -24,7 +24,6 @@ import java.util.Map;
  * Holder of IO statistics.
  */
 public interface IoStatisticsHolder {
-
     /**
      * Track logical read of given page.
      *
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
index 9e854d2..140349a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
@@ -141,6 +141,9 @@ public interface IgniteTree<L, T> {
         REMOVE,
 
         /** */
-        PUT
+        PUT,
+
+        /** */
+        IN_PLACE
     }
 }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
index 395705c..5d9d8da 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java
@@ -9519,6 +9519,60 @@ public abstract class IgniteUtils {
     }
 
     /**
+     * Create a map with single key-value pair.
+     *
+     * @param k Key.
+     * @param v Value.
+     * @return Map.
+     */
+    public static <K, V> Map<K, V> map(K k, V v) {
+        GridLeanMap<K, V> map = new GridLeanMap<>(1);
+
+        map.put(k, v);
+
+        return map;
+    }
+
+    /**
+     * Create a map with two key-value pairs.
+     *
+     * @param k1 Key 1.
+     * @param v1 Value 1.
+     * @param k2 Key 2.
+     * @param v2 Value 2.
+     * @return Map.
+     */
+    public static <K, V> Map<K, V> map(K k1, V v1, K k2, V v2) {
+        GridLeanMap<K, V> map = new GridLeanMap<>(2);
+
+        map.put(k1, v1);
+        map.put(k2, v2);
+
+        return map;
+    }
+
+    /**
+     * Create a map with three key-value pairs.
+     *
+     * @param k1 Key 1.
+     * @param v1 Value 1.
+     * @param k2 Key 2.
+     * @param v2 Value 2.
+     * @param k3 Key 3.
+     * @param v3 Value 3.
+     * @return Map.
+     */
+    public static <K, V> Map<K, V> map(K k1, V v1, K k2, V v2, K k3, V v3) {
+        GridLeanMap<K, V> map = new GridLeanMap<>(3);
+
+        map.put(k1, v1);
+        map.put(k2, v2);
+        map.put(k3, v3);
+
+        return map;
+    }
+
+    /**
      * @param col non-null collection with one element
      * @return a SingletonList containing the element in the original collection
      */
@@ -9699,7 +9753,7 @@ public abstract class IgniteUtils {
     public static <T extends R, R> List<R> arrayList(Collection<T> c, @Nullable IgnitePredicate<? super T>... p) {
         assert c != null;
 
-        return arrayList(c, c.size(), p);
+        return arrayList(c.iterator(), c.size(), p);
     }
 
     /**
@@ -9718,14 +9772,16 @@ public abstract class IgniteUtils {
      * @param p Optional filters.
      * @return Resulting array list.
      */
-    public static <T extends R, R> List<R> arrayList(Iterable<T> c, int cap,
+    public static <T extends R, R> List<R> arrayList(Iterator<T> c, int cap,
         @Nullable IgnitePredicate<? super T>... p) {
         assert c != null;
         assert cap >= 0;
 
         List<R> list = new ArrayList<>(cap);
 
-        for (T t : c) {
+        while (c.hasNext()) {
+            T t = c.next();
+
             if (F.isAll(t, p))
                 list.add(t);
         }
diff --git a/modules/core/src/test/config/log4j-test.xml b/modules/core/src/test/config/log4j-test.xml
index bb5f94a..7206b4e 100755
--- a/modules/core/src/test/config/log4j-test.xml
+++ b/modules/core/src/test/config/log4j-test.xml
@@ -97,6 +97,15 @@
         </category>
     -->
 
+    <!--
+        Uncomment to enable debugging of partition counters.
+    -->
+    <!--
+    <category name="org.apache.ignite.internal.processors.cache.PartitionTxUpdateCounterDebugWrapper">
+        <level value="DEBUG"/>
+    </category>
+    -->
+
     <!-- Disable all open source debugging. -->
     <category name="org">
         <level value="INFO"/>
diff --git a/modules/core/src/test/java/org/apache/ignite/cache/ResetLostPartitionTest.java b/modules/core/src/test/java/org/apache/ignite/cache/ResetLostPartitionTest.java
index 8d318dc..3e1568a 100644
--- a/modules/core/src/test/java/org/apache/ignite/cache/ResetLostPartitionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/cache/ResetLostPartitionTest.java
@@ -36,6 +36,7 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
 
+import static org.apache.ignite.configuration.WALMode.LOG_ONLY;
 import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.LOST;
 import static org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState.OWNING;
 import static org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.DFLT_STORE_DIR;
@@ -75,6 +76,8 @@ public class ResetLostPartitionTest extends GridCommonAbstractTest {
 
         DataStorageConfiguration storageCfg = new DataStorageConfiguration();
 
+        storageCfg.setPageSize(1024).setWalMode(LOG_ONLY).setWalSegmentSize(8 * 1024 * 1024);
+
         storageCfg.getDefaultDataRegionConfiguration()
             .setPersistenceEnabled(true)
             .setMaxSize(500L * 1024 * 1024);
@@ -104,7 +107,7 @@ public class ResetLostPartitionTest extends GridCommonAbstractTest {
             .setBackups(1)
             .setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC)
             .setPartitionLossPolicy(PartitionLossPolicy.READ_ONLY_SAFE)
-            .setAffinity(new RendezvousAffinityFunction(false, 1024))
+            .setAffinity(new RendezvousAffinityFunction(false, 64))
             .setIndexedTypes(String.class, String.class);
     }
 
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java b/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
index 988395f..5969d25 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/TestRecordingCommunicationSpi.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -30,7 +30,7 @@ import org.apache.ignite.IgniteException;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
 import org.apache.ignite.internal.managers.communication.GridIoMessage;
-import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
 import org.apache.ignite.internal.util.typedef.T2;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiInClosure;
@@ -300,7 +300,7 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
      * Stops block messages and sends all already blocked messages.
      */
     public void stopBlock() {
-        stopBlock(true, null);
+        stopBlock(true, null, true, true);
     }
 
     /**
@@ -309,7 +309,7 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
      * @param sndMsgs {@code True} to send blocked messages.
      */
     public void stopBlock(boolean sndMsgs) {
-        stopBlock(sndMsgs, null);
+        stopBlock(sndMsgs, null, true, true);
     }
 
     /**
@@ -320,15 +320,37 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
      * @param unblockPred If not null unblocks only messages allowed by predicate.
      */
     public void stopBlock(boolean sndMsgs, @Nullable IgnitePredicate<T2<ClusterNode, GridIoMessage>> unblockPred) {
+        stopBlock(sndMsgs, unblockPred, true, true);
+    }
+
+    /**
+     * Stops block messages and sends all already blocked messages if sndMsgs is 'true' optionally filtered by
+     * unblockPred.
+     *
+     * @param sndMsgs If {@code true} sends blocked messages.
+     * @param unblockPred If not null unblocks only messages allowed by predicate.
+     * @param clearFilters {@code true} to clear filters.
+     * @param rmvBlockedMsgs {@code true} to remove blocked messages. Sometimes useful in conjunction with {@code
+     * sndMsgs=false}.
+     */
+    public void stopBlock(boolean sndMsgs, @Nullable IgnitePredicate<T2<ClusterNode, GridIoMessage>> unblockPred,
+        boolean clearFilters, boolean rmvBlockedMsgs) {
         synchronized (this) {
-            blockCls.clear();
-            blockP = null;
+            if (clearFilters) {
+                blockCls.clear();
+                blockP = null;
+            }
 
-            Collection<T2<ClusterNode, GridIoMessage>> msgs =
-                unblockPred == null ? blockedMsgs : F.view(blockedMsgs, unblockPred);
+            Iterator<T2<ClusterNode, GridIoMessage>> iter = blockedMsgs.iterator();
 
-            if (sndMsgs) {
-                for (T2<ClusterNode, GridIoMessage> msg : msgs) {
+            while (iter.hasNext()) {
+                T2<ClusterNode, GridIoMessage> msg = iter.next();
+
+                // It is important what predicate if called only once for each message.
+                if (unblockPred != null && !unblockPred.apply(msg))
+                    continue;
+
+                if (sndMsgs) {
                     try {
                         ignite.log().info("Send blocked message [node=" + msg.get1().id() +
                             ", order=" + msg.get1().order() +
@@ -340,9 +362,18 @@ public class TestRecordingCommunicationSpi extends TcpCommunicationSpi {
                         U.error(ignite.log(), "Failed to send blocked message: " + msg, e);
                     }
                 }
-            }
 
-            msgs.clear();
+                if (rmvBlockedMsgs)
+                    iter.remove();
+            }
         }
     }
+
+    /**
+     * Stop blocking all messages.
+     */
+    public static void stopBlockAll() {
+        for (Ignite ignite : G.allGrids())
+            spi(ignite).stopBlock(true);
+    }
 }
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
index 222b161..2f02e10 100644
--- 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
@@ -115,7 +115,8 @@ import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_REA
 import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE;
 
 /**
- *
+ * TODO FIXME Mixed atomic-tx cache groups currently are broken if tx cache is dynamically started after atomic.
+ * TODO FIXME See https://issues.apache.org/jira/browse/IGNITE-11797
  */
 @SuppressWarnings({"unchecked", "ThrowableNotThrown"})
 public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
@@ -2291,7 +2292,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
 
         for (final int i : sequence(loaders)) {
             final IgniteDataStreamer ldr = clientNode.dataStreamer(cache.getName());
-
+            ldr.allowOverwrite(true); // TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11793
             ldr.autoFlushFrequency(0);
 
             cls.add(new Callable<Void>() {
@@ -2985,6 +2986,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
     }
 
     /**
+     * TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11797
      * @throws Exception If failed.
      */
     @Test
@@ -3090,6 +3092,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
     }
 
     /**
+     * TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11797
      * @throws Exception If failed.
      */
     @Test
@@ -3196,7 +3199,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
 
                         IgniteCache cache = node.createCache(cacheConfiguration(grp, "tmpCache-" + cntr++,
                             PARTITIONED,
-                            rnd.nextBoolean() ? ATOMIC : TRANSACTIONAL,
+                            grp.equals(GROUP1) ? ATOMIC : TRANSACTIONAL,
                             backups,
                             rnd.nextBoolean()));
 
@@ -3816,6 +3819,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
     }
 
     /**
+     * TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11797
      * @throws Exception If failed.
      */
     @Test
@@ -3824,6 +3828,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
     }
 
     /**
+     * TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11797
      * @throws Exception If failed.
      */
     @Test
@@ -3846,8 +3851,8 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
         client.createCache(cacheConfiguration(GROUP1, "c2", PARTITIONED, TRANSACTIONAL, 1, false));
         client.createCache(cacheConfiguration(GROUP1, "c3", PARTITIONED, ATOMIC, 1, false));
 
-        client.createCache(cacheConfiguration(GROUP2, "c4", PARTITIONED, TRANSACTIONAL, 1, false));
-        client.createCache(cacheConfiguration(GROUP2, "c5", PARTITIONED, ATOMIC, 1, false));
+        client.createCache(cacheConfiguration(GROUP2, "c4", PARTITIONED, ATOMIC, 1, false));
+        client.createCache(cacheConfiguration(GROUP2, "c5", PARTITIONED, TRANSACTIONAL, 1, false));
         client.createCache(cacheConfiguration(GROUP2, "c6", PARTITIONED, TRANSACTIONAL, 1, false));
 
         client.createCache(cacheConfiguration(null, "c7", PARTITIONED, ATOMIC, 1, false));
@@ -3946,6 +3951,7 @@ public class IgniteCacheGroupsTest extends GridCommonAbstractTest {
     }
 
     /**
+     * TODO FIXME https://issues.apache.org/jira/browse/IGNITE-11797
      * @throws Exception If failed.
      */
     @Test
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionLossDetectionOnNodeLeftTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionLossDetectionOnNodeLeftTest.java
new file mode 100644
index 0000000..61b024c
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/CachePartitionLossDetectionOnNodeLeftTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.events.CacheRebalancingEvent;
+import org.apache.ignite.events.EventType;
+import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL;
+import static org.apache.ignite.cache.CacheMode.PARTITIONED;
+import static org.apache.ignite.cache.PartitionLossPolicy.IGNORE;
+import static org.apache.ignite.testframework.GridTestUtils.mergeExchangeWaitVersion;
+
+/**
+ * Tests if lost partitions are same on left nodes after other owners removal.
+ */
+@RunWith(JUnit4.class)
+public class CachePartitionLossDetectionOnNodeLeftTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setConsistentId(igniteInstanceName);
+
+        cfg.setCacheConfiguration(new CacheConfiguration(DEFAULT_CACHE_NAME).
+            setAtomicityMode(TRANSACTIONAL).
+            setCacheMode(PARTITIONED).
+            setPartitionLossPolicy(IGNORE). // If default will ever change...
+            setBackups(0));
+
+        return cfg;
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void testPartitionLossDetectionOnNodeLeft() throws Exception {
+        try {
+            final Ignite srv0 = startGrids(5);
+
+            List<Integer> lost0 = Collections.synchronizedList(new ArrayList<>());
+            List<Integer> lost1 = Collections.synchronizedList(new ArrayList<>());
+
+            grid(0).events().localListen(evt -> {
+                lost0.add(((CacheRebalancingEvent)evt).partition());
+
+                return true;
+            }, EventType.EVT_CACHE_REBALANCE_PART_DATA_LOST);
+
+            grid(1).events().localListen(evt -> {
+                lost1.add(((CacheRebalancingEvent)evt).partition());
+
+                return true;
+            }, EventType.EVT_CACHE_REBALANCE_PART_DATA_LOST);
+
+            awaitPartitionMapExchange();
+
... 6200 lines suppressed ...