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

sk0x50 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 92b0c32404b IGNITE-27296 Fix table metrics registration on recovery 
(#7208)
92b0c32404b is described below

commit 92b0c32404bb988d78fdedbb054cd807121ff5d0
Author: Slava Koptilin <[email protected]>
AuthorDate: Mon Dec 15 16:36:48 2025 +0200

    IGNITE-27296 Fix table metrics registration on recovery (#7208)
---
 .../ignite/internal/metrics/MetricManager.java     |   8 +-
 .../ignite/internal/metrics/MetricManagerImpl.java |  10 +-
 .../metrics/exporters/jmx/MetricSetMbean.java      |  15 +--
 .../ignite/internal/metrics/MetricManagerTest.java | 113 +++++++++++++++++++++
 .../internal/table/distributed/TableManager.java   |  29 +++---
 5 files changed, 150 insertions(+), 25 deletions(-)

diff --git 
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
 
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
index 23212e5bb2f..b9201d06107 100644
--- 
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
+++ 
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManager.java
@@ -65,16 +65,20 @@ public interface MetricManager extends IgniteComponent {
     void registerSource(MetricSource src);
 
     /**
-     * Unregister metric source. See {@link 
MetricRegistry#unregisterSource(MetricSource)}.
+     * Disables and unregisters metric source.
      *
      * @param src Metric source.
+     * @see #disable(MetricSource) 
+     * @see MetricRegistry#unregisterSource(MetricSource)
      */
     void unregisterSource(MetricSource src);
 
     /**
-     * Unregister metric source by name. See {@link 
MetricRegistry#unregisterSource(String)}.
+     * Disables and unregisters metric source by name.
      *
      * @param srcName Metric source name.
+     * @see #disable(String)
+     * @see MetricRegistry#unregisterSource(String) 
      */
     void unregisterSource(String srcName);
 
diff --git 
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManagerImpl.java
 
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManagerImpl.java
index 7232da5a606..7fb5bda92b2 100644
--- 
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManagerImpl.java
+++ 
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/MetricManagerImpl.java
@@ -186,12 +186,18 @@ public class MetricManagerImpl implements MetricManager {
 
     @Override
     public void unregisterSource(MetricSource src) {
-        inBusyLockSafe(busyLock, () -> registry.unregisterSource(src));
+        inBusyLockSafe(busyLock, () -> {
+            disable(src);
+            registry.unregisterSource(src);
+        });
     }
 
     @Override
     public void unregisterSource(String srcName) {
-        inBusyLockSafe(busyLock, () -> registry.unregisterSource(srcName));
+        inBusyLockSafe(busyLock, () -> {
+            disable(srcName);
+            registry.unregisterSource(srcName);
+        });
     }
 
     @Override
diff --git 
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/jmx/MetricSetMbean.java
 
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/jmx/MetricSetMbean.java
index c220233d64d..e6072ce47fe 100644
--- 
a/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/jmx/MetricSetMbean.java
+++ 
b/modules/metrics/src/main/java/org/apache/ignite/internal/metrics/exporters/jmx/MetricSetMbean.java
@@ -45,7 +45,7 @@ import org.apache.ignite.internal.metrics.UuidGauge;
  */
 public class MetricSetMbean implements DynamicMBean {
     /** Metric set. */
-    private MetricSet metricSet;
+    private final MetricSet metricSet;
 
     /**
      * Constructs new MBean.
@@ -63,6 +63,9 @@ public class MetricSetMbean implements DynamicMBean {
         }
 
         Metric metric = metricSet.get(attribute);
+        if (metric == null) {
+            throw new AttributeNotFoundException("Attribute not found 
[attribute=" + attribute + ']');
+        }
 
         if (metric instanceof DoubleMetric) {
             return ((DoubleMetric) metric).value();
@@ -81,7 +84,8 @@ public class MetricSetMbean implements DynamicMBean {
             return ((UuidGauge) metric).value();
         }
 
-        throw new AttributeNotFoundException("Unknown metric class [class=" + 
metric.getClass().getName() + ']');
+        throw new AttributeNotFoundException(
+                "Unknown metric class [attribute=" + attribute + ", class=" + 
metric.getClass().getName() + ']');
     }
 
     @Override
@@ -91,13 +95,10 @@ public class MetricSetMbean implements DynamicMBean {
 
         try {
             for (String attribute : attributes) {
+                // getAttribute must return a value of the attribute, not 
Attribute instance.
                 Object val = getAttribute(attribute);
 
-                if (val instanceof Attribute) {
-                    attrList.add((Attribute) val);
-                } else {
-                    attrList.add(new Attribute(attribute, val));
-                }
+                attrList.add(new Attribute(attribute, val));
             }
         } catch (AttributeNotFoundException e) {
             throw new IllegalArgumentException(e);
diff --git 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
new file mode 100644
index 00000000000..49281fb3691
--- /dev/null
+++ 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.metrics;
+
+import static 
org.apache.ignite.internal.metrics.exporters.jmx.JmxExporter.JMX_EXPORTER_NAME;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willSucceedFast;
+import static org.apache.ignite.internal.util.IgniteUtils.makeMbeanName;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import java.lang.management.ManagementFactory;
+import java.util.UUID;
+import javax.management.ObjectName;
+import 
org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
+import 
org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.manager.ComponentContext;
+import org.apache.ignite.internal.metrics.configuration.MetricConfiguration;
+import org.apache.ignite.internal.metrics.sources.OsMetricSource;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * Tests {@link MetricManager}.
+ */
+@ExtendWith(ConfigurationExtension.class)
+public class MetricManagerTest extends BaseIgniteAbstractTest {
+    private static final IgniteLogger LOG = 
Loggers.forClass(MetricManagerTest.class);
+
+    private static final String NODE_NAME = "test-node-name";
+
+    private static final UUID NODE_ID = UUID.randomUUID();
+
+    @InjectConfiguration("mock.exporters = {" + JMX_EXPORTER_NAME + " = 
{exporterName = " + JMX_EXPORTER_NAME + "}}")
+    private MetricConfiguration jmxMetricConfiguration;
+
+    @Test
+    void metricSourceRegistration() throws Exception {
+        MetricManager metricManager = 
createAndStartManager(jmxMetricConfiguration);
+
+        OsMetricSource source = new OsMetricSource();
+        metricManager.registerSource(source);
+
+        MetricSet metricSet = metricManager.enable(source);
+        assertThat(metricSet, notNullValue());
+
+        ObjectName name = makeMbeanName(NODE_NAME, metricSet.group(), 
metricSet.name());
+        
assertThat(ManagementFactory.getPlatformMBeanServer().isRegistered(name), 
is(true));
+
+        stopManager(metricManager);
+
+        
assertThat(ManagementFactory.getPlatformMBeanServer().isRegistered(name), 
is(false));
+    }
+
+    @Test
+    void metricSourceCleanup() throws Exception {
+        MetricManager metricManager = 
createAndStartManager(jmxMetricConfiguration);
+
+        OsMetricSource source = new OsMetricSource();
+        metricManager.registerSource(source);
+
+        MetricSet metricSet = metricManager.enable(source);
+        assertThat(metricSet, notNullValue());
+
+        ObjectName name = makeMbeanName(NODE_NAME, metricSet.group(), 
metricSet.name());
+        assertThat(
+                "Metric source was not registered.",
+                ManagementFactory.getPlatformMBeanServer().isRegistered(name),
+                is(true));
+
+        metricManager.unregisterSource(source);
+
+        assertThat(
+                "Metric source was not disabled before unregistering.",
+                ManagementFactory.getPlatformMBeanServer().isRegistered(name),
+                is(false));
+
+        stopManager(metricManager);
+    }
+
+    private static MetricManager createAndStartManager(MetricConfiguration 
configuration) {
+        MetricManager metricManager = new MetricManagerImpl(LOG, null);
+
+        metricManager.configure(configuration, () -> NODE_ID, NODE_NAME);
+
+        assertThat(metricManager.startAsync(new ComponentContext()), 
willSucceedFast());
+
+        return metricManager;
+    }
+
+    private static void stopManager(MetricManager metricManager) {
+        metricManager.beforeNodeStop();
+        assertThat(metricManager.stopAsync(new ComponentContext()), 
willSucceedFast());
+    }
+}
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 928f430d891..6b2ed891f88 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
@@ -1834,8 +1834,7 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
                 // Handle missed table drop event.
                 int tableId = tableDescriptor.id();
 
-                boolean destroyTableEventFired = nextCatalog != null && 
nextCatalog.table(tableId) == null;
-                if (destroyTableEventFired) {
+                if (nextCatalog != null && nextCatalog.table(tableId) == null) 
{
                     destructionEventsQueue.enqueue(new 
DestroyTableEvent(nextCatalog.version(), tableId));
                 }
 
@@ -1853,12 +1852,6 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
                         schemaDescriptor
                 );
 
-                if (destroyTableEventFired) {
-                    // prepareTableResourcesOnRecovery registers a table 
metric source, so we need to unregister it here,
-                    // just because the table is being dropped and there is no 
need to keep the metric source.
-                    unregisterMetricsSource(tables.get(tableId));
-                }
-
                 startTableFutures.add(startTableFuture);
             }
 
@@ -2090,10 +2083,15 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
     }
 
     private TableMetricSource 
createAndRegisterMetricsSource(StorageTableDescriptor tableDescriptor, 
QualifiedName tableName) {
+        // The table might be created during the recovery phase.
+        // In that case, we should only register the metric source for the 
actual tables that exist in the latest catalog.
+        boolean registrationNeeded =
+                catalogService.latestCatalog().table(tableDescriptor.getId()) 
!= null;
+
         StorageEngine engine = 
dataStorageMgr.engineByStorageProfile(tableDescriptor.getStorageProfile());
 
         // Engine can be null sometimes, see "TableManager.createTableStorage".
-        if (engine != null) {
+        if (engine != null && registrationNeeded) {
             StorageEngineTablesMetricSource engineMetricSource = new 
StorageEngineTablesMetricSource(engine.name(), tableName);
 
             engine.addTableMetrics(tableDescriptor, engineMetricSource);
@@ -2109,11 +2107,13 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
 
         TableMetricSource source = new TableMetricSource(tableName);
 
-        try {
-            metricManager.registerSource(source);
-            metricManager.enable(source);
-        } catch (Exception e) {
-            LOG.warn("Failed to register metrics source for table [id={}, 
name={}].", e, tableDescriptor.getId(), tableName);
+        if (registrationNeeded) {
+            try {
+                metricManager.registerSource(source);
+                metricManager.enable(source);
+            } catch (Exception e) {
+                LOG.warn("Failed to register metrics source for table [id={}, 
name={}].", e, tableDescriptor.getId(), tableName);
+            }
         }
 
         return source;
@@ -2134,6 +2134,7 @@ public class TableManager implements 
IgniteTablesInternal, IgniteComponent {
 
         String storageProfile = 
table.internalTable().storage().getTableDescriptor().getStorageProfile();
         StorageEngine engine = 
dataStorageMgr.engineByStorageProfile(storageProfile);
+
         // Engine can be null sometimes, see "TableManager.createTableStorage".
         if (engine != null) {
             try {

Reply via email to