You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2017/06/07 09:30:13 UTC

[2/2] ignite git commit: IGNITE-5375 - Persistence metrics introduced

IGNITE-5375 - Persistence metrics introduced


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

Branch: refs/heads/ignite-5267
Commit: fffad68f6c23c36cf28e61bd7465082d71569665
Parents: a2e1730
Author: Alexey Goncharuk <al...@gmail.com>
Authored: Wed Jun 7 12:28:29 2017 +0300
Committer: Alexey Goncharuk <al...@gmail.com>
Committed: Wed Jun 7 12:28:29 2017 +0300

----------------------------------------------------------------------
 .../src/main/java/org/apache/ignite/Ignite.java |   6 +
 .../java/org/apache/ignite/MemoryMetrics.java   |  29 +-
 .../org/apache/ignite/PersistenceMetrics.java   | 114 +++++++
 .../PersistentStoreConfiguration.java           |  95 +++++-
 .../apache/ignite/internal/IgniteKernal.java    |  13 +
 .../pagemem/wal/IgniteWriteAheadLogManager.java |   5 +
 .../IgniteCacheDatabaseSharedManager.java       |  10 +
 .../cache/database/MemoryMetricsImpl.java       | 203 ++++++-------
 .../cache/database/MemoryMetricsMXBeanImpl.java |  15 +
 .../cache/database/MemoryMetricsSnapshot.java   |  27 ++
 .../database/PersistenceMetricsSnapshot.java    | 144 +++++++++
 .../cache/database/tree/io/PageIO.java          |   8 +
 .../ignite/mxbean/MemoryMetricsMXBean.java      |  12 +
 .../ignite/mxbean/PersistenceMetricsMXBean.java | 121 ++++++++
 .../database/MemoryMetricsSelfTest.java         |  10 +-
 .../processors/igfs/IgfsIgniteMock.java         |   8 +
 .../ignite/testframework/junits/IgniteMock.java |   6 +
 .../junits/multijvm/IgniteProcessProxy.java     |   6 +
 .../GridCacheDatabaseSharedManager.java         | 189 ++++++++----
 .../cache/database/PersistenceMetricsImpl.java  | 297 +++++++++++++++++++
 .../pagemem/CheckpointMetricsTracker.java       | 183 ++++++++++++
 .../cache/database/pagemem/PageMemoryEx.java    |   3 +-
 .../cache/database/pagemem/PageMemoryImpl.java  |  38 ++-
 .../database/wal/FileWriteAheadLogManager.java  | 138 +++++++--
 .../IgnitePersistenceMetricsSelfTest.java       | 236 +++++++++++++++
 ...entStoreRecoveryAfterFileCorruptionTest.java |   2 +-
 .../PageStoreCheckpointSimulationSelfTest.java  |   4 +-
 ...PlusTreeReuseListPageMemoryImplSelfTest.java |   6 +-
 ...BPlusTreeSelfTestPageMemoryImplSelfTest.java |   5 +-
 .../MetadataStoragePageMemoryImplSelfTest.java  |   5 +-
 .../cache/database/pagemem/NoOpWALManager.java  |   5 +
 .../pagemem/PageMemoryImplNoLoadSelfTest.java   |   5 +-
 .../database/pagemem/PageMemoryImplTest.java    |   5 +-
 .../ignite/testsuites/IgnitePdsTestSuite2.java  |   7 +-
 .../org/apache/ignite/IgniteSpringBean.java     |   7 +
 35 files changed, 1745 insertions(+), 222 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/Ignite.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/Ignite.java b/modules/core/src/main/java/org/apache/ignite/Ignite.java
index 8d5e967..f32782a 100644
--- a/modules/core/src/main/java/org/apache/ignite/Ignite.java
+++ b/modules/core/src/main/java/org/apache/ignite/Ignite.java
@@ -638,4 +638,10 @@ public interface Ignite extends AutoCloseable {
      * @return {@link MemoryMetrics} snapshot or {@code null} if no memory region is configured under specified name.
      */
     @Nullable public MemoryMetrics memoryMetrics(String memPlcName);
+
+    /**
+     *
+     * @return {@link PersistenceMetrics} snapshot.
+     */
+    public PersistenceMetrics persistentStoreMetrics();
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/MemoryMetrics.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/MemoryMetrics.java b/modules/core/src/main/java/org/apache/ignite/MemoryMetrics.java
index 81f8309..c709777 100644
--- a/modules/core/src/main/java/org/apache/ignite/MemoryMetrics.java
+++ b/modules/core/src/main/java/org/apache/ignite/MemoryMetrics.java
@@ -55,7 +55,9 @@ public interface MemoryMetrics {
     public String getName();
 
     /**
-     * Gets a total number of allocated pages in a memory region.
+     * Gets a total number of allocated pages related to the memory policy. When persistence is disabled, this
+     * metric shows the total number of pages in memory. When persistence is enabled, this metric shows the
+     * total number of pages in memory and on disk.
      *
      * @return Total number of allocated pages.
      */
@@ -89,4 +91,29 @@ public interface MemoryMetrics {
      * @return The percentage of space that is still free and can be filled in.
      */
     public float getPagesFillFactor();
+
+    /**
+     * Gets the number of dirty pages (pages which contents is different from the current persistent storage state).
+     * This metric is enabled only for Ignite nodes with enabled persistence.
+     *
+     * @return Current number of dirty pages.
+     */
+    public long getDirtyPages();
+
+    /**
+     * Gets rate (pages per second) at which pages get replaced with other pages from persistent storage.
+     * The rate effectively represents the rate at which pages get 'evicted' in favor of newly needed pages.
+     * This metric is enabled only for Ignite nodes with enabled persistence.
+     *
+     * @return Pages per second replace rate.
+     */
+    public float getPagesReplaceRate();
+
+    /**
+     * Gets total number of pages currently loaded to the RAM. When persistence is disabled, this metric is equal
+     * to {@link #getTotalAllocatedPages()}.
+     *
+     * @return Total number of pages loaded to RAM.
+     */
+    public long getPhysicalMemoryPages();
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/PersistenceMetrics.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/PersistenceMetrics.java b/modules/core/src/main/java/org/apache/ignite/PersistenceMetrics.java
new file mode 100644
index 0000000..3f00b01
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/PersistenceMetrics.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import org.apache.ignite.configuration.PersistentStoreConfiguration;
+
+/**
+ *
+ */
+public interface PersistenceMetrics {
+    /**
+     * Gets the average number of WAL records per second written during the last time interval.
+     * <p>
+     * The length of time interval is configured via {@link PersistentStoreConfiguration#setRateTimeInterval(long)}
+     * configurartion property.
+     * The number of subintervals is configured via {@link PersistentStoreConfiguration#setSubIntervals(int)}
+     * configuration property.
+     */
+    public float getWalLoggingRate();
+
+    /**
+     * Gets the average number of bytes per second written during the last time interval.
+     * The length of time interval is configured via {@link PersistentStoreConfiguration#setRateTimeInterval(long)}
+     * configurartion property.
+     * The number of subintervals is configured via {@link PersistentStoreConfiguration#setSubIntervals(int)}
+     * configuration property.
+     */
+    public float getWalWritingRate();
+
+    /**
+     * Gets the current number of WAL segments in the WAL archive.
+     */
+    public int getWalArchiveSegments();
+
+    /**
+     * Gets the average WAL fsync duration in microseconds over the last time interval.
+     * <p>
+     * The length of time interval is configured via {@link PersistentStoreConfiguration#setRateTimeInterval(long)}
+     * configurartion property.
+     * The number of subintervals is configured via {@link PersistentStoreConfiguration#setSubIntervals(int)}
+     * configuration property.
+     */
+    public float getWalFsyncTimeAverage();
+
+    /**
+     * Gets the duration of the last checkpoint in milliseconds.
+     *
+     * @return Total checkpoint duration in milliseconds.
+     */
+    public long getLastCheckpointingDuration();
+
+    /**
+     * Gets the duration of last checkpoint lock wait in milliseconds.
+     *
+     * @return Checkpoint lock wait time in milliseconds.
+     */
+    public long getLastCheckpointLockWaitDuration();
+
+    /**
+     * Gets the duration of last checkpoint mark phase in milliseconds.
+     *
+     * @return Checkpoint mark duration in milliseconds.
+     */
+    public long getLastCheckpointMarkDuration();
+
+    /**
+     * Gets the duration of last checkpoint pages write phase in milliseconds.
+     *
+     * @return Checkpoint pages write phase in milliseconds.
+     */
+    public long getLastCheckpointPagesWriteDuration();
+
+    /**
+     * Gets the duration of the sync phase of the last checkpoint in milliseconds.
+     *
+     * @return Checkpoint fsync time in milliseconds.
+     */
+    public long getLastCheckpointFsyncDuration();
+
+    /**
+     * Gets the total number of pages written during the last checkpoint.
+     *
+     * @return Total number of pages written during the last checkpoint.
+     */
+    public long getLastCheckpointTotalPagesNumber();
+
+    /**
+     * Gets the number of data pages written during the last checkpoint.
+     *
+     * @return Total number of data pages written during the last checkpoint.
+     */
+    public long getLastCheckpointDataPagesNumber();
+
+    /**
+     * Gets the number of pages copied to a temporary checkpoint buffer during the last checkpoint.
+     *
+     * @return Total number of pages copied to a temporary checkpoint buffer during the last checkpoint.
+     */
+    public long getLastCheckpointCopiedOnWritePagesNumber();
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
index 14e3a73..362dcdf 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/PersistentStoreConfiguration.java
@@ -32,6 +32,15 @@ public class PersistentStoreConfiguration implements Serializable {
     public static final int DFLT_LOCK_WAIT_TIME = 10 * 1000;
 
     /** */
+    public static final boolean DFLT_METRICS_ENABLED = false;
+
+    /** Default amount of sub intervals to calculate rate-based metric. */
+    public static final int DFLT_SUB_INTERVALS = 5;
+
+    /** Default length of interval over which rate-based metric is calculated. */
+    public static final int DFLT_RATE_TIME_INTERVAL_MILLIS = 60_000;
+
+    /** */
     @SuppressWarnings("UnnecessaryBoxing")
     public static final Long DFLT_CHECKPOINTING_PAGE_BUFFER_SIZE = new Long(256L * 1024 * 1024);
 
@@ -48,7 +57,7 @@ public class PersistentStoreConfiguration implements Serializable {
     private static final int DFLT_WAL_SEGMENT_SIZE = 64 * 1024 * 1024;
 
     /** */
-    private String persistenteStorePath;
+    private String persistenceStorePath;
 
     /** Checkpointing frequency. */
     private long checkpointingFreq = DFLT_CHECKPOINTING_FREQ;
@@ -77,11 +86,27 @@ public class PersistentStoreConfiguration implements Serializable {
     /** WAL archive path. */
     private String walArchivePath;
 
+    /** Metrics enabled flag. */
+    private boolean metricsEnabled = DFLT_METRICS_ENABLED;
+
+    /**
+     * Number of sub-intervals the whole {@link #setRateTimeInterval(long)} will be split into to calculate
+     * rate-based metrics.
+     * <p>
+     * Setting it to a bigger value will result in more precise calculation and smaller drops of
+     * rate-based metrics when next sub-interval has to be recycled but introduces bigger
+     * calculation overhead.
+     */
+    private int subIntervals = DFLT_SUB_INTERVALS;
+
+    /** Time interval (in milliseconds) for rate-based metrics. */
+    private long rateTimeInterval = DFLT_RATE_TIME_INTERVAL_MILLIS;
+
     /**
      * Returns a path the root directory where the Persistent Store will persist data and indexes.
      */
     public String getPersistentStorePath() {
-        return persistenteStorePath;
+        return persistenceStorePath;
     }
 
     /**
@@ -91,7 +116,7 @@ public class PersistentStoreConfiguration implements Serializable {
      * @param persistenceStorePath Persistence store path.
      */
     public PersistentStoreConfiguration setPersistentStorePath(String persistenceStorePath) {
-        this.persistenteStorePath = persistenceStorePath;
+        this.persistenceStorePath = persistenceStorePath;
 
         return this;
     }
@@ -291,4 +316,68 @@ public class PersistentStoreConfiguration implements Serializable {
 
         return this;
     }
+
+    /**
+     * Gets flag indicating whether persistence metrics collection is enabled.
+     * Default value is {@link #DFLT_METRICS_ENABLED}.
+     *
+     * @return Metrics enabled flag.
+     */
+    public boolean isMetricsEnabled() {
+        return metricsEnabled;
+    }
+
+    /**
+     * Sets flag indicating whether persistence metrics collection is enabled.
+     *
+     * @param metricsEnabled Metrics enabled flag.
+     */
+    public PersistentStoreConfiguration setMetricsEnabled(boolean metricsEnabled) {
+        this.metricsEnabled = metricsEnabled;
+
+        return this;
+    }
+
+    /**
+     * Gets the length of the time interval for rate-based metrics. This interval defines a window over which
+     * hits will be tracked. Default value is {@link #DFLT_RATE_TIME_INTERVAL_MILLIS}.
+     *
+     * @return Time interval in milliseconds.
+     */
+    public long getRateTimeInterval() {
+        return rateTimeInterval;
+    }
+
+    /**
+     * Sets the length of the time interval for rate-based metrics. This interval defines a window over which
+     * hits will be tracked.
+     *
+     * @param rateTimeInterval Time interval in milliseconds.
+     */
+    public PersistentStoreConfiguration setRateTimeInterval(long rateTimeInterval) {
+        this.rateTimeInterval = rateTimeInterval;
+
+        return this;
+    }
+
+    /**
+     * Gets the number of sub-intervals to split the {@link #getRateTimeInterval()} into to track the update history.
+     * Default value is {@link #DFLT_SUB_INTERVALS}.
+     *
+     * @return The number of sub-intervals for history tracking.
+     */
+    public int getSubIntervals() {
+        return subIntervals;
+    }
+
+    /**
+     * Sets the number of sub-intervals to split the {@link #getRateTimeInterval()} into to track the update history.
+     *
+     * @param subIntervals The number of sub-intervals for history tracking.
+     */
+    public PersistentStoreConfiguration setSubIntervals(int subIntervals) {
+        this.subIntervals = subIntervals;
+
+        return this;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
index b04a969..bce3032 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java
@@ -75,6 +75,7 @@ import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.IgniteTransactions;
 import org.apache.ignite.Ignition;
 import org.apache.ignite.MemoryMetrics;
+import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.cache.affinity.Affinity;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterMetrics;
@@ -3441,6 +3442,18 @@ public class IgniteKernal implements IgniteEx, IgniteMXBean, Externalizable {
     }
 
     /** {@inheritDoc} */
+    @Override public PersistenceMetrics persistentStoreMetrics() {
+        guard();
+
+        try {
+            return ctx.cache().context().database().persistentStoreMetrics();
+        }
+        finally {
+            unguard();
+        }
+    }
+
+    /** {@inheritDoc} */
     @Nullable @Override public IgniteAtomicSequence atomicSequence(String name, long initVal, boolean create) {
         guard();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
index ac785b6..187df8d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/pagemem/wal/IgniteWriteAheadLogManager.java
@@ -102,6 +102,11 @@ public interface IgniteWriteAheadLogManager extends GridCacheSharedManager, Igni
     public int truncate(WALPointer ptr);
 
     /**
+     * @return Total number of segments in the WAL archive.
+     */
+    public int walArchiveSegments();
+
+    /**
      * @param ptr Pointer.
      * @return True if given pointer is located in reserved segment.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
index 19c25aa..f13532d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/IgniteCacheDatabaseSharedManager.java
@@ -28,6 +28,7 @@ import javax.management.JMException;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.MemoryMetrics;
+import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.configuration.MemoryConfiguration;
@@ -583,6 +584,13 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     }
 
     /**
+     * @return PersistenceMetrics if persistence is enabled or {@code null} otherwise.
+     */
+    public PersistenceMetrics persistentStoreMetrics() {
+        return null;
+    }
+
+    /**
      * @param memPlcName Name of {@link MemoryPolicy} to obtain {@link MemoryMetrics} for.
      * @return {@link MemoryMetrics} snapshot for specified {@link MemoryPolicy} or {@code null} if
      * no {@link MemoryPolicy} is configured for specified name.
@@ -904,6 +912,8 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
         MemoryPolicyConfiguration memPlcCfg,
         MemoryMetricsImpl memMetrics
     ) {
+        memMetrics.persistenceEnabled(false);
+
         return new PageMemoryNoStoreImpl(
             log,
             memProvider,

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsImpl.java
index 2bd96e7..2e5a78c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsImpl.java
@@ -16,12 +16,11 @@
  */
 package org.apache.ignite.internal.processors.cache.database;
 
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
 import org.apache.ignite.MemoryMetrics;
 import org.apache.ignite.configuration.MemoryPolicyConfiguration;
+import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.database.freelist.FreeListImpl;
-import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.internal.processors.cache.ratemetrics.HitRateMetrics;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jsr166.LongAdder8;
 
@@ -40,24 +39,30 @@ public class MemoryMetricsImpl implements MemoryMetrics {
      */
     private final LongAdder8 largeEntriesPages = new LongAdder8();
 
+    /** Counter for number of dirty pages. */
+    private LongAdder8 dirtyPages = new LongAdder8();
+
     /** */
     private volatile boolean metricsEnabled;
 
     /** */
-    private volatile int subInts;
+    private boolean persistenceEnabled;
 
     /** */
-    private volatile LongAdder8[] allocRateCounters;
+    private volatile int subInts;
 
-    /** */
-    private final AtomicInteger counterIdx = new AtomicInteger(0);
+    /** Allocation rate calculator. */
+    private volatile HitRateMetrics allocRate = new HitRateMetrics(60_000, 5);
 
     /** */
-    private final AtomicLong lastUpdTime = new AtomicLong(0);
+    private volatile HitRateMetrics pageReplaceRate = new HitRateMetrics(60_000, 5);
 
     /** */
     private final MemoryPolicyConfiguration memPlcCfg;
 
+    /** */
+    private PageMemory pageMem;
+
     /** Time interval (in milliseconds) when allocations/evictions are counted to calculate rate. */
     private volatile long rateTimeInterval;
 
@@ -72,11 +77,6 @@ public class MemoryMetricsImpl implements MemoryMetrics {
         rateTimeInterval = memPlcCfg.getRateTimeInterval();
 
         subInts = memPlcCfg.getSubIntervals();
-
-        allocRateCounters = new LongAdder8[subInts];
-
-        for (int i = 0; i < subInts; i++)
-            allocRateCounters[i] = new LongAdder8();
     }
 
     /** {@inheritDoc} */
@@ -94,12 +94,7 @@ public class MemoryMetricsImpl implements MemoryMetrics {
         if (!metricsEnabled)
             return 0;
 
-        float res = 0;
-
-        for (int i = 0; i < subInts; i++)
-            res += allocRateCounters[i].floatValue();
-
-        return res * 1000 / rateTimeInterval;
+        return ((float) allocRate.getRate()) / rateTimeInterval;
     }
 
     /** {@inheritDoc} */
@@ -125,125 +120,87 @@ public class MemoryMetricsImpl implements MemoryMetrics {
         return freeList.fillFactor();
     }
 
-    /**
-     * Increments totalAllocatedPages counter.
-     */
-    public void incrementTotalAllocatedPages() {
-        if (metricsEnabled) {
-            totalAllocatedPages.increment();
+    /** {@inheritDoc} */
+    @Override public long getDirtyPages() {
+        if (!metricsEnabled || !persistenceEnabled)
+            return 0;
 
-            try {
-                updateAllocationRateMetrics();
-            }
-            catch (ArrayIndexOutOfBoundsException | NullPointerException ignored) {
-                // No-op.
-            }
-        }
+        return dirtyPages.longValue();
     }
 
-    /**
-     *
-     */
-    private void updateAllocationRateMetrics() {
-        long lastUpdT = lastUpdTime.get();
-        long currT = IgniteUtils.currentTimeMillis();
-
-        int currIdx = counterIdx.get();
-
-        long deltaT = currT - lastUpdT;
-
-        int subInts = this.subInts;
-
-        LongAdder8[] rateCntrs = allocRateCounters;
-
-        if (subInts != rateCntrs.length)
-            return;
-
-        int cntrIdx = counterIdx.get();
-
-        for (int i = 1; i <= subInts; i++) {
-            if (deltaT < subInt(i)) {
-                if (i > 1) {
-                    if (!lastUpdTime.compareAndSet(lastUpdT, currT)) {
-                        rateCntrs[cntrIdx].increment();
-
-                        break;
-                    }
-                }
-
-                if (rotateIndex(currIdx, i - 1)) {
-                    currIdx = counterIdx.get();
-
-                    resetCounters(currIdx, i - 1);
-
-                    rateCntrs[currIdx].increment();
+    /** {@inheritDoc} */
+    @Override public float getPagesReplaceRate() {
+        if (!metricsEnabled || !persistenceEnabled)
+            return 0;
 
-                    break;
-                }
-                else {
-                    rateCntrs[cntrIdx].increment();
+        return ((float) pageReplaceRate.getRate()) / rateTimeInterval;
+    }
 
-                    break;
-                }
-            }
-            else if (i == subInts && lastUpdTime.compareAndSet(lastUpdT, currT))
-                resetAll();
+    /** {@inheritDoc} */
+    @Override public long getPhysicalMemoryPages() {
+        if (!metricsEnabled || !persistenceEnabled)
+            return 0;
 
-            if (currIdx != cntrIdx) {
-                rateCntrs[cntrIdx].increment();
+        assert pageMem != null;
 
-                break;
-            }
-        }
+        return pageMem.loadedPages();
     }
 
     /**
-     * @param intervalNum Interval number.
+     * Updates pageReplaceRate metric.
      */
-    private long subInt(int intervalNum) {
-        return (rateTimeInterval * intervalNum) / subInts;
+    public void updatePageReplaceRate() {
+        if (metricsEnabled)
+            pageReplaceRate.onHit();
     }
 
     /**
-     * @param idx Index.
-     * @param rotateStep Rotate step.
+     * Increments dirtyPages counter.
      */
-    private boolean rotateIndex(int idx, int rotateStep) {
-        if (rotateStep == 0)
-            return true;
+    public void incrementDirtyPages() {
+        if (metricsEnabled)
+            dirtyPages.increment();
+    }
 
-        int subInts = this.subInts;
+    /**
+     * Decrements dirtyPages counter.
+     */
+    public void decrementDirtyPages() {
+        if (metricsEnabled)
+            dirtyPages.decrement();
+    }
 
-        assert rotateStep < subInts;
+    /**
+     * Resets dirtyPages counter to zero.
+     */
+    public void resetDirtyPages() {
+        if (metricsEnabled)
+            dirtyPages.reset();
+    }
 
-        int nextIdx = (idx + rotateStep) % subInts;
+    /**
+     * Increments totalAllocatedPages counter.
+     */
+    public void incrementTotalAllocatedPages() {
+        if (metricsEnabled) {
+            totalAllocatedPages.increment();
 
-        return counterIdx.compareAndSet(idx, nextIdx);
+            updateAllocationRateMetrics();
+        }
     }
 
     /**
      *
      */
-    private void resetAll() {
-        LongAdder8[] cntrs = allocRateCounters;
-
-        for (LongAdder8 cntr : cntrs)
-            cntr.reset();
+    private void updateAllocationRateMetrics() {
+        allocRate.onHit();
     }
 
     /**
-     * @param currIdx Current index.
-     * @param resettingCntrs Resetting allocRateCounters.
+     * @param intervalNum Interval number.
      */
-    private void resetCounters(int currIdx, int resettingCntrs) {
-        if (resettingCntrs == 0)
-            return;
-
-        for (int j = 0; j < resettingCntrs; j++) {
-            int cleanIdx = currIdx - j >= 0 ? currIdx - j : currIdx - j + subInts;
-
-            allocRateCounters[cleanIdx].reset();
-        }
+    private long subInt(int intervalNum) {
+        return (rateTimeInterval * intervalNum) / subInts;
     }
 
     /**
@@ -277,10 +234,27 @@ public class MemoryMetricsImpl implements MemoryMetrics {
     }
 
     /**
+     * @param persistenceEnabled Persistence enabled.
+     */
+    public void persistenceEnabled(boolean persistenceEnabled) {
+        this.persistenceEnabled = persistenceEnabled;
+    }
+
+    /**
+     * @param pageMem Page mem.
+     */
+    public void pageMemory(PageMemory pageMem) {
+        this.pageMem = pageMem;
+    }
+
+    /**
      * @param rateTimeInterval Time interval (in milliseconds) used to calculate allocation/eviction rate.
      */
     public void rateTimeInterval(long rateTimeInterval) {
         this.rateTimeInterval = rateTimeInterval;
+
+        allocRate = new HitRateMetrics((int) rateTimeInterval, subInts);
+        pageReplaceRate = new HitRateMetrics((int) rateTimeInterval, subInts);
     }
 
     /**
@@ -297,13 +271,8 @@ public class MemoryMetricsImpl implements MemoryMetrics {
         if (rateTimeInterval / subInts < 10)
             subInts = (int) rateTimeInterval / 10;
 
-        LongAdder8[] newCounters = new LongAdder8[subInts];
-
-        for (int i = 0; i < subInts; i++)
-            newCounters[i] = new LongAdder8();
-
-        this.subInts = subInts;
-        allocRateCounters = newCounters;
+        allocRate = new HitRateMetrics((int) rateTimeInterval, subInts);
+        pageReplaceRate = new HitRateMetrics((int) rateTimeInterval, subInts);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsMXBeanImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsMXBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsMXBeanImpl.java
index d3ae378..b53db4b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsMXBeanImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsMXBeanImpl.java
@@ -67,6 +67,21 @@ class MemoryMetricsMXBeanImpl implements MemoryMetricsMXBean {
     }
 
     /** {@inheritDoc} */
+    @Override public long getDirtyPages() {
+        return memMetrics.getDirtyPages();
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getPagesReplaceRate() {
+        return memMetrics.getPagesReplaceRate();
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getPhysicalMemoryPages() {
+        return memMetrics.getPhysicalMemoryPages();
+    }
+
+    /** {@inheritDoc} */
     @Override public void rateTimeInterval(long rateTimeInterval) {
         if (rateTimeInterval < 1000)
             throw new IllegalArgumentException("rateTimeInterval property must be positive " +

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsSnapshot.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsSnapshot.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsSnapshot.java
index 5f337bd..f4874eb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsSnapshot.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/MemoryMetricsSnapshot.java
@@ -41,6 +41,15 @@ public class MemoryMetricsSnapshot implements MemoryMetrics {
     /** */
     private float pagesFillFactor;
 
+    /** */
+    private long dirtyPages;
+
+    /** */
+    private float pageReplaceRate;
+
+    /** */
+    private long physicalMemoryPages;
+
     /**
      * @param metrics Metrics instance to take a copy.
      */
@@ -51,6 +60,9 @@ public class MemoryMetricsSnapshot implements MemoryMetrics {
         evictionRate = metrics.getEvictionRate();
         largeEntriesPagesPercentage = metrics.getLargeEntriesPagesPercentage();
         pagesFillFactor = metrics.getPagesFillFactor();
+        dirtyPages = metrics.getDirtyPages();
+        pageReplaceRate = metrics.getPagesReplaceRate();
+        physicalMemoryPages = metrics.getPhysicalMemoryPages();
     }
 
     /** {@inheritDoc} */
@@ -82,4 +94,19 @@ public class MemoryMetricsSnapshot implements MemoryMetrics {
     @Override public float getPagesFillFactor() {
         return pagesFillFactor;
     }
+
+    /** {@inheritDoc} */
+    @Override public long getDirtyPages() {
+        return dirtyPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getPagesReplaceRate() {
+        return pageReplaceRate;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getPhysicalMemoryPages() {
+        return physicalMemoryPages;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsSnapshot.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsSnapshot.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsSnapshot.java
new file mode 100644
index 0000000..c939710
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsSnapshot.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.internal.processors.cache.database;
+
+import org.apache.ignite.PersistenceMetrics;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ *
+ */
+public class PersistenceMetricsSnapshot implements PersistenceMetrics {
+    /** */
+    private float walLoggingRate;
+
+    /** */
+    private float walWritingRate;
+
+    /** */
+    private int walArchiveSegments;
+
+    /** */
+    private float walFsyncTimeAvg;
+
+    /** */
+    private long lastCpDuration;
+
+    /** */
+    private long lastCpLockWaitDuration;
+
+    /** */
+    private long lastCpMmarkDuration;
+
+    /** */
+    private long lastCpPagesWriteDuration;
+
+    /** */
+    private long lastCpFsyncDuration;
+
+    /** */
+    private long lastCpTotalPages;
+
+    /** */
+    private long lastCpDataPages;
+
+    /** */
+    private long lastCpCowPages;
+
+    /**
+     * @param metrics Metrics.
+     */
+    public PersistenceMetricsSnapshot(PersistenceMetrics metrics) {
+        walLoggingRate = metrics.getWalLoggingRate();
+        walWritingRate = metrics.getWalWritingRate();
+        walArchiveSegments = metrics.getWalArchiveSegments();
+        walFsyncTimeAvg = metrics.getWalFsyncTimeAverage();
+        lastCpDuration = metrics.getLastCheckpointingDuration();
+        lastCpLockWaitDuration = metrics.getLastCheckpointLockWaitDuration();
+        lastCpMmarkDuration = metrics.getLastCheckpointMarkDuration();
+        lastCpPagesWriteDuration = metrics.getLastCheckpointPagesWriteDuration();
+        lastCpFsyncDuration = metrics.getLastCheckpointFsyncDuration();
+        lastCpTotalPages = metrics.getLastCheckpointTotalPagesNumber();
+        lastCpDataPages = metrics.getLastCheckpointDataPagesNumber();
+        lastCpCowPages = metrics.getLastCheckpointCopiedOnWritePagesNumber();
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getWalLoggingRate() {
+        return walLoggingRate;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getWalWritingRate() {
+        return walWritingRate;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getWalArchiveSegments() {
+        return walArchiveSegments;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getWalFsyncTimeAverage() {
+        return walFsyncTimeAvg;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointingDuration() {
+        return lastCpDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointLockWaitDuration() {
+        return lastCpLockWaitDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointMarkDuration() {
+        return lastCpMmarkDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointPagesWriteDuration() {
+        return lastCpPagesWriteDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointFsyncDuration() {
+        return lastCpFsyncDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointTotalPagesNumber() {
+        return lastCpTotalPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointDataPagesNumber() {
+        return lastCpDataPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointCopiedOnWritePagesNumber() {
+        return lastCpCowPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(PersistenceMetricsSnapshot.class, this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
index e40ed11..dd6a503 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/io/PageIO.java
@@ -537,4 +537,12 @@ public abstract class PageIO {
 
         throw new IgniteCheckedException("Unknown page IO type: " + type);
     }
+
+    /**
+     * @param type Type to test.
+     * @return {@code True} if data page.
+     */
+    public static boolean isDataPageType(int type) {
+        return type == T_DATA;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/mxbean/MemoryMetricsMXBean.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/MemoryMetricsMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/MemoryMetricsMXBean.java
index d0900f6..4d6c96a 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/MemoryMetricsMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/MemoryMetricsMXBean.java
@@ -73,6 +73,18 @@ public interface MemoryMetricsMXBean extends MemoryMetrics {
     @MXBeanDescription("Percentage of space that is still free and can be filled in.")
     @Override public float getPagesFillFactor();
 
+    /** {@inheritDoc} */
+    @MXBeanDescription("Number of pages in memory not yet synchronized with persistent storage.")
+    @Override public long getDirtyPages();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Rate at which pages in memory are replaced with pages from persistent storage (pages per second).")
+    @Override public float getPagesReplaceRate();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Number of pages residing in physical RAM.")
+    @Override public long getPhysicalMemoryPages();
+
     /**
      * Enables memory metrics collection on an Apache Ignite node.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/main/java/org/apache/ignite/mxbean/PersistenceMetricsMXBean.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/PersistenceMetricsMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/PersistenceMetricsMXBean.java
new file mode 100644
index 0000000..30ccab4
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/PersistenceMetricsMXBean.java
@@ -0,0 +1,121 @@
+/*
+ * 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.mxbean;
+
+import org.apache.ignite.PersistenceMetrics;
+import org.apache.ignite.configuration.PersistentStoreConfiguration;
+
+/**
+ *
+ */
+public interface PersistenceMetricsMXBean extends PersistenceMetrics {
+    /** {@inheritDoc} */
+    @MXBeanDescription("Average number of WAL records per second written during the last time interval.")
+    @Override float getWalLoggingRate();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Average number of bytes per second written during the last time interval.")
+    @Override float getWalWritingRate();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Current number of WAL segments in the WAL archive.")
+    @Override int getWalArchiveSegments();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Average WAL fsync duration in microseconds over the last time interval.")
+    @Override float getWalFsyncTimeAverage();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Duration of the last checkpoint in milliseconds.")
+    @Override long getLastCheckpointingDuration();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Duration of the checkpoint lock wait in milliseconds.")
+    @Override long getLastCheckpointLockWaitDuration();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Duration of the checkpoint mark in milliseconds.")
+    @Override long getLastCheckpointMarkDuration();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Duration of the checkpoint pages write in milliseconds.")
+    @Override long getLastCheckpointPagesWriteDuration();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Duration of the sync phase of the last checkpoint in milliseconds.")
+    @Override long getLastCheckpointFsyncDuration();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Total number of pages written during the last checkpoint.")
+    @Override long getLastCheckpointTotalPagesNumber();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Total number of data pages written during the last checkpoint.")
+    @Override long getLastCheckpointDataPagesNumber();
+
+    /** {@inheritDoc} */
+    @MXBeanDescription("Number of pages copied to a temporary checkpoint buffer during the last checkpoint.")
+    @Override long getLastCheckpointCopiedOnWritePagesNumber();
+
+    /**
+     * Enables persistence metrics collection on an Apache Ignite node.
+     */
+    @MXBeanDescription("Enables persistence metrics collection on an Apache Ignite node.")
+    public void enableMetrics();
+
+    /**
+     * Disables persistence metrics collection on an Apache Ignite node.
+     */
+    @MXBeanDescription("Disables persistence metrics collection on an Apache Ignite node.")
+    public void disableMetrics();
+
+    /**
+     * Sets time interval for rate-based metrics. Identical to setting
+     * {@link PersistentStoreConfiguration#setRateTimeInterval(long)} configuration property.
+     *
+     * @param rateTimeInterval Time interval (in milliseconds) used for allocation and eviction rates calculations.
+     */
+    @MXBeanDescription(
+        "Sets time interval for pages allocation and eviction monitoring purposes."
+    )
+    @MXBeanParametersNames(
+        "rateTimeInterval"
+    )
+    @MXBeanParametersDescriptions(
+        "Time interval (in milliseconds) to set."
+    )
+    public void rateTimeInterval(long rateTimeInterval);
+
+    /**
+     * Sets a number of sub-intervals the whole {@link #rateTimeInterval(long)} will be split into to calculate
+     * rate-based metrics. Identical to setting {@link PersistentStoreConfiguration#setSubIntervals(int)} configuration
+     * property.
+     *
+     * @param subInts A number of sub-intervals.
+     */
+    @MXBeanDescription(
+        "Sets a number of sub-intervals to calculate allocation and eviction rates metrics."
+    )
+    @MXBeanParametersNames(
+        "subInts"
+    )
+    @MXBeanParametersDescriptions(
+        "Number of subintervals to set."
+    )
+    public void subIntervals(int subInts);
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/test/java/org/apache/ignite/internal/processors/database/MemoryMetricsSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/MemoryMetricsSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/MemoryMetricsSelfTest.java
index 706e717..3a99cb2 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/MemoryMetricsSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/MemoryMetricsSelfTest.java
@@ -66,7 +66,9 @@ public class MemoryMetricsSelfTest extends GridCommonAbstractTest {
 
         joinAllThreads();
 
-        assertEquals(4, watcher.rateDropsCntr);
+        assertTrue(watcher.rateDropsCntr > 3);
+
+        assertTrue(watcher.rateDropsCntr < 6);
     }
 
     /**
@@ -87,7 +89,9 @@ public class MemoryMetricsSelfTest extends GridCommonAbstractTest {
 
         joinAllocationThreads();
 
-        assertEquals(4, watcher.rateDropsCntr);
+        assertTrue(watcher.rateDropsCntr > 3);
+
+        assertTrue(watcher.rateDropsCntr < 6);
 
         sleep(3);
 
@@ -150,7 +154,7 @@ public class MemoryMetricsSelfTest extends GridCommonAbstractTest {
         for (int i = 0; i < 10; i++) {
             Thread.sleep(25);
 
-            memMetrics.subIntervals((2 + i * 5) % 3 + 1);
+            memMetrics.subIntervals((2 + i * 5) % 3 + 2);
         }
 
         joinAllThreads();

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsIgniteMock.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsIgniteMock.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsIgniteMock.java
index b4fcc70..2f3a878 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsIgniteMock.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/igfs/IgfsIgniteMock.java
@@ -39,6 +39,7 @@ import org.apache.ignite.IgniteServices;
 import org.apache.ignite.IgniteSet;
 import org.apache.ignite.IgniteTransactions;
 import org.apache.ignite.MemoryMetrics;
+import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.cache.affinity.Affinity;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterNode;
@@ -553,6 +554,13 @@ public class IgfsIgniteMock implements IgniteEx {
         return null;
     }
 
+    /** {@inheritDoc} */
+    @Override public PersistenceMetrics persistentStoreMetrics() {
+        throwUnsupported();
+
+        return null;
+    }
+
     /**
      * Throw {@link UnsupportedOperationException}.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java
index 97157a8..709639e 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java
@@ -46,6 +46,7 @@ import org.apache.ignite.IgniteServices;
 import org.apache.ignite.IgniteSet;
 import org.apache.ignite.IgniteTransactions;
 import org.apache.ignite.MemoryMetrics;
+import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.binary.BinaryObjectBuilder;
 import org.apache.ignite.binary.BinaryObjectException;
 import org.apache.ignite.cache.affinity.Affinity;
@@ -452,6 +453,11 @@ public class IgniteMock implements Ignite {
         return null;
     }
 
+    /** {@inheritDoc} */
+    @Override public PersistenceMetrics persistentStoreMetrics() {
+        return null;
+    }
+
     /**
      * @param staticCfg Configuration.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
index f4dc491..4fea480 100644
--- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
+++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/multijvm/IgniteProcessProxy.java
@@ -50,6 +50,7 @@ import org.apache.ignite.IgniteServices;
 import org.apache.ignite.IgniteSet;
 import org.apache.ignite.IgniteTransactions;
 import org.apache.ignite.MemoryMetrics;
+import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.cache.affinity.Affinity;
 import org.apache.ignite.cluster.ClusterGroup;
 import org.apache.ignite.cluster.ClusterNode;
@@ -670,6 +671,11 @@ public class IgniteProcessProxy implements IgniteEx {
     }
 
     /** {@inheritDoc} */
+    @Override public PersistenceMetrics persistentStoreMetrics() {
+        throw new UnsupportedOperationException("Operation isn't supported yet.");
+    }
+
+    /** {@inheritDoc} */
     @Override public void close() throws IgniteException {
         if (locJvmGrid != null) {
             final CountDownLatch rmtNodeStoppedLatch = new CountDownLatch(1);

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
index 9f51750..ff5dfa9 100755
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/GridCacheDatabaseSharedManager.java
@@ -57,10 +57,15 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanRegistrationException;
+import javax.management.ObjectName;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.PersistenceMetrics;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.configuration.IgniteConfiguration;
@@ -101,6 +106,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.StoredCacheData;
 import org.apache.ignite.internal.processors.cache.database.file.FilePageStoreManager;
+import org.apache.ignite.internal.processors.cache.database.pagemem.CheckpointMetricsTracker;
 import org.apache.ignite.internal.processors.cache.database.pagemem.PageMemoryEx;
 import org.apache.ignite.internal.processors.cache.database.pagemem.PageMemoryImpl;
 import org.apache.ignite.internal.processors.cache.database.tree.io.PageIO;
@@ -127,6 +133,7 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.internal.util.worker.GridWorker;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteInClosure;
+import org.apache.ignite.mxbean.PersistenceMetricsMXBean;
 import org.apache.ignite.thread.IgniteThread;
 import org.jetbrains.annotations.Nullable;
 
@@ -235,7 +242,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     private volatile boolean printCheckpointStats = true;
 
     /** Database configuration. */
-    private final PersistentStoreConfiguration dbCfg;
+    private final PersistentStoreConfiguration persistenceCfg;
 
     /** */
     private final Collection<DbCheckpointListener> lsnrs = new CopyOnWriteArrayList<>();
@@ -270,19 +277,25 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /** Snapshot manager. */
     private IgniteCacheSnapshotManager snapshotMgr;
 
+    /** */
+    private PersistenceMetricsImpl persStoreMetrics;
+
+    /** */
+    private ObjectName persistenceMetricsMbeanName;
+
     /**
      * @param ctx Kernal context.
      */
     public GridCacheDatabaseSharedManager(GridKernalContext ctx) {
         IgniteConfiguration cfg = ctx.config();
 
-        dbCfg = cfg.getPersistentStoreConfiguration();
+        persistenceCfg = cfg.getPersistentStoreConfiguration();
 
-        assert dbCfg != null : "PageStore should not be created if persistence is disabled.";
+        assert persistenceCfg != null : "PageStore should not be created if persistence is disabled.";
 
-        checkpointFreq = dbCfg.getCheckpointingFrequency();
+        checkpointFreq = persistenceCfg.getCheckpointingFrequency();
 
-        lockWaitTime = dbCfg.getLockWaitTime();
+        lockWaitTime = persistenceCfg.getLockWaitTime();
 
         final int pageSize = cfg.getMemoryConfiguration().getPageSize();
 
@@ -296,6 +309,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 return tmpWriteBuf;
             }
         };
+
+        persStoreMetrics = new PersistenceMetricsImpl(
+            persistenceCfg.isMetricsEnabled(),
+            persistenceCfg.getRateTimeInterval(),
+            persistenceCfg.getSubIntervals()
+        );
     }
 
     /**
@@ -339,6 +358,21 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                 throw new IgniteCheckedException("Could not create directory for checkpoint metadata: " + cpDir);
 
             fileLockHolder = new FileLockHolder(storeMgr.workDir().getPath(), cctx.kernalContext(), log);
+
+            persStoreMetrics.wal(cctx.wal());
+
+            try {
+                persistenceMetricsMbeanName = U.registerMBean(
+                    cctx.kernalContext().config().getMBeanServer(),
+                    cctx.kernalContext().igniteInstanceName(),
+                    "Persistent Store",
+                    "PersistenceMetrics",
+                    persStoreMetrics,
+                    PersistenceMetricsMXBean.class);
+            }
+            catch (JMException e) {
+                throw new IgniteCheckedException("Failed to register persistence metrics MBean", e);
+            }
         }
     }
 
@@ -346,19 +380,19 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
      *
      */
     @Override public void initDataBase() throws IgniteCheckedException {
-        Long cpBufSize = dbCfg.getCheckpointingPageBufferSize();
+        Long cpBufSize = persistenceCfg.getCheckpointingPageBufferSize();
 
-        if (dbCfg.getCheckpointingThreads() > 1)
+        if (persistenceCfg.getCheckpointingThreads() > 1)
             asyncRunner = new ThreadPoolExecutor(
-                dbCfg.getCheckpointingThreads(),
-                dbCfg.getCheckpointingThreads(),
+                persistenceCfg.getCheckpointingThreads(),
+                persistenceCfg.getCheckpointingThreads(),
                 30L,
                 TimeUnit.SECONDS,
                 new LinkedBlockingQueue<Runnable>()
             );
 
         // Intentionally use identity comparison to check if configuration default has changed.
-        // Noinspection NumberEquality.
+        //noinspection NumberEquality
         if (cpBufSize == PersistentStoreConfiguration.DFLT_CHECKPOINTING_PAGE_BUFFER_SIZE) {
             MemoryConfiguration memCfg = cctx.kernalContext().config().getMemoryConfiguration();
 
@@ -544,6 +578,18 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
             fileLockHolder.close();
         }
+
+        if (persistenceMetricsMbeanName != null) {
+            try {
+                cctx.kernalContext().config().getMBeanServer().unregisterMBean(persistenceMetricsMbeanName);
+            }
+            catch (InstanceNotFoundException ignore) {
+                // No-op, nothing to unregister.
+            }
+            catch (MBeanRegistrationException e) {
+                U.error(log, "Failed to unregister persistence metrics MBean (will continue stop routine)", e);
+            }
+        }
     }
 
     /** {@inheritDoc} */
@@ -580,7 +626,9 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         MemoryPolicyConfiguration plcCfg,
         MemoryMetricsImpl memMetrics
     ) {
-        return new PageMemoryImpl(
+        memMetrics.persistenceEnabled(true);
+
+        PageMemoryImpl pageMem = new PageMemoryImpl(
             memProvider,
             calculateFragmentSizes(
                 memCfg.getConcurrencyLevel(),
@@ -599,17 +647,22 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     snapshotMgr.flushDirtyPageHandler(fullId, pageBuf, tag);
                 }
             },
-            new GridInClosure3X<Long,FullPageId, PageMemoryEx>() {
+            new GridInClosure3X<Long, FullPageId, PageMemoryEx>() {
                 @Override public void applyx(
                     Long page,
                     FullPageId fullId,
                     PageMemoryEx pageMem
                 ) throws IgniteCheckedException {
-                  snapshotMgr.onChangeTrackerPage(page, fullId, pageMem);
+                    snapshotMgr.onChangeTrackerPage(page, fullId, pageMem);
                 }
             },
-            this
+            this,
+            memMetrics
         );
+
+        memMetrics.pageMemory(pageMem);
+
+        return pageMem;
     }
 
     /** {@inheritDoc} */
@@ -1074,8 +1127,6 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             cp.cancelOrWaitPartitionDestroy(grpId, partId);
     }
 
-
-
     /**
      * Tries to search for a WAL pointer for the given partition counter start.
      *
@@ -1656,7 +1707,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             for (FullPageId fullId : cpPages) {
                 tmpWriteBuf.rewind();
 
-                Integer tag = pageMem.getForCheckpoint(fullId, tmpWriteBuf);
+                Integer tag = pageMem.getForCheckpoint(fullId, tmpWriteBuf, null);
 
                 if (tag != null) {
                     tmpWriteBuf.rewind();
@@ -1969,14 +2020,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
          */
         private void doCheckpoint() {
             try {
-                long start = U.currentTimeMillis();
+                CheckpointMetricsTracker tracker = new CheckpointMetricsTracker();
 
-                Checkpoint chp = markCheckpointBegin();
+                Checkpoint chp = markCheckpointBegin(tracker);
 
                 snapshotMgr.onCheckPointBegin();
 
-                long written, fsync, marked = U.currentTimeMillis();
-
                 int pages = chp.cpPages.size();
 
                 boolean interrupted = true;
@@ -1988,9 +2037,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     CountDownFuture doneWriteFut = new CountDownFuture(
                         asyncRunner == null ? 1 : chp.cpPages.collectionsSize());
 
+                    tracker.onPagesWriteStart();
+
                     if (asyncRunner != null) {
                         for (int i = 0; i < chp.cpPages.collectionsSize(); i++) {
                             Runnable write = new WriteCheckpointPages(
+                                tracker,
                                 chp.cpPages.innerCollection(i),
                                 updStores,
                                 doneWriteFut
@@ -2007,7 +2059,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     }
                     else {
                         // Single-threaded checkpoint.
-                        Runnable write = new WriteCheckpointPages(chp.cpPages, updStores, doneWriteFut);
+                        Runnable write = new WriteCheckpointPages(tracker, chp.cpPages, updStores, doneWriteFut);
 
                         write.run();
                     }
@@ -2022,7 +2074,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
                     snapshotMgr.afterCheckpointPageWritten();
 
-                    written = U.currentTimeMillis();
+                    tracker.onFsyncStart();
 
                     if (!skipSync) {
                         for (PageStore updStore : updStores) {
@@ -2033,8 +2085,6 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                         }
                     }
 
-                    fsync = U.currentTimeMillis();
-
                     // Must mark successful checkpoint only if there are no exceptions or interrupts.
                     interrupted = false;
                 }
@@ -2043,7 +2093,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                         markCheckpointEnd(chp);
                 }
 
-                long fsyncEnd = U.currentTimeMillis();
+                tracker.onEnd();
 
                 // We finished this checkpoint, now it's time to clean up partitions.
                 PartitionDestroyQueue destroyQueue = chp.progress.destroyQueue;
@@ -2073,19 +2123,30 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     finishDestroyPartitionsAsync(reqs);
                 }
 
-                if (printCheckpointStats)
-                    log.info(String.format("Checkpoint finished [cpId=%s, pages=%d, markPos=%s, " +
-                            "walSegmentsCleared=%d, markBegin=%dms, pagesWrite=%dms, fsync=%dms, markEnd=%dms, " +
-                            "total=%dms]",
-                        chp.cpEntry.checkpointId(),
-                        pages,
-                        chp.cpEntry.checkpointMark(),
-                        chp.walFilesDeleted,
-                        marked - start,
-                        written - marked,
-                        fsync - written,
-                        fsyncEnd - fsync,
-                        fsyncEnd - start));
+                if (printCheckpointStats) {
+                    if (log.isInfoEnabled())
+                        log.info(String.format("Checkpoint finished [cpId=%s, pages=%d, markPos=%s, " +
+                                "walSegmentsCleared=%d, markDuration=%dms, pagesWrite=%dms, fsync=%dms, " +
+                                "total=%dms]",
+                            chp.cpEntry.checkpointId(),
+                            pages,
+                            chp.cpEntry.checkpointMark(),
+                            chp.walFilesDeleted,
+                            tracker.markDuration(),
+                            tracker.pagesWriteDuration(),
+                            tracker.fsyncDuration(),
+                            tracker.totalDuration()));
+                }
+
+                persStoreMetrics.onCheckpoint(
+                    tracker.lockWaitDuration(),
+                    tracker.markDuration(),
+                    tracker.pagesWriteDuration(),
+                    tracker.fsyncDuration(),
+                    tracker.totalDuration(),
+                    pages,
+                    tracker.dataPagesWritten(),
+                    tracker.cowPagesWritten());
             }
             catch (IgniteCheckedException e) {
                 // TODO-ignite-db how to handle exception?
@@ -2127,21 +2188,21 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
          *
          */
         @SuppressWarnings("TooBroadScope")
-        private Checkpoint markCheckpointBegin() throws IgniteCheckedException {
+        private Checkpoint markCheckpointBegin(CheckpointMetricsTracker tracker) throws IgniteCheckedException {
             CheckpointRecord cpRec = new CheckpointRecord(null, false);
 
             WALPointer cpPtr;
 
             GridMultiCollectionWrapper<FullPageId> cpPages;
 
-            long lockAcquired, lockReleased, lockStart = U.currentTimeMillis();
-
             final CheckpointProgress curr;
 
+            tracker.onLockWaitStart();
+
             checkpointLock.writeLock().lock();
 
             try {
-                lockAcquired = U.currentTimeMillis();
+                tracker.onMarkStart();
 
                 synchronized (this) {
                     curr = scheduledCp;
@@ -2210,7 +2271,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             finally {
                 checkpointLock.writeLock().unlock();
 
-                lockReleased = U.currentTimeMillis();
+                tracker.onLockRelease();
             }
 
             curr.cpBeginFut.onDone();
@@ -2236,8 +2297,8 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                             "checkpointLockHoldTime=%dms, pages=%d, reason='%s']",
                         cpRec.checkpointId(),
                         cpPtr,
-                        lockAcquired - lockStart,
-                        lockReleased - lockAcquired,
+                        tracker.lockWaitDuration(),
+                        tracker.lockHoldDuration(),
                         cpPages.size(),
                         curr.reason)
                     );
@@ -2315,6 +2376,9 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
      */
     private class WriteCheckpointPages implements Runnable {
         /** */
+        private CheckpointMetricsTracker tracker;
+
+        /** */
         private Collection<FullPageId> writePageIds;
 
         /** */
@@ -2327,10 +2391,12 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
          * @param writePageIds Write page IDs.
          */
         private WriteCheckpointPages(
+            CheckpointMetricsTracker tracker,
             Collection<FullPageId> writePageIds,
             GridConcurrentHashSet<PageStore> updStores,
             CountDownFuture doneFut
         ) {
+            this.tracker = tracker;
             this.writePageIds = writePageIds;
             this.updStores = updStores;
             this.doneFut = doneFut;
@@ -2362,11 +2428,18 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
 
                     PageMemoryEx pageMem = (PageMemoryEx)grp.memoryPolicy().pageMemory();
 
-                    Integer tag = pageMem.getForCheckpoint(fullId, tmpWriteBuf);
+                    Integer tag = pageMem.getForCheckpoint(fullId, tmpWriteBuf, persStoreMetrics.metricsEnabled() ? tracker : null);
 
                     if (tag != null) {
                         tmpWriteBuf.rewind();
 
+                        if (persStoreMetrics.metricsEnabled()) {
+                            int pageType = PageIO.getType(tmpWriteBuf);
+
+                            if (PageIO.isDataPageType(pageType))
+                                tracker.onDataPageWritten();
+                        }
+
                         if (!skipCrc) {
                             PageIO.setCrc(writeAddr, PureJavaCrc32.calcCrc32(tmpWriteBuf, pageSize()));
 
@@ -2488,7 +2561,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     /**
      *
      */
-    public static class CheckpointProgress {
+    private static class CheckpointProgress {
         /** */
         private volatile long nextCpTs;
 
@@ -2499,13 +2572,13 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         private GridFutureAdapter cpFinishFut = new GridFutureAdapter<>();
 
         /** */
-        public volatile boolean nextSnapshot;
+        private volatile boolean nextSnapshot;
 
         /** */
         private volatile boolean started;
 
         /** */
-        public volatile SnapshotOperation snapshotOperation;
+        private volatile SnapshotOperation snapshotOperation;
 
         /** Wakeup reason. */
         private String reason;
@@ -2629,7 +2702,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         private int onCheckpointFinished() {
             int deleted = 0;
 
-            while (histMap.size() > dbCfg.getWalHistorySize()) {
+            while (histMap.size() > persistenceCfg.getWalHistorySize()) {
                 Map.Entry<Long, CheckpointEntry> entry = histMap.firstEntry();
 
                 CheckpointEntry cpEntry = entry.getValue();
@@ -2649,7 +2722,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
                     U.warn(log, "Failed to remove stale checkpoint files [startFile=" + startFile.getAbsolutePath() +
                         ", endFile=" + endFile.getAbsolutePath() + ']');
 
-                    if (histMap.size() > 2 * dbCfg.getWalHistorySize()) {
+                    if (histMap.size() > 2 * persistenceCfg.getWalHistorySize()) {
                         U.error(log, "Too many stale checkpoint entries in the map, will truncate WAL archive anyway.");
 
                         fail = false;
@@ -3178,4 +3251,16 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
             return file.getAbsolutePath();
         }
     }
+
+    /** {@inheritDoc} */
+    @Override public PersistenceMetrics persistentStoreMetrics() {
+        return new PersistenceMetricsSnapshot(persStoreMetrics);
+    }
+
+    /**
+     *
+     */
+    public PersistenceMetricsImpl persistentStoreMetricsImpl() {
+        return persStoreMetrics;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsImpl.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsImpl.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsImpl.java
new file mode 100644
index 0000000..25d95ae
--- /dev/null
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/PersistenceMetricsImpl.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.internal.processors.cache.database;
+
+import org.apache.ignite.internal.pagemem.wal.IgniteWriteAheadLogManager;
+import org.apache.ignite.internal.processors.cache.ratemetrics.HitRateMetrics;
+import org.apache.ignite.mxbean.PersistenceMetricsMXBean;
+
+/**
+ *
+ */
+public class PersistenceMetricsImpl implements PersistenceMetricsMXBean {
+    /** */
+    private volatile HitRateMetrics walLoggingRate;
+
+    /** */
+    private volatile HitRateMetrics walWritingRate;
+
+    /** */
+    private volatile HitRateMetrics walFsyncTimeDuration;
+
+    /** */
+    private volatile HitRateMetrics walFsyncTimeNumber;
+
+    /** */
+    private volatile long lastCpLockWaitDuration;
+
+    /** */
+    private volatile long lastCpMarkDuration;
+
+    /** */
+    private volatile long lastCpPagesWriteDuration;
+
+    /** */
+    private volatile long lastCpDuration;
+
+    /** */
+    private volatile long lastCpFsyncDuration;
+
+    /** */
+    private volatile long lastCpTotalPages;
+
+    /** */
+    private volatile long lastCpDataPages;
+
+    /** */
+    private volatile long lastCpCowPages;
+
+    /** */
+    private volatile long rateTimeInterval;
+
+    /** */
+    private volatile int subInts;
+
+    /** */
+    private volatile boolean metricsEnabled;
+
+    /** */
+    private IgniteWriteAheadLogManager wal;
+
+    /**
+     * @param metricsEnabled Metrics enabled flag.
+     * @param rateTimeInterval Rate time interval.
+     * @param subInts Number of sub-intervals.
+     */
+    public PersistenceMetricsImpl(
+        boolean metricsEnabled,
+        long rateTimeInterval,
+        int subInts
+    ) {
+        this.metricsEnabled = metricsEnabled;
+        this.rateTimeInterval = rateTimeInterval;
+        this.subInts = subInts;
+
+        resetRates();
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getWalLoggingRate() {
+        if (!metricsEnabled)
+            return 0;
+
+        return ((float)walLoggingRate.getRate()) / rateTimeInterval;
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getWalWritingRate() {
+        if (!metricsEnabled)
+            return 0;
+
+        return ((float)walWritingRate.getRate()) / rateTimeInterval;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int getWalArchiveSegments() {
+        if (!metricsEnabled)
+            return 0;
+
+        return wal.walArchiveSegments();
+    }
+
+    /** {@inheritDoc} */
+    @Override public float getWalFsyncTimeAverage() {
+        if (!metricsEnabled)
+            return 0;
+
+        long numRate = walFsyncTimeNumber.getRate();
+
+        if (numRate == 0)
+            return 0;
+
+        return (float)walFsyncTimeDuration.getRate() / numRate;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointingDuration() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointLockWaitDuration() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpLockWaitDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointMarkDuration() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpMarkDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointPagesWriteDuration() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpPagesWriteDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointFsyncDuration() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpFsyncDuration;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointTotalPagesNumber() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpTotalPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointDataPagesNumber() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpDataPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public long getLastCheckpointCopiedOnWritePagesNumber() {
+        if (!metricsEnabled)
+            return 0;
+
+        return lastCpCowPages;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void enableMetrics() {
+        metricsEnabled = true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void disableMetrics() {
+        metricsEnabled = false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void rateTimeInterval(long rateTimeInterval) {
+        this.rateTimeInterval = rateTimeInterval;
+
+        resetRates();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void subIntervals(int subInts) {
+        this.subInts = subInts;
+
+        resetRates();
+    }
+
+    /**
+     * @param wal Write-ahead log manager.
+     */
+    public void wal(IgniteWriteAheadLogManager wal) {
+        this.wal = wal;
+    }
+
+    /**
+     * @return Metrics enabled flag.
+     */
+    public boolean metricsEnabled() {
+        return metricsEnabled;
+    }
+
+    /**
+     * @param lockWaitDuration Lock wait duration.
+     * @param markDuration Mark duration.
+     * @param pagesWriteDuration Pages write duration.
+     * @param fsyncDuration Total checkpoint fsync duration.
+     * @param duration Total checkpoint duration.
+     * @param totalPages Total number of all pages in checkpoint.
+     * @param dataPages Total number of data pages in checkpoint.
+     * @param cowPages Total number of COW-ed pages in checkpoint.
+     */
+    public void onCheckpoint(
+        long lockWaitDuration,
+        long markDuration,
+        long pagesWriteDuration,
+        long fsyncDuration,
+        long duration,
+        long totalPages,
+        long dataPages,
+        long cowPages
+    ) {
+        if (metricsEnabled) {
+            lastCpLockWaitDuration = lockWaitDuration;
+            lastCpMarkDuration = markDuration;
+            lastCpPagesWriteDuration = pagesWriteDuration;
+            lastCpFsyncDuration = fsyncDuration;
+            lastCpDuration = duration;
+            lastCpTotalPages = totalPages;
+            lastCpDataPages = dataPages;
+            lastCpCowPages = cowPages;
+        }
+    }
+
+    /**
+     *
+     */
+    public void onWalRecordLogged() {
+        walLoggingRate.onHit();
+    }
+
+    /**
+     * @param size Size written.
+     */
+    public void onWalBytesWritten(int size) {
+        walWritingRate.onHits(size);
+    }
+
+    /**
+     * @param nanoTime Fsync nano time.
+     */
+    public void onFsync(long nanoTime) {
+        long microseconds = nanoTime / 1_000;
+
+        walFsyncTimeDuration.onHits(microseconds);
+        walFsyncTimeNumber.onHit();
+    }
+
+    /**
+     *
+     */
+    private void resetRates() {
+        walLoggingRate = new HitRateMetrics((int)rateTimeInterval, subInts);
+        walWritingRate = new HitRateMetrics((int)rateTimeInterval, subInts);
+
+        walFsyncTimeDuration = new HitRateMetrics((int)rateTimeInterval, subInts);
+        walFsyncTimeNumber = new HitRateMetrics((int)rateTimeInterval, subInts);
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/CheckpointMetricsTracker.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/CheckpointMetricsTracker.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/CheckpointMetricsTracker.java
new file mode 100644
index 0000000..e6b88d3
--- /dev/null
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/CheckpointMetricsTracker.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.cache.database.pagemem;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+/**
+ * Tracks various checkpoint phases and stats.
+ *
+ * Assumed sequence of events:
+ * <ol>
+ *     <li>Checkpoint start</li>
+ *     <li>CP Lock wait start</li>
+ *     <li>CP mark start</li>
+ *     <li>CP Lock release</li>
+ *     <li>Pages write start</li>
+ *     <li>fsync start</li>
+ *     <li>Checkpoint end</li>
+ * </ol>
+ */
+public class CheckpointMetricsTracker {
+    /** */
+    private static final AtomicIntegerFieldUpdater<CheckpointMetricsTracker> DATA_PAGES_UPDATER =
+        AtomicIntegerFieldUpdater.newUpdater(CheckpointMetricsTracker.class, "dataPages");
+
+    /** */
+    private static final AtomicIntegerFieldUpdater<CheckpointMetricsTracker> COW_PAGES_UPDATER =
+        AtomicIntegerFieldUpdater.newUpdater(CheckpointMetricsTracker.class, "cowPages");
+
+    /** */
+    private volatile int dataPages;
+
+    /** */
+    private volatile int cowPages;
+
+    /** */
+    private long cpStart = System.currentTimeMillis();
+
+    /** */
+    private long cpLockWaitStart;
+
+    /** */
+    private long cpMarkStart;
+
+    /** */
+    private long cpLockRelease;
+
+    /** */
+    private long cpPagesWriteStart;
+
+    /** */
+    private long cpFsyncStart;
+
+    /** */
+    private long cpEnd;
+
+    /**
+     *
+     */
+    public void onCowPageWritten() {
+        COW_PAGES_UPDATER.incrementAndGet(this);
+    }
+
+    /**
+     *
+     */
+    public void onDataPageWritten() {
+        DATA_PAGES_UPDATER.incrementAndGet(this);
+    }
+
+    /**
+     * @return COW pages.
+     */
+    public int cowPagesWritten() {
+        return cowPages;
+    }
+
+    /**
+     * @return Data pages written.
+     */
+    public int dataPagesWritten() {
+        return dataPages;
+    }
+
+    /**
+     *
+     */
+    public void onLockWaitStart() {
+        cpLockWaitStart = System.currentTimeMillis();
+    }
+
+    /**
+     *
+     */
+    public void onMarkStart() {
+        cpMarkStart = System.currentTimeMillis();
+    }
+
+    /**
+     *
+     */
+    public void onLockRelease() {
+        cpLockRelease = System.currentTimeMillis();
+    }
+
+    /**
+     *
+     */
+    public void onPagesWriteStart() {
+        cpPagesWriteStart = System.currentTimeMillis();
+    }
+
+    /**
+     *
+     */
+    public void onFsyncStart() {
+        cpFsyncStart = System.currentTimeMillis();
+    }
+
+    /**
+     *
+     */
+    public void onEnd() {
+        cpEnd = System.currentTimeMillis();
+    }
+
+    /**
+     * @return Total checkpoint duration.
+     */
+    public long totalDuration() {
+        return cpEnd - cpStart;
+    }
+
+    /**
+     * @return Checkpoint lock wait duration.
+     */
+    public long lockWaitDuration() {
+        return cpMarkStart - cpLockWaitStart;
+    }
+
+    /**
+     * @return Checkpoint mark duration.
+     */
+    public long markDuration() {
+        return cpPagesWriteStart - cpMarkStart;
+    }
+
+    /**
+     * @return Checkpoint lock hold duration.
+     */
+    public long lockHoldDuration() {
+        return cpLockRelease - cpMarkStart;
+    }
+
+    /**
+     * @return Pages write duration.
+     */
+    public long pagesWriteDuration() {
+        return cpFsyncStart - cpPagesWriteStart;
+    }
+
+    /**
+     * @return Checkpoint fsync duration.
+     */
+    public long fsyncDuration() {
+        return cpEnd - cpFsyncStart;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/fffad68f/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
----------------------------------------------------------------------
diff --git a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
index ef84d83..56d1be9 100644
--- a/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
+++ b/modules/pds/src/main/java/org/apache/ignite/internal/processors/cache/database/pagemem/PageMemoryEx.java
@@ -116,10 +116,11 @@ public interface PageMemoryEx extends PageMemory {
      * @param pageId Page ID to get byte buffer for. The page ID must be present in the collection returned by
      *      the {@link #beginCheckpoint()} method call.
      * @param tmpBuf Temporary buffer to write changes into.
+     * @param tracker Checkpoint metrics tracker.
      * @return {@code True} if data were read, {@code false} otherwise (data already saved to storage).
      * @throws IgniteException If failed to obtain page data.
      */
-    @Nullable public Integer getForCheckpoint(FullPageId pageId, ByteBuffer tmpBuf);
+    @Nullable public Integer getForCheckpoint(FullPageId pageId, ByteBuffer tmpBuf, CheckpointMetricsTracker tracker);
 
     /**
      * Marks partition as invalid / outdated.