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

ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 61d0735110e IGNITE-26600 Add TotalAllocatedSize table metric (#6692)
61d0735110e is described below

commit 61d0735110ef53b9622f61bfc417b0d7fb63322f
Author: Ivan Bessonov <[email protected]>
AuthorDate: Thu Oct 9 11:58:39 2025 +0300

    IGNITE-26600 Add TotalAllocatedSize table metric (#6692)
---
 .../internal/storage/engine/StorageEngine.java     | 11 +++
 .../storage/engine/StorageTableDescriptor.java     |  7 ++
 .../metrics/StorageEngineTablesMetricSource.java   | 86 ++++++++++++++++++++++
 .../internal/storage/util/MvPartitionStorages.java | 13 ++++
 .../storage/engine/AbstractStorageEngineTest.java  | 21 ++++++
 .../pagememory/PersistentPageMemoryDataRegion.java | 40 ++++++++--
 .../PersistentPageMemoryStorageEngine.java         | 10 +++
 ...PageMemoryStorageEngineConfigurationSchema.java |  5 +-
 .../mv/PersistentPageMemoryMvPartitionStorage.java |  7 ++
 .../PersistentPageMemoryStorageEngineTest.java     | 51 +++++++++++++
 ...entPageMemoryMvTableStorageConcurrencyTest.java |  5 --
 .../internal/table/distributed/TableManager.java   | 51 ++++++++++---
 .../internal/table/metrics/TableMetricSource.java  |  9 ++-
 13 files changed, 292 insertions(+), 24 deletions(-)

diff --git 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
index af931962128..7c32e1a9786 100644
--- 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
+++ 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
@@ -23,6 +23,7 @@ import static 
org.apache.ignite.internal.util.IgniteUtils.getTotalMemoryAvailabl
 import java.util.Set;
 import org.apache.ignite.internal.storage.StorageException;
 import org.apache.ignite.internal.storage.index.StorageIndexDescriptorSupplier;
+import 
org.apache.ignite.internal.storage.metrics.StorageEngineTablesMetricSource;
 
 /**
  * General storage engine interface.
@@ -73,6 +74,16 @@ public interface StorageEngine {
      */
     void destroyMvTable(int tableId);
 
+    /**
+     * Adds metrics related to the table to the given metric source.
+     *
+     * @param tableDescriptor Table descriptor.
+     * @param metricSource Metric source.
+     */
+    default void addTableMetrics(StorageTableDescriptor tableDescriptor, 
StorageEngineTablesMetricSource metricSource) {
+        // No-op.
+    }
+
     /**
      * Default size of a data region, maximum between 256 MiB and 20% of the 
total physical memory.
      *
diff --git 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageTableDescriptor.java
 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageTableDescriptor.java
index 265b5c42588..aed8c77e5c7 100644
--- 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageTableDescriptor.java
+++ 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageTableDescriptor.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.storage.engine;
 
+import org.apache.ignite.internal.tostring.S;
+
 /**
  * Table descriptor.
  */
@@ -60,4 +62,9 @@ public class StorageTableDescriptor {
     public String getStorageProfile() {
         return storageProfile;
     }
+
+    @Override
+    public String toString() {
+        return S.toString(StorageTableDescriptor.class, this);
+    }
 }
diff --git 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/metrics/StorageEngineTablesMetricSource.java
 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/metrics/StorageEngineTablesMetricSource.java
new file mode 100644
index 00000000000..d9b68a35a48
--- /dev/null
+++ 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/metrics/StorageEngineTablesMetricSource.java
@@ -0,0 +1,86 @@
+/*
+ * 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.storage.metrics;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.ignite.internal.metrics.AbstractMetricSource;
+import org.apache.ignite.internal.metrics.Metric;
+import org.apache.ignite.table.QualifiedName;
+
+/**
+ * Metric source for storage engine metrics related to a specific table.
+ */
+public class StorageEngineTablesMetricSource extends 
AbstractMetricSource<StorageEngineTablesMetricSource.Holder> {
+    public static final String METRIC_GROUP = "storage";
+
+    private final Set<Metric> extraMetrics = new HashSet<>();
+
+    /**
+     * Creates a new metric source for the given storage engine and table, 
with a name {@code storage.<engine>.tables.<tableName>} and
+     * group {@value METRIC_GROUP}.
+     *
+     * @param engine Storage engine name.
+     * @param tableName Table qualified name.
+     */
+    public StorageEngineTablesMetricSource(String engine, QualifiedName 
tableName) {
+        super(sourceName(engine, tableName), "\"" + engine + "\" storage 
engine metrics for the specific table.", METRIC_GROUP);
+    }
+
+    /**
+     * Returns a metric source name for the given storage engine and table.
+     */
+    public static String sourceName(String engine, QualifiedName tableName) {
+        return METRIC_GROUP + '.' + engine + ".tables." + 
tableName.toCanonicalForm();
+    }
+
+    /**
+     * Adds a custom metric to the source.
+     *
+     * <p>Note: This method cannot be called when the source is enabled.
+     *
+     * @param metric Metric to add.
+     */
+    public void addMetric(Metric metric) {
+        assert holder() == null : "Cannot add metrics when source is enabled";
+
+        extraMetrics.add(metric);
+    }
+
+    @Override
+    protected Holder createHolder() {
+        return new Holder(extraMetrics);
+    }
+
+    /**
+     * Holder for the storage engine table metrics.
+     */
+    public static class Holder implements AbstractMetricSource.Holder<Holder> {
+        private final List<Metric> metrics;
+
+        protected Holder(Set<Metric> extraMetrics) {
+            metrics = List.copyOf(extraMetrics);
+        }
+
+        @Override
+        public Iterable<Metric> metrics() {
+            return metrics;
+        }
+    }
+}
diff --git 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/util/MvPartitionStorages.java
 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/util/MvPartitionStorages.java
index 9ed8b1db1b9..3079470b9cb 100644
--- 
a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/util/MvPartitionStorages.java
+++ 
b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/util/MvPartitionStorages.java
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
 import java.util.function.Function;
 import java.util.function.IntFunction;
 import java.util.stream.IntStream;
+import java.util.stream.Stream;
 import org.apache.ignite.internal.lang.IgniteStringFormatter;
 import org.apache.ignite.internal.storage.MvPartitionStorage;
 import org.apache.ignite.internal.storage.StorageException;
@@ -454,6 +455,18 @@ public class MvPartitionStorages<T extends 
MvPartitionStorage> {
         return list;
     }
 
+    /**
+     * Returns a stream of all existing storages.
+     *
+     * <p>Note: this method may produce races when a rebalance is happening 
concurrently as the underlying storage array may change.
+     * The callers of this method should resolve these races themselves.
+     */
+    public Stream<T> stream() {
+        return IntStream.range(0, storageByPartitionId.length())
+                .mapToObj(storageByPartitionId::get)
+                .filter(Objects::nonNull);
+    }
+
     /**
      * Returns all storages for closing or destroying after completion of 
operations for all storages.
      *
diff --git 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/engine/AbstractStorageEngineTest.java
 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/engine/AbstractStorageEngineTest.java
index d034daf27e0..af4cd6e3efd 100644
--- 
a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/engine/AbstractStorageEngineTest.java
+++ 
b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/engine/AbstractStorageEngineTest.java
@@ -17,13 +17,20 @@
 
 package org.apache.ignite.internal.storage.engine;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.emptyIterable;
+import static org.hamcrest.Matchers.is;
 import static org.mockito.Mockito.mock;
 
+import org.apache.ignite.internal.catalog.CatalogService;
 import org.apache.ignite.internal.components.LogSyncer;
 import org.apache.ignite.internal.storage.AbstractMvTableStorageTest;
 import org.apache.ignite.internal.storage.BaseMvStoragesTest;
+import 
org.apache.ignite.internal.storage.metrics.StorageEngineTablesMetricSource;
+import org.apache.ignite.table.QualifiedName;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
 
@@ -57,4 +64,18 @@ public abstract class AbstractStorageEngineTest extends 
BaseMvStoragesTest {
      * directory.
      */
     protected abstract StorageEngine createEngine();
+
+    @Test
+    protected void tableMetrics() {
+        var tableDescriptor = new StorageTableDescriptor(10, 1, 
CatalogService.DEFAULT_STORAGE_PROFILE);
+        storageEngine.createMvTable(tableDescriptor, indexId -> null);
+
+        QualifiedName tableName = 
QualifiedName.of(QualifiedName.DEFAULT_SCHEMA_NAME, "foo");
+
+        StorageEngineTablesMetricSource metricSource = new 
StorageEngineTablesMetricSource(storageEngine.name(), tableName);
+        storageEngine.addTableMetrics(tableDescriptor, metricSource);
+
+        metricSource.enable();
+        assertThat(metricSource.holder().metrics(), is(emptyIterable()));
+    }
 }
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
index 45b54e3dc78..afedfe68ecc 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryDataRegion.java
@@ -28,8 +28,8 @@ import static org.apache.ignite.internal.util.Constants.MiB;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Function;
@@ -59,6 +59,8 @@ import 
org.apache.ignite.internal.pagememory.persistence.throttling.TargetRatioP
 import 
org.apache.ignite.internal.pagememory.persistence.throttling.ThrottlingType;
 import org.apache.ignite.internal.storage.StorageException;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
+import org.apache.ignite.internal.storage.engine.StorageTableDescriptor;
+import 
org.apache.ignite.internal.storage.metrics.StorageEngineTablesMetricSource;
 import 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PersistentPageMemoryProfileConfiguration;
 import 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PersistentPageMemoryProfileView;
 import 
org.apache.ignite.internal.storage.pagememory.mv.PersistentPageMemoryMvPartitionStorage;
@@ -111,7 +113,7 @@ public class PersistentPageMemoryDataRegion implements 
DataRegion<PersistentPage
 
     private final PersistentPageMemoryMetricSource metricSource;
 
-    private final Collection<PersistentPageMemoryTableStorage> tableStorages = 
ConcurrentHashMap.newKeySet();
+    private final ConcurrentMap<Integer, PersistentPageMemoryTableStorage> 
tableStorages = new ConcurrentHashMap<>();
 
     /**
      * Constructor.
@@ -437,13 +439,13 @@ public class PersistentPageMemoryDataRegion implements 
DataRegion<PersistentPage
     /** Adds a table storage to the data region. */
     @VisibleForTesting
     public void addTableStorage(PersistentPageMemoryTableStorage tableStorage) 
{
-        boolean add = tableStorages.add(tableStorage);
+        PersistentPageMemoryTableStorage old = 
tableStorages.put(tableStorage.getTableId(), tableStorage);
 
-        assert add : tableStorage.getTableId();
+        assert old == null : "Table storage for tableId=" + 
tableStorage.getTableId() + " already exists";
     }
 
     void removeTableStorage(PersistentPageMemoryTableStorage tableStorage) {
-        tableStorages.remove(tableStorage);
+        tableStorages.remove(tableStorage.getTableId());
     }
 
     private void initMetrics() {
@@ -459,10 +461,34 @@ public class PersistentPageMemoryDataRegion implements 
DataRegion<PersistentPage
         ));
     }
 
+    /**
+     * Registers region-specific metrics for the given table.
+     *
+     * @param tableDescriptor Table descriptor.
+     * @param metricSource Metric source for registering metrics.
+     */
+    void addTableMetrics(StorageTableDescriptor tableDescriptor, 
StorageEngineTablesMetricSource metricSource) {
+        PersistentPageMemoryTableStorage tableStorage = 
tableStorages.get(tableDescriptor.getId());
+
+        assert tableStorage != null : "Adding metrics for a non-existent 
table: " + tableDescriptor;
+
+        metricSource.addMetric(new LongGauge(
+                "TotalAllocatedSize",
+                "Total size of all pages allocated by '" + ENGINE_NAME + "' 
storage engine for a given table, in bytes.",
+                () -> {
+                    long totalPages = tableStorage.mvPartitionStorages.stream()
+                            
.mapToLong(PersistentPageMemoryMvPartitionStorage::pageCount)
+                            .sum();
+
+                    return pageSize * totalPages;
+                }
+        ));
+    }
+
     private long totalAllocatedPagesSizeOnDiskInBytes() {
         long pageCount = 0;
 
-        for (PersistentPageMemoryTableStorage tableStorage : tableStorages) {
+        for (PersistentPageMemoryTableStorage tableStorage : 
tableStorages.values()) {
             for (PersistentPageMemoryMvPartitionStorage partitionStorage : 
tableStorage.mvPartitionStorages.getAll()) {
                 pageCount += 
allocatedPageCountOnDisk(tableStorage.getTableId(), 
partitionStorage.partitionId());
             }
@@ -474,7 +500,7 @@ public class PersistentPageMemoryDataRegion implements 
DataRegion<PersistentPage
     private long totalNonEmptyAllocatedPagesSizeOnDiskInBytes() {
         long pageCount = 0;
 
-        for (PersistentPageMemoryTableStorage tableStorage : tableStorages) {
+        for (PersistentPageMemoryTableStorage tableStorage : 
tableStorages.values()) {
             for (PersistentPageMemoryMvPartitionStorage partitionStorage : 
tableStorage.mvPartitionStorages.getAll()) {
                 pageCount += 
allocatedPageCountOnDisk(tableStorage.getTableId(), 
partitionStorage.partitionId());
 
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
index 8d257645863..767c153b92a 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
@@ -57,6 +57,7 @@ import 
org.apache.ignite.internal.storage.configurations.StorageProfileView;
 import org.apache.ignite.internal.storage.engine.MvTableStorage;
 import org.apache.ignite.internal.storage.engine.StorageTableDescriptor;
 import org.apache.ignite.internal.storage.index.StorageIndexDescriptorSupplier;
+import 
org.apache.ignite.internal.storage.metrics.StorageEngineTablesMetricSource;
 import 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PageMemoryCheckpointConfiguration;
 import 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PersistentPageMemoryProfileConfiguration;
 import 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PersistentPageMemoryProfileView;
@@ -332,6 +333,15 @@ public class PersistentPageMemoryStorageEngine extends 
AbstractPageMemoryStorage
         }
     }
 
+    @Override
+    public void addTableMetrics(StorageTableDescriptor tableDescriptor, 
StorageEngineTablesMetricSource metricSource) {
+        PersistentPageMemoryDataRegion region = 
regions.get(tableDescriptor.getStorageProfile());
+
+        assert region != null : "Adding metrics to the table with non-existent 
data region. [tableDescriptor=" + tableDescriptor + ']';
+
+        region.addTableMetrics(tableDescriptor, metricSource);
+    }
+
     /**
      * Returns checkpoint manager, {@code null} if engine not started.
      */
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/PersistentPageMemoryStorageEngineConfigurationSchema.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/PersistentPageMemoryStorageEngineConfigurationSchema.java
index 6d1ac110398..893f7b50bf4 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/PersistentPageMemoryStorageEngineConfigurationSchema.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/PersistentPageMemoryStorageEngineConfigurationSchema.java
@@ -31,12 +31,15 @@ import 
org.apache.ignite.internal.storage.pagememory.PersistentPageMemoryStorage
  */
 @Config
 public class PersistentPageMemoryStorageEngineConfigurationSchema {
+    /** Default page size for the engine. */
+    public static final int DEFAULT_PAGE_SIZE = 16 * 1024;
+
     @Immutable
     @PowerOfTwo
     @Range(min = 1024, max = 16 * 1024)
     @Value(hasDefault = true)
     @PublicName(legacyNames = "pageSize")
-    public int pageSizeBytes = 16 * 1024;
+    public int pageSizeBytes = DEFAULT_PAGE_SIZE;
 
     /* Checkpoint configuration for persistent data regions. */
     @ConfigValue
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
index 59626b843f4..4d18553fde2 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
@@ -555,6 +555,13 @@ public class PersistentPageMemoryMvPartitionStorage 
extends AbstractPageMemoryMv
         return meta.estimatedSize();
     }
 
+    /**
+     * Returns a total number of allocated pages for the storage, including 
pages that are not in the memory currently.
+     */
+    public int pageCount() {
+        return meta.pageCount();
+    }
+
     @Override
     public void incrementEstimatedSize() {
         updateMeta((lastCheckpointId, meta) -> 
meta.incrementEstimatedSize(lastCheckpointId));
diff --git 
a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/engine/PersistentPageMemoryStorageEngineTest.java
 
b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/engine/PersistentPageMemoryStorageEngineTest.java
index 49742c22cd4..ffc41c2a40b 100644
--- 
a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/engine/PersistentPageMemoryStorageEngineTest.java
+++ 
b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/engine/PersistentPageMemoryStorageEngineTest.java
@@ -17,27 +17,41 @@
 
 package org.apache.ignite.internal.storage.pagememory.engine;
 
+import static 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PersistentPageMemoryStorageEngineConfigurationSchema.DEFAULT_PAGE_SIZE;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mock;
 
 import java.nio.file.Path;
+import java.util.Iterator;
 import java.util.concurrent.ExecutorService;
+import org.apache.ignite.internal.catalog.CatalogService;
 import 
org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
 import org.apache.ignite.internal.failure.FailureManager;
+import org.apache.ignite.internal.metrics.LongMetric;
+import org.apache.ignite.internal.metrics.Metric;
 import org.apache.ignite.internal.metrics.MetricManager;
 import org.apache.ignite.internal.pagememory.io.PageIoRegistry;
 import org.apache.ignite.internal.storage.configurations.StorageConfiguration;
 import org.apache.ignite.internal.storage.configurations.StorageProfileView;
 import 
org.apache.ignite.internal.storage.engine.AbstractPersistentStorageEngineTest;
 import org.apache.ignite.internal.storage.engine.AbstractStorageEngineTest;
+import org.apache.ignite.internal.storage.engine.MvTableStorage;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
+import org.apache.ignite.internal.storage.engine.StorageTableDescriptor;
+import 
org.apache.ignite.internal.storage.metrics.StorageEngineTablesMetricSource;
 import 
org.apache.ignite.internal.storage.pagememory.PersistentPageMemoryStorageEngine;
 import 
org.apache.ignite.internal.storage.pagememory.configuration.schema.PersistentPageMemoryProfileView;
 import org.apache.ignite.internal.testframework.ExecutorServiceExtension;
 import org.apache.ignite.internal.testframework.InjectExecutorService;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
+import org.apache.ignite.table.QualifiedName;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
@@ -98,4 +112,41 @@ public class PersistentPageMemoryStorageEngineTest extends 
AbstractPersistentSto
     protected void persistTableDestructionIfNeeded() {
         // No-op as table destruction is durable for this engine.
     }
+
+    @Test
+    @Override
+    protected void tableMetrics() {
+        var engine = (PersistentPageMemoryStorageEngine) storageEngine;
+
+        var tableDescriptor = new StorageTableDescriptor(10, 1, 
CatalogService.DEFAULT_STORAGE_PROFILE);
+        MvTableStorage table = engine.createMvTable(tableDescriptor, indexId 
-> null);
+
+        QualifiedName tableName = 
QualifiedName.of(QualifiedName.DEFAULT_SCHEMA_NAME, "foo");
+
+        StorageEngineTablesMetricSource metricSource = new 
StorageEngineTablesMetricSource(storageEngine.name(), tableName);
+        storageEngine.addTableMetrics(tableDescriptor, metricSource);
+
+        metricSource.enable();
+        Iterator<Metric> metrics = metricSource.holder().metrics().iterator();
+        assertTrue(metrics.hasNext());
+
+        Metric metric = metrics.next();
+        assertThat(metric.name(), is("TotalAllocatedSize"));
+        assertThat(metric, is(instanceOf(LongMetric.class)));
+
+        assertFalse(metrics.hasNext());
+
+        LongMetric totalAllocatedSize = (LongMetric) metric;
+        assertEquals(0, totalAllocatedSize.value());
+
+        var otherTableDescriptor = new StorageTableDescriptor(20, 1, 
CatalogService.DEFAULT_STORAGE_PROFILE);
+        MvTableStorage otherTable = engine.createMvTable(otherTableDescriptor, 
indexId -> null);
+
+        otherTable.createMvPartition(0);
+        assertEquals(0, totalAllocatedSize.value());
+
+        table.createMvPartition(0);
+        assertThat(totalAllocatedSize.value(), is(greaterThan(0L)));
+        assertEquals(0, totalAllocatedSize.value() % DEFAULT_PAGE_SIZE);
+    }
 }
diff --git 
a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvTableStorageConcurrencyTest.java
 
b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvTableStorageConcurrencyTest.java
index 89313528847..85ab3220166 100644
--- 
a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvTableStorageConcurrencyTest.java
+++ 
b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvTableStorageConcurrencyTest.java
@@ -75,11 +75,6 @@ class PersistentPageMemoryMvTableStorageConcurrencyTest 
extends AbstractMvTableS
 
         engine.start();
 
-        tableStorage = engine.createMvTable(
-                new StorageTableDescriptor(1, DEFAULT_PARTITION_COUNT, 
DEFAULT_STORAGE_PROFILE),
-                indexDescriptorSupplier
-        );
-
         initialize();
     }
 
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
index eb7c7e92c97..1da94c5d46c 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
@@ -211,6 +211,7 @@ import 
org.apache.ignite.internal.storage.StorageDestroyedException;
 import org.apache.ignite.internal.storage.engine.MvTableStorage;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
 import org.apache.ignite.internal.storage.engine.StorageTableDescriptor;
+import 
org.apache.ignite.internal.storage.metrics.StorageEngineTablesMetricSource;
 import org.apache.ignite.internal.table.IgniteTablesInternal;
 import org.apache.ignite.internal.table.InternalTable;
 import org.apache.ignite.internal.table.LongPriorityQueue;
@@ -1840,7 +1841,7 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
                 () -> txCfg.value().readWriteTimeoutMillis(),
                 () -> txCfg.value().readOnlyTimeoutMillis(),
                 nodeProperties.colocationEnabled(),
-                createAndRegisterMetricsSource(tableName)
+                
createAndRegisterMetricsSource(tableStorage.getTableDescriptor(), tableName)
         );
 
         return new TableImpl(
@@ -3637,29 +3638,59 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
         }
     }
 
-    private TableMetricSource createAndRegisterMetricsSource(QualifiedName 
tableName) {
+    private TableMetricSource 
createAndRegisterMetricsSource(StorageTableDescriptor tableDescriptor, 
QualifiedName tableName) {
+        StorageEngine engine = 
dataStorageMgr.engineByStorageProfile(tableDescriptor.getStorageProfile());
+
+        // Engine can be null sometimes, see "TableManager.createTableStorage".
+        if (engine != null) {
+            StorageEngineTablesMetricSource engineMetricSource = new 
StorageEngineTablesMetricSource(engine.name(), tableName);
+
+            engine.addTableMetrics(tableDescriptor, engineMetricSource);
+
+            try {
+                metricManager.registerSource(engineMetricSource);
+                metricManager.enable(engineMetricSource);
+            } catch (Exception e) {
+                String message = "Failed to register storage engine metrics 
source for table [id={}, name={}].";
+                LOG.warn(message, e, tableDescriptor.getId(), tableName);
+            }
+        }
+
         TableMetricSource source = new TableMetricSource(tableName);
 
         try {
             metricManager.registerSource(source);
             metricManager.enable(source);
         } catch (Exception e) {
-            LOG.warn("Failed to register metrics source for table [name={}].", 
e, source.qualifiedTableName());
+            LOG.warn("Failed to register metrics source for table [id={}, 
name={}].", e, tableDescriptor.getId(), tableName);
         }
 
         return source;
     }
 
     private void unregisterMetricsSource(int tableId) {
-        try {
-            TableViewInternal table = startedTables.get(tableId);
-            if (table == null) {
-                return;
-            }
+        TableViewInternal table = startedTables.get(tableId);
+        if (table == null) {
+            return;
+        }
 
-            metricManager.unregisterSource(table.metrics());
+        QualifiedName tableName = table.qualifiedName();
+
+        try {
+            
metricManager.unregisterSource(TableMetricSource.sourceName(tableName));
         } catch (Exception e) {
-            LOG.warn("Failed to unregister metrics source for table 
[tableId={}].", e, tableId);
+            LOG.warn("Failed to unregister metrics source for table [id={}, 
name={}].", e, tableId, tableName);
+        }
+
+        String storageProfile = 
table.internalTable().storage().getTableDescriptor().getStorageProfile();
+        StorageEngine engine = 
dataStorageMgr.engineByStorageProfile(storageProfile);
+        // Engine can be null sometimes, see "TableManager.createTableStorage".
+        if (engine != null) {
+            try {
+                
metricManager.unregisterSource(StorageEngineTablesMetricSource.sourceName(engine.name(),
 tableName));
+            } catch (Exception e) {
+                LOG.warn("Failed to unregister storage engine metrics source 
for table [id={}, name={}].", e, tableId, tableName);
+            }
         }
     }
 
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/metrics/TableMetricSource.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/metrics/TableMetricSource.java
index 0348a4f9b79..732c6b55f24 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/metrics/TableMetricSource.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/metrics/TableMetricSource.java
@@ -124,10 +124,17 @@ public class TableMetricSource extends 
AbstractMetricSource<Holder> {
      * @param tableName Qualified table name.
      */
     public TableMetricSource(QualifiedName tableName) {
-        super(SOURCE_NAME + '.' + tableName.toCanonicalForm(), "Table 
metrics.", "tables");
+        super(sourceName(tableName), "Table metrics.", "tables");
         this.tableName = tableName;
     }
 
+    /**
+     * Returns a metric source name for the given table.
+     */
+    public static String sourceName(QualifiedName tableName) {
+        return SOURCE_NAME + '.' + tableName.toCanonicalForm();
+    }
+
     /**
      * Returns the qualified name of the table.
      *

Reply via email to