You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ir...@apache.org on 2019/03/26 20:11:41 UTC

[ignite] branch master updated: IGNITE-10997 Add new property to DataRegionMetrics: empty pages count in reuseList. - Fixes #6319.

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

irakov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new ed9a415  IGNITE-10997 Add new property to DataRegionMetrics: empty pages count in reuseList. - Fixes #6319.
ed9a415 is described below

commit ed9a41522d76e72dc3d529abf8e2dd777c230996
Author: denis-chudov <dc...@gridgain.com>
AuthorDate: Tue Mar 26 21:21:57 2019 +0300

    IGNITE-10997 Add new property to DataRegionMetrics: empty pages count in reuseList. - Fixes #6319.
    
    Signed-off-by: Ivan Rakov <ir...@apache.org>
---
 .../java/org/apache/ignite/DataRegionMetrics.java  | 12 +++
 .../apache/ignite/DataRegionMetricsProvider.java   | 39 +++++++++
 .../cache/persistence/DataRegionMetricsImpl.java   | 18 ++--
 .../persistence/DataRegionMetricsMXBeanImpl.java   |  5 ++
 .../persistence/DataRegionMetricsSnapshot.java     |  9 ++
 .../GridCacheDatabaseSharedManager.java            | 43 ++++++++++
 .../cache/persistence/GridCacheOffheapManager.java | 24 +++++-
 .../IgniteCacheDatabaseSharedManager.java          | 41 ++++++++-
 .../platform/cluster/PlatformClusterGroup.java     |  1 +
 .../ignite/mxbean/DataRegionMetricsMXBean.java     |  4 +
 .../pagemem/UsedPagesMetricAbstractTest.java       | 97 ++++++++++++++++++++++
 .../persistence/pagemem/UsedPagesMetricTest.java   | 58 +++++++++++++
 .../pagemem/UsedPagesMetricTestPersistence.java    | 91 ++++++++++++++++++++
 .../ignite/testsuites/IgnitePdsTestSuite.java      |  4 +
 .../Apache.Ignite.Core/IDataRegionMetrics.cs       | 21 +++--
 .../Apache.Ignite.Core/Impl/DataRegionMetrics.cs   | 20 +++--
 16 files changed, 463 insertions(+), 24 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java b/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java
index 88dcd16..87a9e19 100644
--- a/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java
+++ b/modules/core/src/main/java/org/apache/ignite/DataRegionMetrics.java
@@ -64,6 +64,18 @@ public interface DataRegionMetrics {
     public long getTotalAllocatedPages();
 
     /**
+     * Gets a total number of pages used for storing the data. It includes allocated pages except of empty
+     * pages that are not used yet or pages that can be reused.
+     * <p>
+     * E. g. data region contains 1000 allocated pages, and 200 pages are used to store some data, this
+     * metric shows 200 used pages. Then the data was partially deleted and 50 pages were totally freed,
+     * hence this metric should show 150 used pages.
+     *
+     * @return Total number of used pages.
+     */
+    public long getTotalUsedPages();
+
+    /**
      * Gets a total size of memory allocated in the data region. When persistence is disabled, this
      * metric shows the total size of pages in memory. When persistence is enabled, this metric shows the
      * total size of pages in memory and on disk.
diff --git a/modules/core/src/main/java/org/apache/ignite/DataRegionMetricsProvider.java b/modules/core/src/main/java/org/apache/ignite/DataRegionMetricsProvider.java
new file mode 100644
index 0000000..4a13039
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/DataRegionMetricsProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+
+/**
+ * This interface provides calculated metrics for data region.
+ */
+public interface DataRegionMetricsProvider {
+    /**
+     * Calculates free space of partially filled pages for this data region. It does not include
+     * empty data pages.
+     *
+     * @return free space in bytes.
+     */
+    public long partiallyFilledPagesFreeSpace();
+
+    /**
+     * Calculates empty data pages count for region. It counts only totally free pages that
+     * can be reused (e. g. pages that are contained in reuse bucket of free list).
+     *
+     * @return empty data pages count.
+     */
+    public long emptyDataPages();
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java
index 29cfc71..15064c8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsImpl.java
@@ -21,12 +21,12 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.LongAdder;
 import org.apache.ignite.DataRegionMetrics;
+import org.apache.ignite.DataRegionMetricsProvider;
 import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.internal.pagemem.PageMemory;
 import org.apache.ignite.internal.processors.cache.CacheGroupMetricsMXBeanImpl.GroupAllocationTracker;
 import org.apache.ignite.internal.processors.cache.ratemetrics.HitRateMetrics;
 import org.apache.ignite.internal.util.typedef.internal.U;
-import org.apache.ignite.lang.IgniteOutClosure;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable;
  */
 public class DataRegionMetricsImpl implements DataRegionMetrics, AllocatedPageTracker {
     /** */
-    private final IgniteOutClosure<Long> freeSpaceProvider;
+    private final DataRegionMetricsProvider dataRegionMetricsProvider;
 
     /** */
     private final LongAdder totalAllocatedPages = new LongAdder();
@@ -105,9 +105,10 @@ public class DataRegionMetricsImpl implements DataRegionMetrics, AllocatedPageTr
     /**
      * @param memPlcCfg DataRegionConfiguration.
      */
-    public DataRegionMetricsImpl(DataRegionConfiguration memPlcCfg, @Nullable IgniteOutClosure<Long> freeSpaceProvider) {
+    public DataRegionMetricsImpl(DataRegionConfiguration memPlcCfg,
+                                 @Nullable DataRegionMetricsProvider dataRegionMetricsProvider) {
         this.memPlcCfg = memPlcCfg;
-        this.freeSpaceProvider = freeSpaceProvider;
+        this.dataRegionMetricsProvider = dataRegionMetricsProvider;
 
         metricsEnabled = memPlcCfg.isMetricsEnabled();
 
@@ -127,6 +128,11 @@ public class DataRegionMetricsImpl implements DataRegionMetrics, AllocatedPageTr
     }
 
     /** {@inheritDoc} */
+    @Override public long getTotalUsedPages() {
+        return getTotalAllocatedPages() - dataRegionMetricsProvider.emptyDataPages();
+    }
+
+    /** {@inheritDoc} */
     @Override public long getTotalAllocatedSize() {
         assert pageMem != null;
 
@@ -161,10 +167,10 @@ public class DataRegionMetricsImpl implements DataRegionMetrics, AllocatedPageTr
 
     /** {@inheritDoc} */
     @Override public float getPagesFillFactor() {
-        if (!metricsEnabled || freeSpaceProvider == null)
+        if (!metricsEnabled || dataRegionMetricsProvider == null)
             return 0;
 
-        long freeSpace = freeSpaceProvider.apply();
+        long freeSpace = dataRegionMetricsProvider.partiallyFilledPagesFreeSpace();
 
         long totalAllocated = getPageSize() * totalAllocatedPages.longValue();
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsMXBeanImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsMXBeanImpl.java
index f837168..046593d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsMXBeanImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsMXBeanImpl.java
@@ -67,6 +67,11 @@ class DataRegionMetricsMXBeanImpl implements DataRegionMetricsMXBean {
     }
 
     /** {@inheritDoc} */
+    @Override public long getTotalUsedPages() {
+        return memMetrics.getTotalUsedPages();
+    }
+
+    /** {@inheritDoc} */
     @Override public long getTotalAllocatedSize() {
         return memMetrics.getTotalAllocatedSize();
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java
index f119419..d182192 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataRegionMetricsSnapshot.java
@@ -30,6 +30,9 @@ public class DataRegionMetricsSnapshot implements DataRegionMetrics {
     private long totalAllocatedPages;
 
     /** */
+    private long totalUsedPages;
+
+    /** */
     private long totalAllocatedSize;
 
     /** */
@@ -92,6 +95,7 @@ public class DataRegionMetricsSnapshot implements DataRegionMetrics {
     public DataRegionMetricsSnapshot(DataRegionMetrics metrics) {
         name = metrics.getName();
         totalAllocatedPages = metrics.getTotalAllocatedPages();
+        totalUsedPages = metrics.getTotalUsedPages();
         totalAllocatedSize = metrics.getTotalAllocatedSize();
         allocationRate = metrics.getAllocationRate();
         evictionRate = metrics.getEvictionRate();
@@ -124,6 +128,11 @@ public class DataRegionMetricsSnapshot implements DataRegionMetrics {
     }
 
     /** {@inheritDoc} */
+    @Override public long getTotalUsedPages() {
+        return totalUsedPages;
+    }
+
+    /** {@inheritDoc} */
     @Override public long getTotalAllocatedSize() {
         return totalAllocatedSize;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
index 4329d31..55fa785 100755
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheDatabaseSharedManager.java
@@ -67,12 +67,14 @@ import java.util.function.ToLongFunction;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+
 import org.apache.ignite.DataStorageMetrics;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteInterruptedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.DataRegionMetricsProvider;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.configuration.CacheConfiguration;
 import org.apache.ignite.configuration.CheckpointWriteOrder;
@@ -855,6 +857,7 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
     }
 
     /** {@inheritDoc} */
+    @Deprecated
     @Override protected IgniteOutClosure<Long> freeSpaceProvider(final DataRegionConfiguration dataRegCfg) {
         if (!dataRegCfg.isPersistenceEnabled())
             return super.freeSpaceProvider(dataRegCfg);
@@ -879,6 +882,46 @@ public class GridCacheDatabaseSharedManager extends IgniteCacheDatabaseSharedMan
         };
     }
 
+    /** {@inheritDoc} */
+    @Override protected DataRegionMetricsProvider dataRegionMetricsProvider(final DataRegionConfiguration dataRegCfg) {
+        if (!dataRegCfg.isPersistenceEnabled())
+            return super.dataRegionMetricsProvider(dataRegCfg);
+
+        final String dataRegName = dataRegCfg.getName();
+
+        return new DataRegionMetricsProvider() {
+            @Override public long partiallyFilledPagesFreeSpace() {
+                long freeSpace = 0L;
+
+                for (CacheGroupContext grpCtx : cctx.cache().cacheGroups()) {
+                    if (!grpCtx.dataRegion().config().getName().equals(dataRegName))
+                        continue;
+
+                    assert grpCtx.offheap() instanceof GridCacheOffheapManager;
+
+                    freeSpace += ((GridCacheOffheapManager)grpCtx.offheap()).freeSpace();
+                }
+
+                return freeSpace;
+            }
+
+            @Override public long emptyDataPages() {
+                long emptyDataPages = 0L;
+
+                for (CacheGroupContext grpCtx : cctx.cache().cacheGroups()) {
+                    if (!grpCtx.dataRegion().config().getName().equals(dataRegName))
+                        continue;
+
+                    assert grpCtx.offheap() instanceof GridCacheOffheapManager;
+
+                    emptyDataPages += ((GridCacheOffheapManager)grpCtx.offheap()).emptyDataPages();
+                }
+
+                return emptyDataPages;
+            }
+        };
+    }
+
     /**
      * Restores last valid WAL pointer and resumes logging from that pointer.
      * Re-creates metastorage if needed.
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
index ccba934..ab48540 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java
@@ -1013,7 +1013,7 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     /**
      * Calculates free space of all partition data stores - number of bytes available for use in allocated pages.
      *
-     * @return Tuple (numenator, denominator).
+     * @return free space size in bytes.
      */
     long freeSpace() {
         long freeSpace = 0;
@@ -1033,6 +1033,28 @@ public class GridCacheOffheapManager extends IgniteCacheOffheapManagerImpl imple
     }
 
     /**
+     * Calculates empty data pages of all partition data stores.
+     *
+     * @return empty data pages count.
+     */
+    long emptyDataPages() {
+        long emptyDataPages = 0;
+
+        for (CacheDataStore store : partDataStores.values()) {
+            assert store instanceof GridCacheDataStore;
+
+            CacheFreeListImpl freeList = ((GridCacheDataStore)store).freeList;
+
+            if (freeList == null)
+                continue;
+
+            emptyDataPages += freeList.emptyDataPages();
+        }
+
+        return emptyDataPages;
+    }
+
+    /**
      *
      */
     private static class WALHistoricalIterator implements IgniteHistoricalIterator {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
index 7fc70d0..ee19aed 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java
@@ -27,11 +27,13 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import javax.management.InstanceNotFoundException;
+
 import org.apache.ignite.DataRegionMetrics;
 import org.apache.ignite.DataStorageMetrics;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.DataRegionMetricsProvider;
 import org.apache.ignite.configuration.DataPageEvictionMode;
 import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
@@ -352,7 +354,8 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
         if (dfltMemPlcName == null)
             dfltMemPlcName = DFLT_DATA_REG_DEFAULT_NAME;
 
-        DataRegionMetricsImpl memMetrics = new DataRegionMetricsImpl(dataRegionCfg, freeSpaceProvider(dataRegionCfg));
+        DataRegionMetricsImpl memMetrics =
+                new DataRegionMetricsImpl(dataRegionCfg, dataRegionMetricsProvider(dataRegionCfg));
 
         DataRegion region = initMemory(dataStorageCfg, dataRegionCfg, memMetrics, trackable);
 
@@ -372,7 +375,10 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
      *
      * @param dataRegCfg Data region configuration.
      * @return Closure.
+     *
+     * @Deprecated use {@link #dataRegionMetricsProvider(DataRegionConfiguration)} instead.
      */
+    @Deprecated
     protected IgniteOutClosure<Long> freeSpaceProvider(final DataRegionConfiguration dataRegCfg) {
         final String dataRegName = dataRegCfg.getName();
 
@@ -395,6 +401,39 @@ public class IgniteCacheDatabaseSharedManager extends GridCacheSharedManagerAdap
     }
 
     /**
+     * Provide that can be used to compute some metrics for provided data region.
+     *
+     * @param dataRegCfg Data region configuration.
+     * @return DataRegionMetricsProvider.
+     */
+    protected DataRegionMetricsProvider dataRegionMetricsProvider(final DataRegionConfiguration dataRegCfg) {
+        final String dataRegName = dataRegCfg.getName();
+
+        return new DataRegionMetricsProvider() {
+            private CacheFreeListImpl freeList;
+
+            private CacheFreeListImpl getFreeList() {
+                if (freeList == null)
+                    freeList = freeListMap.get(dataRegName);
+
+                return freeList;
+            }
+
+            @Override public long partiallyFilledPagesFreeSpace() {
+                CacheFreeListImpl freeList0 = getFreeList();
+
+                return freeList0 == null ? 0L : freeList0.freeSpace();
+            }
+
+            @Override public long emptyDataPages() {
+                CacheFreeListImpl freeList0 = getFreeList();
+
+                return freeList0 == null ? 0L : freeList0.emptyDataPages();
+            }
+        };
+    }
+
+    /**
      * @param memPlcsCfgs User-defined data region configurations.
      */
     private boolean hasCustomDefaultDataRegion(DataRegionConfiguration[] memPlcsCfgs) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cluster/PlatformClusterGroup.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cluster/PlatformClusterGroup.java
index f1c70aa..097833d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cluster/PlatformClusterGroup.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/cluster/PlatformClusterGroup.java
@@ -549,6 +549,7 @@ public class PlatformClusterGroup extends PlatformAbstractTarget {
 
         writer.writeString(metrics.getName());
         writer.writeLong(metrics.getTotalAllocatedPages());
+        writer.writeLong(metrics.getTotalUsedPages());
         writer.writeLong(metrics.getTotalAllocatedSize());
         writer.writeFloat(metrics.getAllocationRate());
         writer.writeFloat(metrics.getEvictionRate());
diff --git a/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java b/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java
index 9bb1af6..949daea 100644
--- a/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java
+++ b/modules/core/src/main/java/org/apache/ignite/mxbean/DataRegionMetricsMXBean.java
@@ -58,6 +58,10 @@ public interface DataRegionMetricsMXBean extends DataRegionMetrics {
     @Override public long getTotalAllocatedPages();
 
     /** {@inheritDoc} */
+    @MXBeanDescription("Total number of used pages.")
+    @Override public long getTotalUsedPages();
+
+    /** {@inheritDoc} */
     @MXBeanDescription("Allocation rate (pages per second) averaged across rateTimeInternal.")
     @Override public float getAllocationRate();
 
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricAbstractTest.java
new file mode 100644
index 0000000..1a5032a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricAbstractTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.pagemem;
+
+import org.apache.ignite.DataRegionMetrics;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+/**
+ * Abstract class for TotalUsedPages metric tests.
+ */
+public class UsedPagesMetricAbstractTest extends GridCommonAbstractTest {
+    /** */
+    public static final String MY_CACHE = "myCache";
+
+    /** */
+    public static final String DEFAULT_DATA_REGION = "default";
+
+    /** */
+    public static final long LARGE_PRIME = 4294967291L;
+
+    /**
+     * Common scenario for used pages metric test
+     *
+     * @param nodeCnt count of ignite nodes
+     * @param iterations count of check iterations
+     * @param storedEntriesCnt count of key-value pairs in each iteration
+     * @param valSize size of value in bytes
+     * @throws Exception if failed
+     */
+    protected void testFillAndRemove(
+        int nodeCnt,
+        int iterations,
+        int storedEntriesCnt,
+        int valSize
+    ) throws Exception {
+        Ignite node = startGrids(nodeCnt);
+
+        node.cluster().active(true);
+
+        IgniteCache<Long, Object> cache = node.getOrCreateCache(MY_CACHE);
+
+        long beforeFill;
+        long afterFill;
+        long afterRmv;
+
+        for (int iter = 0; iter < iterations; iter++) {
+
+            DataRegionMetrics metricsBeforeFill = node.dataRegionMetrics(DEFAULT_DATA_REGION);
+
+            beforeFill = metricsBeforeFill.getTotalUsedPages();
+
+            for (int i = 0; i < storedEntriesCnt; i++) {
+                final long res = (i * i) % LARGE_PRIME;
+
+                cache.put(res, new byte[valSize]);
+            }
+
+            DataRegionMetrics metricsAfterFill = node.dataRegionMetrics(DEFAULT_DATA_REGION);
+
+            afterFill = metricsAfterFill.getTotalUsedPages();
+
+            for (int i = 0; i < storedEntriesCnt; i++) {
+                final long res = (i * i) % LARGE_PRIME;
+
+                cache.remove(res);
+            }
+
+            DataRegionMetrics metricsAfterRmv = node.dataRegionMetrics(DEFAULT_DATA_REGION);
+
+            afterRmv = metricsAfterRmv.getTotalUsedPages();
+
+            log.info(String.format("Used pages count before fill: %d", beforeFill));
+            log.info(String.format("Used pages count after fill: %d", afterFill));
+            log.info(String.format("Used pages count after remove: %d\n", afterRmv));
+
+            assertTrue(afterFill > beforeFill);
+            assertTrue(afterRmv < afterFill);
+            assertTrue(afterRmv >= beforeFill);
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricTest.java
new file mode 100644
index 0000000..279ab80
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.pagemem;
+
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.junit.Test;
+
+/**
+ * TotalUsedPages metric in-memory tests.
+ */
+public class UsedPagesMetricTest extends UsedPagesMetricAbstractTest {
+    /** */
+    public static final int NODES = 2;
+
+    /** */
+    public static final int ITERATIONS = 3;
+
+    /** */
+    public static final int STORED_ENTRIES_COUNT = 50000;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setDataStorageConfiguration(
+                new DataStorageConfiguration().setDefaultDataRegionConfiguration(
+                    new DataRegionConfiguration()
+                        .setInitialSize(100 * 1024L * 1024L)
+                        .setMaxSize(500 * 1024L * 1024L)
+                        .setMetricsEnabled(true)
+                ));
+    }
+
+    /**
+     * Tests that totalUsedPages metric for in-memory data region behaves correctly.
+     *
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testFillAndRemoveInMemory() throws Exception {
+        testFillAndRemove(NODES, ITERATIONS, STORED_ENTRIES_COUNT, 256);
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricTestPersistence.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricTestPersistence.java
new file mode 100644
index 0000000..9fabc4b
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/pagemem/UsedPagesMetricTestPersistence.java
@@ -0,0 +1,91 @@
+/*
+ * 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.pagemem;
+
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * TotalUsedPages metric persistence tests.
+ */
+public class UsedPagesMetricTestPersistence extends UsedPagesMetricAbstractTest {
+    /** */
+    public static final int NODES = 1;
+
+    /** */
+    public static final int ITERATIONS = 1;
+
+    /** */
+    public static final int STORED_ENTRIES_COUNT = 50000;
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        return super.getConfiguration(igniteInstanceName)
+            .setDataStorageConfiguration(
+                new DataStorageConfiguration().setDefaultDataRegionConfiguration(
+                    new DataRegionConfiguration()
+                        .setPersistenceEnabled(true)
+                        .setInitialSize(100 * 1024L * 1024L)
+                        .setMaxSize(500 * 1024L * 1024L)
+                        .setMetricsEnabled(true)
+                ));
+    }
+
+    /**
+     *
+     */
+    @Before
+    public void cleanBeforeStart() throws Exception {
+        cleanPersistenceDir();
+    }
+
+    /**
+     *
+     */
+    @After
+    public void stopAndClean() throws Exception {
+        stopAllGrids();
+
+        cleanPersistenceDir();
+    }
+
+    /**
+     * Tests that totalUsedPages metric for data region with enabled persistence
+     * and pages being rotated to disk behaves correctly.
+     *
+     * @throws Exception if failed
+     */
+    @Test
+    public void testFillAndRemovePagesRotation() throws Exception {
+        testFillAndRemove(NODES, ITERATIONS, STORED_ENTRIES_COUNT, 8192);
+    }
+
+    /**
+     * Tests that totalUsedPages metric for data region with enabled persistence
+     * and pages that are not being rotated to disk behaves correctly.
+     *
+     * @throws Exception if failed
+     */
+    @Test
+    public void testFillAndRemoveWithoutPagesRotation() throws Exception {
+        testFillAndRemove(NODES, ITERATIONS, STORED_ENTRIES_COUNT, 256);
+    }
+}
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 a58b2dd..5a052bb 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
@@ -45,6 +45,8 @@ 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.pagemem.UsedPagesMetricTest;
+import org.apache.ignite.internal.processors.cache.persistence.pagemem.UsedPagesMetricTestPersistence;
 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;
@@ -101,6 +103,8 @@ public class IgnitePdsTestSuite {
 
         // Metrics
         GridTestUtils.addTestIfNeeded(suite, FillFactorMetricTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, UsedPagesMetricTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, UsedPagesMetricTestPersistence.class, ignoredTests);
 
         // WAL delta consistency
         GridTestUtils.addTestIfNeeded(suite, CpTriggeredWalDeltaConsistencyTest.class, ignoredTests);
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/IDataRegionMetrics.cs b/modules/platforms/dotnet/Apache.Ignite.Core/IDataRegionMetrics.cs
index cd28fa3..107d248 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/IDataRegionMetrics.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/IDataRegionMetrics.cs
@@ -35,6 +35,11 @@ namespace Apache.Ignite.Core
         long TotalAllocatedPages { get; }
 
         /// <summary>
+        /// Gets a total number of pages used for storing the data.
+        /// </summary>
+        long TotalUsedPages { get; }
+
+        /// <summary>
         /// Gets the size of allocated pages in bytes.
         /// </summary>
         long TotalAllocatedSize { get; }
@@ -83,14 +88,14 @@ namespace Apache.Ignite.Core
         /// Gets the size of pages loaded to RAM in bytes.
         /// </summary>
         long PhysicalMemorySize { get; }
-        
+
         /// <summary>
         /// Gets checkpoint buffer size in pages.
         /// Deprecated, always returns 0. Use <see cref="UsedCheckpointBufferPages"/> instead.
         /// </summary>
         [Obsolete("Deprecated, always returns 0. Use UsedCheckpointBufferPages instead.")]
         long CheckpointBufferPages { get; }
-        
+
         /// <summary>
         /// Gets checkpoint buffer size in bytes.
         /// </summary>
@@ -100,7 +105,7 @@ namespace Apache.Ignite.Core
         /// Gets used checkpoint buffer size in pages.
         /// </summary>
         long UsedCheckpointBufferPages { get; }
-        
+
         /// <summary>
         /// Gets used checkpoint buffer size in bytes.
         /// </summary>
@@ -110,27 +115,27 @@ namespace Apache.Ignite.Core
         /// Gets memory page size in bytes.
         /// </summary>
         int PageSize { get; }
-        
+
         /// <summary>
         /// Gets the number of read pages from last restart.
         /// </summary>
         long PagesRead { get; }
-        
+
         /// <summary>
         ///  Gets the number of written pages from last restart.
         /// </summary>
         long PagesWritten  { get; }
-        
+
         /// <summary>
         /// Gets the number of replaced pages from last restart.
         /// </summary>
         long PagesReplaced  { get; }
-        
+
         /// <summary>
         /// Gets total offheap size in bytes.
         /// </summary>
         long OffHeapSize { get; }
-        
+
         /// <summary>
         /// Gets total used offheap size in bytes.
         /// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/DataRegionMetrics.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/DataRegionMetrics.cs
index 4cc4c0d..b121b12 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/DataRegionMetrics.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/DataRegionMetrics.cs
@@ -34,6 +34,7 @@ namespace Apache.Ignite.Core.Impl
 
             Name = reader.ReadString();
             TotalAllocatedPages = reader.ReadLong();
+            TotalUsedPages = reader.ReadLong();
             TotalAllocatedSize = reader.ReadLong();
             AllocationRate = reader.ReadFloat();
             EvictionRate = reader.ReadFloat();
@@ -62,11 +63,14 @@ namespace Apache.Ignite.Core.Impl
         public long TotalAllocatedPages { get; private set; }
 
         /** <inheritdoc /> */
+        public long TotalUsedPages { get; private set; }
+
+        /** <inheritdoc /> */
         public long TotalAllocatedSize { get; private set; }
 
         /** <inheritdoc /> */
         public float AllocationRate { get; private set; }
-        
+
         /** <inheritdoc /> */
         public float EvictionRate { get; private set; }
 
@@ -90,8 +94,8 @@ namespace Apache.Ignite.Core.Impl
 
         /** <inheritdoc /> */
         public long PhysicalMemorySize { get; private set; }
-        
-        /** <inheritdoc /> */ 
+
+        /** <inheritdoc /> */
         public long CheckpointBufferPages
         {
             get
@@ -111,19 +115,19 @@ namespace Apache.Ignite.Core.Impl
 
         /** <inheritdoc /> */
         public int PageSize { get; private set; }
-        
+
         /** <inheritdoc /> */
         public long PagesRead { get; private set; }
-        
+
         /** <inheritdoc /> */
         public long PagesWritten { get; private set; }
-        
+
         /** <inheritdoc /> */
         public long PagesReplaced { get; private set; }
-        
+
         /** <inheritdoc /> */
         public long OffHeapSize { get; private set; }
-        
+
         /** <inheritdoc /> */
         public long OffheapUsedSize { get; private set; }
     }