ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ira...@apache.org
Subject ignite git commit: IGNITE-8529 Implement testing framework for checking WAL delta records consistency - Fixes #4159.
Date Thu, 14 Jun 2018 17:13:56 GMT
Repository: ignite
Updated Branches:
  refs/heads/master 2d746b5d3 -> 34059d0ca


IGNITE-8529 Implement testing framework for checking WAL delta records consistency - Fixes #4159.

Signed-off-by: Ivan Rakov <irakov@apache.org>


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

Branch: refs/heads/master
Commit: 34059d0ca4ae7a20df766b8b140dd84d50edbac9
Parents: 2d746b5
Author: Aleksey Plekhanov <plehanov.alex@gmail.com>
Authored: Thu Jun 14 20:10:50 2018 +0300
Committer: Ivan Rakov <irakov@apache.org>
Committed: Thu Jun 14 20:10:50 2018 +0300

----------------------------------------------------------------------
 .../processors/cache/GridCacheProcessor.java    |  18 +-
 .../org.apache.ignite.plugin.PluginProvider     |   3 +-
 .../wal/AbstractWalDeltaConsistencyTest.java    |  84 ++
 .../wal/CpTriggeredWalDeltaConsistencyTest.java |  65 ++
 .../wal/ExplicitWalDeltaConsistencyTest.java    |  93 +++
 .../wal/SysPropWalDeltaConsistencyTest.java     |  82 ++
 .../wal/memtracker/PageMemoryTracker.java       | 806 +++++++++++++++++++
 .../PageMemoryTrackerConfiguration.java         |  69 ++
 .../PageMemoryTrackerPluginProvider.java        | 200 +++++
 .../ignite/testsuites/IgnitePdsTestSuite.java   |   8 +
 10 files changed, 1422 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheProcessor.java
index 92e2e7b..ae79ef0 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
@@ -2429,12 +2429,20 @@ public class GridCacheProcessor extends GridProcessorAdapter {
 
             dbMgr = new GridCacheDatabaseSharedManager(ctx);
 
-            pageStoreMgr = new FilePageStoreManager(ctx);
+            pageStoreMgr = ctx.plugins().createComponent(IgnitePageStoreManager.class);
 
-            if (ctx.config().getDataStorageConfiguration().getWalMode() == WALMode.FSYNC && !walFsyncWithDedicatedWorker)
-                walMgr = new FsyncModeFileWriteAheadLogManager(ctx);
-            else
-                walMgr = new FileWriteAheadLogManager(ctx);
+            if (pageStoreMgr == null)
+                pageStoreMgr = new FilePageStoreManager(ctx);
+
+            walMgr = ctx.plugins().createComponent(IgniteWriteAheadLogManager.class);
+
+            if (walMgr == null) {
+                if (ctx.config().getDataStorageConfiguration().getWalMode() == WALMode.FSYNC &&
+                    !walFsyncWithDedicatedWorker)
+                    walMgr = new FsyncModeFileWriteAheadLogManager(ctx);
+                else
+                    walMgr = new FileWriteAheadLogManager(ctx);
+            }
         }
         else
             dbMgr = new IgniteCacheDatabaseSharedManager();

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider b/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider
index 959d444..1c03b7c 100644
--- a/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider
+++ b/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider
@@ -1,2 +1,3 @@
 org.apache.ignite.spi.discovery.tcp.TestReconnectPluginProvider
-org.apache.ignite.internal.processors.cache.persistence.standbycluster.IgniteStandByClusterTest$StanByClusterTestProvider
\ No newline at end of file
+org.apache.ignite.internal.processors.cache.persistence.standbycluster.IgniteStandByClusterTest$StanByClusterTestProvider
+org.apache.ignite.internal.processors.cache.persistence.wal.memtracker.PageMemoryTrackerPluginProvider

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalDeltaConsistencyTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalDeltaConsistencyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalDeltaConsistencyTest.java
new file mode 100644
index 0000000..c20285b
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/AbstractWalDeltaConsistencyTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.wal;
+
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.failure.FailureHandler;
+import org.apache.ignite.failure.StopNodeFailureHandler;
+import org.apache.ignite.internal.processors.cache.persistence.wal.memtracker.PageMemoryTrackerConfiguration;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ * Abstract WAL delta records consistency test.
+ */
+public abstract class AbstractWalDeltaConsistencyTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setConsistentId(igniteInstanceName);
+
+        cfg.setDataStorageConfiguration(getDataStorageConfiguration());
+
+        cfg.setPluginConfigurations(new PageMemoryTrackerConfiguration().setEnabled(true)
+            .setCheckPagesOnCheckpoint(checkPagesOnCheckpoint()));
+
+        return cfg;
+    }
+
+    /**
+     * Check page memory on each checkpoint.
+     */
+    protected boolean checkPagesOnCheckpoint() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected FailureHandler getFailureHandler(String igniteInstanceName) {
+        return checkPagesOnCheckpoint() ? new StopNodeFailureHandler() : super.getFailureHandler(igniteInstanceName);
+    }
+
+    /**
+     * Default configuration contains one data region ('dflt-plc') with persistence enabled.
+     * This method should be overridden by subclasses if another data storage configuration is needed.
+     *
+     * @return Data storage configuration used for starting of grid.
+     */
+    protected DataStorageConfiguration getDataStorageConfiguration() {
+        return new DataStorageConfiguration()
+            .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
+                .setPersistenceEnabled(true)
+                .setName("dflt-plc"));
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        cleanPersistenceDir();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/CpTriggeredWalDeltaConsistencyTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/CpTriggeredWalDeltaConsistencyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/CpTriggeredWalDeltaConsistencyTest.java
new file mode 100644
index 0000000..781111c
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/CpTriggeredWalDeltaConsistencyTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.wal;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.internal.IgniteEx;
+
+/**
+ * Checkpoint triggered WAL delta records consistency test.
+ */
+public class CpTriggeredWalDeltaConsistencyTest extends AbstractWalDeltaConsistencyTest {
+    /** {@inheritDoc} */
+    @Override protected boolean checkPagesOnCheckpoint() {
+        return true;
+    }
+
+    /**
+     *
+     */
+    public final void testPutRemoveCacheDestroy() throws Exception {
+        IgniteEx ignite = startGrid(0);
+
+        ignite.cluster().active(true);
+
+        IgniteCache<Integer, Object> cache0 = ignite.getOrCreateCache("cache0");
+
+        for (int i = 0; i < 3_000; i++)
+            cache0.put(i, "Cache value " + i);
+
+        for (int i = 2_000; i < 5_000; i++)
+            cache0.put(i, "Changed cache value " + i);
+
+        for (int i = 1_000; i < 4_000; i++)
+            cache0.remove(i);
+
+        for (int i = 5; i >= 0; i--) {
+            IgniteCache<Integer, Object> cache1 = ignite.getOrCreateCache("cache1");
+
+            for (int j = 0; j < 300; j++)
+                cache1.put(j + i * 100, "Cache value " + j);
+
+            if (i != 0)
+                ignite.destroyCache("cache1");
+        }
+
+        forceCheckpoint();
+
+        stopAllGrids();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/ExplicitWalDeltaConsistencyTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/ExplicitWalDeltaConsistencyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/ExplicitWalDeltaConsistencyTest.java
new file mode 100644
index 0000000..1b9a18a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/ExplicitWalDeltaConsistencyTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.wal;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.persistence.wal.memtracker.PageMemoryTrackerPluginProvider;
+
+/**
+ * WAL delta records consistency test with explicit checks.
+ */
+public class ExplicitWalDeltaConsistencyTest extends AbstractWalDeltaConsistencyTest {
+    /**
+     *
+     */
+    public final void testPutRemoveAfterCheckpoint() throws Exception {
+        IgniteEx ignite = startGrid(0);
+
+        ignite.cluster().active(true);
+
+        IgniteCache<Integer, Object> cache = ignite.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+        for (int i = 0; i < 5_000; i++)
+            cache.put(i, "Cache value " + i);
+
+        for (int i = 1_000; i < 2_000; i++)
+            cache.put(i, i);
+
+        for (int i = 500; i < 1_500; i++)
+            cache.remove(i);
+
+        assertTrue(PageMemoryTrackerPluginProvider.tracker(ignite).checkPages(true));
+
+        forceCheckpoint();
+
+        for (int i = 3_000; i < 10_000; i++)
+            cache.put(i, "Changed cache value " + i);
+
+        for (int i = 4_000; i < 7_000; i++)
+            cache.remove(i);
+
+        assertTrue(PageMemoryTrackerPluginProvider.tracker(ignite).checkPages(true));
+
+        stopAllGrids();
+    }
+
+    /**
+     *
+     */
+    public final void testNotEmptyPds() throws Exception {
+        IgniteEx ignite = startGrid(0);
+
+        ignite.cluster().active(true);
+
+        IgniteCache<Integer, Object> cache = ignite.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+        for (int i = 0; i < 3_000; i++)
+            cache.put(i, "Cache value " + i);
+
+        stopGrid(0);
+
+        ignite = startGrid(0);
+
+        ignite.cluster().active(true);
+
+        cache = ignite.getOrCreateCache(DEFAULT_CACHE_NAME);
+
+        for (int i = 2_000; i < 5_000; i++)
+            cache.put(i, "Changed cache value " + i);
+
+        for (int i = 1_000; i < 4_000; i++)
+            cache.remove(i);
+
+        assertTrue(PageMemoryTrackerPluginProvider.tracker(ignite).checkPages(true));
+
+        stopAllGrids();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/SysPropWalDeltaConsistencyTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/SysPropWalDeltaConsistencyTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/SysPropWalDeltaConsistencyTest.java
new file mode 100644
index 0000000..db9f104
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/SysPropWalDeltaConsistencyTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.wal;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.processors.cache.persistence.wal.memtracker.PageMemoryTrackerPluginProvider;
+
+/**
+ * WAL delta records consistency test enabled by system property.
+ */
+public class SysPropWalDeltaConsistencyTest extends AbstractWalDeltaConsistencyTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        System.setProperty(PageMemoryTrackerPluginProvider.IGNITE_ENABLE_PAGE_MEMORY_TRACKER, "true");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        super.afterTestsStopped();
+
+        System.clearProperty(PageMemoryTrackerPluginProvider.IGNITE_ENABLE_PAGE_MEMORY_TRACKER);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        cfg.setPluginConfigurations();
+
+        return cfg;
+    }
+
+    /**
+     *
+     */
+    public final void testPutRemoveMultinode() throws Exception {
+        IgniteEx ignite0 = startGrid(0);
+
+        ignite0.cluster().active(true);
+
+        IgniteCache<Integer, Object> cache0 = ignite0.getOrCreateCache("cache0");
+
+        for (int i = 0; i < 3_000; i++)
+            cache0.put(i, "Cache value " + i);
+
+        IgniteEx ignite1 = startGrid(1);
+
+        for (int i = 2_000; i < 5_000; i++)
+            cache0.put(i, "Changed cache value " + i);
+
+        for (int i = 1_000; i < 4_000; i++)
+            cache0.remove(i);
+
+        IgniteCache<Integer, Object> cache1 = ignite1.getOrCreateCache("cache1");
+
+        for (int i = 0; i < 1_000; i++)
+            cache1.put(i, "Cache value " + i);
+
+        forceCheckpoint();
+
+        stopAllGrids();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTracker.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTracker.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTracker.java
new file mode 100644
index 0000000..6649993
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTracker.java
@@ -0,0 +1,806 @@
+/*
+ * 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.wal.memtracker;
+
+import java.nio.ByteBuffer;
+import java.util.BitSet;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.configuration.WALMode;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.mem.DirectMemoryProvider;
+import org.apache.ignite.internal.mem.DirectMemoryRegion;
+import org.apache.ignite.internal.mem.unsafe.UnsafeMemoryProvider;
+import org.apache.ignite.internal.pagemem.FullPageId;
+import org.apache.ignite.internal.pagemem.PageIdUtils;
+import org.apache.ignite.internal.pagemem.PageMemory;
+import org.apache.ignite.internal.pagemem.PageUtils;
+import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.pagemem.wal.WALPointer;
+import org.apache.ignite.internal.pagemem.wal.record.PageSnapshot;
+import org.apache.ignite.internal.pagemem.wal.record.WALRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.InitNewPageRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.PageDeltaRecord;
+import org.apache.ignite.internal.pagemem.wal.record.delta.RecycleRecord;
+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.persistence.DataRegion;
+import org.apache.ignite.internal.processors.cache.persistence.DbCheckpointListener;
+import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
+import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
+import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryEx;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImpl;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FileWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.persistence.wal.FsyncModeFileWriteAheadLogManager;
+import org.apache.ignite.internal.util.GridUnsafe;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgnitePredicate;
+import org.apache.ignite.plugin.IgnitePlugin;
+import org.apache.ignite.plugin.PluginContext;
+import org.mockito.Mockito;
+
+/**
+ * Page memory tracker.
+ *
+ * Replicates Ignite's page memory changes to own managed memory region by intercepting WAL records and
+ * applying page snapshots and deltas.
+ */
+public class PageMemoryTracker implements IgnitePlugin {
+    /** Plugin context. */
+    private final PluginContext ctx;
+
+    /** Config. */
+    private final PageMemoryTrackerConfiguration cfg;
+
+    /** Logger. */
+    private final IgniteLogger log;
+
+    /** Grid context. */
+    private final GridKernalContext gridCtx;
+
+    /** Page allocator mutex. */
+    private final Object pageAllocatorMux = new Object();
+
+    /** Pages. */
+    private final Map<FullPageId, DirectMemoryPage> pages = new ConcurrentHashMap<>();
+
+    /** Page slots. */
+    private volatile DirectMemoryPageSlot[] pageSlots;
+
+    /** Free page slots. */
+    private final BitSet freeSlots = new BitSet();
+
+    /** Last allocated page index. */
+    private volatile int lastPageIdx;
+
+    /** Free page slots count. */
+    private volatile int freeSlotsCnt;
+
+    /** Page size. */
+    private volatile int pageSize;
+
+    /** Page memory mock. */
+    private volatile PageMemory pageMemoryMock;
+
+    /** Memory provider. */
+    private volatile DirectMemoryProvider memoryProvider;
+
+    /** Memory region. */
+    private volatile DirectMemoryRegion memoryRegion;
+
+    /** Max pages. */
+    private volatile int maxPages;
+
+    /** Tracking started. */
+    private volatile boolean started;
+
+    /** Tracker was started with empty PDS. */
+    private volatile boolean emptyPds;
+
+    /** Statistics. */
+    private final ConcurrentMap<WALRecord.RecordType, AtomicInteger> stats = new ConcurrentHashMap<>();
+
+    /** Checkpoint listener. */
+    private DbCheckpointListener checkpointLsnr;
+
+    /**
+     * @param ctx Plugin context.
+     * @param cfg Configuration.
+     */
+    PageMemoryTracker(PluginContext ctx, PageMemoryTrackerConfiguration cfg) {
+        this.ctx = ctx;
+        this.cfg = cfg;
+        log = ctx.log(getClass());
+        gridCtx = ((IgniteEx)ctx.grid()).context();
+    }
+
+    /**
+     * Creates WAL manager.
+     */
+    IgniteWriteAheadLogManager createWalManager() {
+        if (isEnabled()) {
+            if (ctx.igniteConfiguration().getDataStorageConfiguration().getWalMode() == WALMode.FSYNC) {
+                return new FsyncModeFileWriteAheadLogManager(gridCtx) {
+                    @Override public WALPointer log(WALRecord record) throws IgniteCheckedException {
+                        WALPointer res = super.log(record);
+
+                        applyWalRecord(record);
+
+                        return res;
+                    }
+
+                    @Override public void resumeLogging(WALPointer lastPtr) throws IgniteCheckedException {
+                        super.resumeLogging(lastPtr);
+
+                        emptyPds = (lastPtr == null);
+                    }
+                };
+            }
+            else {
+                return new FileWriteAheadLogManager(gridCtx) {
+                    @Override public WALPointer log(WALRecord record) throws IgniteCheckedException {
+                        WALPointer res = super.log(record);
+
+                        applyWalRecord(record);
+
+                        return res;
+                    }
+
+                    @Override public void resumeLogging(WALPointer lastPtr) throws IgniteCheckedException {
+                        super.resumeLogging(lastPtr);
+
+                        emptyPds = (lastPtr == null);
+                    }
+                };
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates page store manager.
+     */
+    IgnitePageStoreManager createPageStoreManager() {
+        if (isEnabled()) {
+            return new FilePageStoreManager(gridCtx) {
+                @Override public void shutdownForCacheGroup(CacheGroupContext grp,
+                    boolean destroy) throws IgniteCheckedException {
+                    super.shutdownForCacheGroup(grp, destroy);
+
+                    cleanupPages(fullPageId -> fullPageId.groupId() == grp.groupId());
+                }
+
+                @Override public void onPartitionDestroyed(int grpId, int partId, int tag) throws IgniteCheckedException {
+                    super.onPartitionDestroyed(grpId, partId, tag);
+
+                    cleanupPages(fullPageId -> fullPageId.groupId() == grpId
+                        && PageIdUtils.partId(fullPageId.pageId()) == partId);
+                }
+            };
+        }
+
+        return null;
+    }
+
+    /**
+     * Start tracking pages.
+     */
+    synchronized void start() {
+        if (!isEnabled() || started)
+            return;
+
+        pageSize = ctx.igniteConfiguration().getDataStorageConfiguration().getPageSize();
+
+        pageMemoryMock = Mockito.mock(PageMemory.class);
+
+        Mockito.doReturn(pageSize).when(pageMemoryMock).pageSize();
+
+        GridCacheSharedContext sharedCtx = gridCtx.cache().context();
+
+        // Initialize one memory region for all data regions of target ignite node.
+        long maxMemorySize = 0;
+
+        for (DataRegion dataRegion : sharedCtx.database().dataRegions()) {
+            if (dataRegion.pageMemory() instanceof PageMemoryImpl)
+                maxMemorySize += dataRegion.config().getMaxSize();
+        }
+
+        long[] chunks = new long[] {maxMemorySize};
+
+        memoryProvider = new UnsafeMemoryProvider(log);
+
+        memoryProvider.initialize(chunks);
+
+        memoryRegion = memoryProvider.nextRegion();
+
+        maxPages = (int)(maxMemorySize / pageSize);
+
+        pageSlots = new DirectMemoryPageSlot[maxPages];
+
+        freeSlotsCnt = maxPages;
+
+        if (cfg.isCheckPagesOnCheckpoint()) {
+            checkpointLsnr = ctx -> {
+                if (!checkPages(false))
+                    throw new IgniteCheckedException("Page memory is inconsistent after applying WAL delta records.");
+            };
+
+            ((GridCacheDatabaseSharedManager)gridCtx.cache().context().database()).addCheckpointListener(checkpointLsnr);
+        }
+
+        lastPageIdx = 0;
+
+        started = true;
+
+        log.info("PageMemory tracker started, " + U.readableSize(maxMemorySize, false) + " offheap memory allocated.");
+    }
+
+    /**
+     * Stop tracking, release resources.
+     */
+    synchronized void stop() {
+        if (!started)
+            return;
+
+        started = false;
+
+        pages.clear();
+
+        pageSlots = null;
+
+        freeSlots.clear();
+
+        stats.clear();
+
+        memoryProvider.shutdown();
+
+        if (checkpointLsnr != null) {
+            ((GridCacheDatabaseSharedManager)gridCtx.cache().context().database())
+                .removeCheckpointListener(checkpointLsnr);
+
+            checkpointLsnr = null;
+        }
+
+        log.info("PageMemory tracker stopped.");
+    }
+
+    /**
+     * Is plugin enabled.
+     */
+    private boolean isEnabled() {
+        return (cfg != null && cfg.isEnabled() && CU.isPersistenceEnabled(ctx.igniteConfiguration()));
+    }
+
+
+    /**
+     * Cleanup pages by predicate.
+     *
+     * @param pred Predicate.
+     */
+    private void cleanupPages(IgnitePredicate<FullPageId> pred) {
+        synchronized (pageAllocatorMux) {
+            for (Map.Entry<FullPageId, DirectMemoryPage> pageEntry : pages.entrySet()) {
+                if (pred.apply(pageEntry.getKey())) {
+                    pages.remove(pageEntry.getKey());
+
+                    freeSlots.set(pageEntry.getValue().slot().index());
+
+                    freeSlotsCnt++;
+                }
+            }
+        }
+    }
+
+    /**
+     * Allocates new page for given FullPageId.
+     *
+     * @param fullPageId Full page id.
+     */
+    private DirectMemoryPage allocatePage(FullPageId fullPageId) throws IgniteCheckedException {
+        synchronized (pageAllocatorMux) {
+            // Double check.
+            DirectMemoryPage page = pages.get(fullPageId);
+
+            if (page != null)
+                return page;
+
+            if (freeSlotsCnt == 0)
+                throw new IgniteCheckedException("Can't allocate new page");
+
+            int pageIdx;
+
+            if (lastPageIdx < maxPages)
+                pageIdx = lastPageIdx++;
+            else {
+                pageIdx = freeSlots.nextSetBit(0);
+
+                assert pageIdx >= 0;
+
+                freeSlots.clear(pageIdx);
+            }
+
+            freeSlotsCnt--;
+
+            long pageAddr = memoryRegion.address() + ((long)pageIdx) * pageSize;
+
+            DirectMemoryPageSlot pageSlot = pageSlots[pageIdx];
+            if (pageSlot == null)
+                pageSlot = pageSlots[pageIdx] = new DirectMemoryPageSlot(pageAddr, pageIdx);
+
+            pageSlot.lock();
+
+            try {
+                page = new DirectMemoryPage(pageSlot);
+
+                page.fullPageId(fullPageId);
+
+                pages.put(fullPageId, page);
+
+                if (pageSlot.owningPage() != null) {
+                    // Clear memory if slot was already used.
+                    ByteBuffer pageBuf = GridUnsafe.wrapPointer(pageAddr, pageSize);
+
+                    pageBuf.put(new byte[pageSize]);
+                }
+
+                pageSlot.owningPage(page);
+            }
+            finally {
+                pageSlot.unlock();
+            }
+
+            return page;
+        }
+    }
+
+    /**
+     * Gets or allocates page for given FullPageId.
+     *
+     * @param fullPageId Full page id.
+     * @return Page.
+     */
+    private DirectMemoryPage page(FullPageId fullPageId) throws IgniteCheckedException {
+        DirectMemoryPage page = pages.get(fullPageId);
+
+        if (page == null)
+            page = allocatePage(fullPageId);
+
+        return page;
+    }
+
+    /**
+     * Apply WAL record to local memory region.
+     */
+    private void applyWalRecord(WALRecord record) throws IgniteCheckedException {
+        if (!started)
+            return;
+
+        if (record instanceof PageSnapshot) {
+            PageSnapshot snapshot = (PageSnapshot)record;
+
+            int grpId = snapshot.fullPageId().groupId();
+            long pageId = snapshot.fullPageId().pageId();
+
+            FullPageId fullPageId = new FullPageId(pageId, grpId);
+
+            DirectMemoryPage page = page(fullPageId);
+
+            page.lock();
+
+            try {
+                PageUtils.putBytes(page.address(), 0, snapshot.pageData());
+
+                page.fullPageId(fullPageId);
+
+                page.changeHistory().clear();
+
+                page.changeHistory().add(record);
+            }
+            finally {
+                page.unlock();
+            }
+        }
+        else if (record instanceof PageDeltaRecord) {
+            PageDeltaRecord deltaRecord = (PageDeltaRecord)record;
+
+            int grpId = deltaRecord.groupId();
+            long pageId = deltaRecord.pageId();
+
+            FullPageId fullPageId = new FullPageId(pageId, grpId);
+
+            DirectMemoryPage page = page(fullPageId);
+
+            page.lock();
+
+            try {
+                deltaRecord.applyDelta(pageMemoryMock, page.address());
+
+                // Set new fullPageId after recycle or after new page init, because pageId tag is changed.
+                if (record instanceof RecycleRecord)
+                    page.fullPageId(new FullPageId(((RecycleRecord)record).newPageId(), grpId));
+                else if (record instanceof InitNewPageRecord)
+                    page.fullPageId(new FullPageId(((InitNewPageRecord)record).newPageId(), grpId));
+
+                page.changeHistory().add(record);
+            }
+            finally {
+                page.unlock();
+            }
+        }
+        else
+            return;
+
+        // Increment statistics.
+        AtomicInteger statCnt = stats.get(record.type());
+
+        if (statCnt == null) {
+            statCnt = new AtomicInteger();
+
+            AtomicInteger oldCnt = stats.putIfAbsent(record.type(), statCnt);
+
+            if (oldCnt != null)
+                statCnt = oldCnt;
+        }
+
+        statCnt.incrementAndGet();
+    }
+
+    /**
+     * Total count of allocated pages in page store.
+     */
+    private long pageStoreAllocatedPages() {
+        IgnitePageStoreManager pageStoreMgr = gridCtx.cache().context().pageStore();
+
+        assert pageStoreMgr != null;
+
+        long totalAllocated = pageStoreMgr.pagesAllocated(MetaStorage.METASTORAGE_CACHE_ID);
+
+        for (CacheGroupContext ctx : gridCtx.cache().cacheGroups())
+            totalAllocated += pageStoreMgr.pagesAllocated(ctx.groupId());
+
+        return totalAllocated;
+    }
+
+    /**
+     * Checks if there are any differences between the Ignite's data regions content and pages inside the tracker.
+     *
+     * @param checkAll Check all tracked pages, otherwise check until first error.
+     * @return {@code true} if content of all tracked pages equals to content of these pages in the ignite instance.
+     */
+    public boolean checkPages(boolean checkAll) throws IgniteCheckedException {
+        if (!started)
+            throw new IgniteCheckedException("Page memory checking only possible when tracker is started.");
+
+        GridCacheProcessor cacheProc = gridCtx.cache();
+
+        boolean res = true;
+
+        synchronized (pageAllocatorMux) {
+            long totalAllocated = pageStoreAllocatedPages();
+
+            long metaId = ((PageMemoryEx)cacheProc.context().database().metaStorage().pageMemory()).metaPageId(
+                MetaStorage.METASTORAGE_CACHE_ID);
+
+            // Meta storage meta page is counted as allocated, but never used in current implementation.
+            // This behavior will be fixed by https://issues.apache.org/jira/browse/IGNITE-8735
+            if (!pages.containsKey(new FullPageId(metaId, MetaStorage.METASTORAGE_CACHE_ID))
+                && pages.containsKey(new FullPageId(metaId + 1, MetaStorage.METASTORAGE_CACHE_ID)))
+                totalAllocated--;
+
+            log.info(">>> Total tracked pages: " + pages.size());
+            log.info(">>> Total allocated pages: " + totalAllocated);
+
+            dumpStats();
+
+            if (emptyPds && pages.size() != totalAllocated) {
+                res = false;
+
+                log.error("Started from empty PDS, but tracked pages count not equals to allocated pages count");
+
+                if (!checkAll)
+                    return false;
+            }
+        }
+
+        Set<Integer> groupsWarned = new HashSet<>();
+
+        for (DirectMemoryPage page : pages.values()) {
+            FullPageId fullPageId = page.fullPageId();
+
+            PageMemory pageMem;
+
+            if (fullPageId.groupId() == MetaStorage.METASTORAGE_CACHE_ID)
+                pageMem = cacheProc.context().database().metaStorage().pageMemory();
+            else {
+                CacheGroupContext ctx = cacheProc.cacheGroup(fullPageId.groupId());
+
+                if (ctx != null)
+                    pageMem = ctx.dataRegion().pageMemory();
+                else {
+                    if (!groupsWarned.contains(fullPageId.groupId())) {
+                        log.warning("Cache group " + fullPageId.groupId() + " not found.");
+
+                        groupsWarned.add(fullPageId.groupId());
+                    }
+
+                    continue;
+                }
+            }
+
+            assert pageMem instanceof PageMemoryImpl;
+
+            long rmtPage = pageMem.acquirePage(fullPageId.groupId(), fullPageId.pageId());
+
+            try {
+                long rmtPageAddr = pageMem.readLock(fullPageId.groupId(), fullPageId.pageId(), rmtPage);
+
+                try {
+                    page.lock();
+
+                    try {
+                        if (rmtPageAddr == 0L) {
+                            res = false;
+
+                            log.error("Can't lock page: " + fullPageId);
+
+                            dumpHistory(page);
+                        }
+                        else {
+                            ByteBuffer locBuf = GridUnsafe.wrapPointer(page.address(), pageSize);
+                            ByteBuffer rmtBuf = GridUnsafe.wrapPointer(rmtPageAddr, pageSize);
+
+                            if (!locBuf.equals(rmtBuf)) {
+                                res = false;
+
+                                log.error("Page buffers are not equals: " + fullPageId);
+
+                                dumpDiff(locBuf, rmtBuf);
+
+                                dumpHistory(page);
+                            }
+                        }
+
+                        if (!res && !checkAll)
+                            return false;
+                    }
+                    finally {
+                        page.unlock();
+                    }
+                }
+                finally {
+                    if (rmtPageAddr != 0L)
+                        pageMem.readUnlock(fullPageId.groupId(), fullPageId.pageId(), rmtPage);
+                }
+            }
+            finally {
+                pageMem.releasePage(fullPageId.groupId(), fullPageId.pageId(), rmtPage);
+            }
+        }
+
+        return res;
+    }
+
+    /**
+     * Dump statistics to log.
+     */
+    private void dumpStats() {
+        log.info(">>> Processed WAL records:");
+
+        for (Map.Entry<WALRecord.RecordType, AtomicInteger> entry : stats.entrySet())
+            log.info("        " + entry.getKey() + '=' + entry.getValue().get());
+    }
+
+    /**
+     * Dump difference between two ByteBuffers to log.
+     *
+     * @param buf1 Buffer 1.
+     * @param buf2 Buffer 2.
+     */
+    private void dumpDiff(ByteBuffer buf1, ByteBuffer buf2) {
+        log.error(">>> Diff:");
+
+        for (int i = 0; i < Math.min(buf1.remaining(), buf2.remaining()); i++) {
+            byte b1 = buf1.get(buf1.position() + i);
+            byte b2 = buf2.get(buf2.position() + i);
+
+            if (b1 != b2)
+                log.error(String.format("        0x%04X: %02X %02X", i, b1, b2));
+        }
+
+        if (buf1.remaining() < buf2.remaining()) {
+            for (int i = buf1.remaining(); i < buf2.remaining(); i++)
+                log.error(String.format("        0x%04X:    %02X", i, buf2.get(buf2.position() + i)));
+        }
+        else if (buf1.remaining() > buf2.remaining()) {
+            for (int i = buf2.remaining(); i < buf1.remaining(); i++)
+                log.error(String.format("        0x%04X: %02X", i, buf1.get(buf1.position() + i)));
+        }
+    }
+
+    /**
+     * Dump page change history to log.
+     *
+     * @param page Page.
+     */
+    private void dumpHistory(DirectMemoryPage page) {
+        log.error(">>> Change history:");
+
+        for (WALRecord record : page.changeHistory())
+            log.error("        " + record);
+    }
+
+    /**
+     *
+     */
+    private static class DirectMemoryPage {
+        /** Page slot. */
+        private final DirectMemoryPageSlot slot;
+
+        /** Change history. */
+        private final List<WALRecord> changeHist = new LinkedList<>();
+
+        /** Full page id. */
+        private volatile FullPageId fullPageId;
+
+        /**
+         * @param slot Memory page slot.
+         */
+        private DirectMemoryPage(DirectMemoryPageSlot slot) {
+            this.slot = slot;
+        }
+
+        /**
+         * Lock page.
+         */
+        public void lock() throws IgniteCheckedException {
+            slot.lock();
+
+            if (slot.owningPage() != this) {
+                slot.unlock();
+
+                throw new IgniteCheckedException("Memory slot owning page changed, can't access page buffer.");
+            }
+        }
+
+        /**
+         * Unlock page.
+         */
+        public void unlock() {
+            slot.unlock();
+        }
+
+        /**
+         * @return Page address.
+         */
+        public long address() {
+            return slot.address();
+        }
+
+        /**
+         * Change history.
+         */
+        public List<WALRecord> changeHistory() {
+            return changeHist;
+        }
+
+        /**
+         * @return Full page id.
+         */
+        public FullPageId fullPageId() {
+            return fullPageId;
+        }
+
+        /**
+         * @param fullPageId Full page id.
+         */
+        public void fullPageId(FullPageId fullPageId) {
+            this.fullPageId = fullPageId;
+        }
+
+        /**
+         * @return Memory page slot.
+         */
+        public DirectMemoryPageSlot slot() {
+            return slot;
+        }
+    }
+
+    /**
+     *
+     */
+    private static class DirectMemoryPageSlot {
+        /** Page slot address. */
+        private final long addr;
+
+        /** Page slot index. */
+        private final int idx;
+
+        /** Page lock. */
+        private final Lock lock = new ReentrantLock();
+
+        /** Owning page. */
+        private DirectMemoryPage owningPage;
+
+        /**
+         * @param addr Page address.
+         * @param idx Page slot index
+         */
+        private DirectMemoryPageSlot(long addr, int idx) {
+            this.addr = addr;
+            this.idx = idx;
+        }
+
+        /**
+         * Lock page slot.
+         */
+        public void lock() {
+            lock.lock();
+        }
+
+        /**
+         * Unlock page slot.
+         */
+        public void unlock() {
+            lock.unlock();
+        }
+
+        /**
+         * @return Page slot address.
+         */
+        public long address() {
+            return addr;
+        }
+
+        /**
+         * @return Page slot index.
+         */
+        public int index() {
+            return idx;
+        }
+
+        /**
+         * @return Owning page.
+         */
+        public DirectMemoryPage owningPage() {
+            return owningPage;
+        }
+
+        /**
+         * @param owningPage Owning page.
+         */
+        public void owningPage(DirectMemoryPage owningPage) {
+            this.owningPage = owningPage;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerConfiguration.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerConfiguration.java
new file mode 100644
index 0000000..5369d52
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerConfiguration.java
@@ -0,0 +1,69 @@
+/*
+ * 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.wal.memtracker;
+
+import org.apache.ignite.plugin.PluginConfiguration;
+
+/**
+ * PageMemory tracker plugin configuration.
+ */
+public class PageMemoryTrackerConfiguration implements PluginConfiguration {
+    /** Plugin enabled. */
+    private boolean enabled;
+
+    /** Perform page memory check on each checkpoint. */
+    private boolean checkPagesOnCheckpoint;
+
+    /**
+     * Gets enabled flag.
+     */
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Sets enabled flag.
+     *
+     * @param enabled Enabled.
+     * @return {@code this} for chaining.
+     */
+    public PageMemoryTrackerConfiguration setEnabled(boolean enabled) {
+        this.enabled = enabled;
+
+        return this;
+    }
+
+    /**
+     * Perform page memory check on each checkpoint.
+     */
+    public boolean isCheckPagesOnCheckpoint() {
+        return checkPagesOnCheckpoint;
+    }
+
+    /**
+     * Perform page memory check on each checkpoint.
+     *
+     * @param checkPagesOnCheckpoint Check on checkpoint.
+     * @return {@code this} for chaining.
+     */
+    public PageMemoryTrackerConfiguration setCheckPagesOnCheckpoint(boolean checkPagesOnCheckpoint) {
+        this.checkPagesOnCheckpoint = checkPagesOnCheckpoint;
+
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerPluginProvider.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerPluginProvider.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerPluginProvider.java
new file mode 100644
index 0000000..c5f83b5
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/wal/memtracker/PageMemoryTrackerPluginProvider.java
@@ -0,0 +1,200 @@
+/*
+ * 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.wal.memtracker;
+
+import java.io.Serializable;
+import java.util.UUID;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.pagemem.store.IgnitePageStoreManager;
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cluster.IgniteChangeGlobalStateSupport;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.plugin.CachePluginContext;
+import org.apache.ignite.plugin.CachePluginProvider;
+import org.apache.ignite.plugin.ExtensionRegistry;
+import org.apache.ignite.plugin.IgnitePlugin;
+import org.apache.ignite.plugin.PluginConfiguration;
+import org.apache.ignite.plugin.PluginContext;
+import org.apache.ignite.plugin.PluginNotFoundException;
+import org.apache.ignite.plugin.PluginProvider;
+import org.apache.ignite.plugin.PluginValidationException;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * PageMemory tracker plugin provider.
+ */
+public class PageMemoryTrackerPluginProvider implements PluginProvider<PageMemoryTrackerConfiguration>,
+    IgniteChangeGlobalStateSupport {
+    /** System property name to implicitly enable page memory tracker . */
+    public static final String IGNITE_ENABLE_PAGE_MEMORY_TRACKER = "IGNITE_ENABLE_PAGE_MEMORY_TRACKER";
+
+    /** Plugin name. */
+    private static final String PLUGIN_NAME = "PageMemory tracker plugin";
+
+    /** Plugin instance */
+    private PageMemoryTracker plugin;
+
+    /** Logger. */
+    private IgniteLogger log;
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return PLUGIN_NAME;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String version() {
+        return "1.0";
+    }
+
+    /** {@inheritDoc} */
+    @Override public String copyright() {
+        return "";
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public <T extends IgnitePlugin> T plugin() {
+        return (T)plugin;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void initExtensions(PluginContext ctx, ExtensionRegistry registry) {
+        IgniteConfiguration igniteCfg = ctx.igniteConfiguration();
+
+        log = ctx.log(getClass());
+
+        if (igniteCfg.getPluginConfigurations() != null) {
+            for (PluginConfiguration pluginCfg : igniteCfg.getPluginConfigurations()) {
+                if (pluginCfg instanceof PageMemoryTrackerConfiguration) {
+                    PageMemoryTrackerConfiguration cfg = (PageMemoryTrackerConfiguration)pluginCfg;
+
+                    plugin = new PageMemoryTracker(ctx, cfg);
+
+                    if (cfg.isEnabled() && !CU.isPersistenceEnabled(igniteCfg)) {
+                        log.warning("Page memory tracker plugin enabled, " +
+                            "but there are no persistable data regions in configuration. Tracker will be disabled.");
+                    }
+
+                    return;
+                }
+            }
+        }
+
+        if (Boolean.getBoolean(IGNITE_ENABLE_PAGE_MEMORY_TRACKER) && CU.isPersistenceEnabled(igniteCfg)) {
+            plugin = new PageMemoryTracker(ctx, new PageMemoryTrackerConfiguration()
+                .setEnabled(true)
+                .setCheckPagesOnCheckpoint(true)
+            );
+
+            log.info("PageMemory tracking enabled by system property.");
+        }
+        else
+            plugin = new PageMemoryTracker(ctx, null);
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Nullable @Override public <T> T createComponent(PluginContext ctx, Class<T> cls) {
+        if (plugin != null) {
+            if (IgniteWriteAheadLogManager.class.equals(cls))
+                return (T)plugin.createWalManager();
+            else if (IgnitePageStoreManager.class.equals(cls))
+                return (T)plugin.createPageStoreManager();
+        }
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public CachePluginProvider createCacheProvider(CachePluginContext ctx) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start(PluginContext ctx) {
+        // No-op
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop(boolean cancel) {
+        // No-op
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onIgniteStart() {
+        // No-op
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onIgniteStop(boolean cancel) {
+        if (plugin != null)
+            plugin.stop();
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Serializable provideDiscoveryData(UUID nodeId) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void receiveDiscoveryData(UUID nodeId, Serializable data) {
+        // No-op
+    }
+
+    /** {@inheritDoc} */
+    @Override public void validateNewNode(ClusterNode node) throws PluginValidationException {
+        // No-op
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onActivate(GridKernalContext kctx) {
+        if (plugin != null) {
+            try {
+                plugin.start();
+            }
+            catch (Exception e) {
+                log.error("Can't start plugin", e);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onDeActivate(GridKernalContext kctx) {
+        if (plugin != null)
+            plugin.stop();
+    }
+
+    /**
+     * Gets PageMemory tracker for ignite instance or null if it's not enabled.
+     *
+     * @param ignite Ignite.
+     */
+    public static PageMemoryTracker tracker(Ignite ignite) {
+        try {
+            return ignite.plugin(PLUGIN_NAME);
+        }
+        catch (PluginNotFoundException ignore) {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/34059d0c/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
index 7294a60..39c4a2b 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite.java
@@ -39,7 +39,10 @@ import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemor
 import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryImplTest;
 import org.apache.ignite.internal.processors.cache.persistence.pagemem.PageMemoryNoStoreLeakTest;
 import org.apache.ignite.internal.processors.cache.persistence.pagemem.PagesWriteThrottleSmokeTest;
+import org.apache.ignite.internal.processors.cache.persistence.wal.CpTriggeredWalDeltaConsistencyTest;
+import org.apache.ignite.internal.processors.cache.persistence.wal.ExplicitWalDeltaConsistencyTest;
 import org.apache.ignite.internal.processors.cache.persistence.wal.SegmentedRingByteBufferTest;
+import org.apache.ignite.internal.processors.cache.persistence.wal.SysPropWalDeltaConsistencyTest;
 import org.apache.ignite.internal.processors.database.IgniteDbDynamicCacheSelfTest;
 import org.apache.ignite.internal.processors.database.IgniteDbMultiNodePutGetTest;
 import org.apache.ignite.internal.processors.database.IgniteDbPutGetWithCacheStoreTest;
@@ -78,6 +81,11 @@ public class IgnitePdsTestSuite extends TestSuite {
         // Metrics
         suite.addTestSuite(FillFactorMetricTest.class);
 
+        // WAL delta consistency
+        suite.addTestSuite(CpTriggeredWalDeltaConsistencyTest.class);
+        suite.addTestSuite(ExplicitWalDeltaConsistencyTest.class);
+        suite.addTestSuite(SysPropWalDeltaConsistencyTest.class);
+
         return suite;
     }
 


Mime
View raw message