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

snuyanzin pushed a commit to branch release-2.2
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/release-2.2 by this push:
     new dae60fd1430 [FLINK-40596] Closing metric might lead to a number of 
test failures
dae60fd1430 is described below

commit dae60fd1430d31d6c4451687b15809b6691b521e
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Wed Sep 9 09:02:32 2026 +0200

    [FLINK-40596] Closing metric might lead to a number of test failures
---
 .../metrics/groups/AbstractMetricGroup.java        | 12 ++--
 .../runtime/minicluster/MiniClusterITCase.java     | 79 ++++++++++++++++++++++
 2 files changed, 87 insertions(+), 4 deletions(-)

diff --git 
a/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/AbstractMetricGroup.java
 
b/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/AbstractMetricGroup.java
index ec5509707f9..3f31d59bf25 100644
--- 
a/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/AbstractMetricGroup.java
+++ 
b/flink-runtime/src/main/java/org/apache/flink/runtime/metrics/groups/AbstractMetricGroup.java
@@ -85,6 +85,9 @@ public abstract class AbstractMetricGroup<A extends 
AbstractMetricGroup<?>> impl
     /** All metric subgroups of this group. */
     private final Map<String, AbstractMetricGroup<?>> groups = new HashMap<>();
 
+    /** Key under which this group sits in its parent's {@link #groups} map. */
+    private volatile String nameInParent;
+
     /**
      * The metrics scope represented by this group. For example ["host-7", 
"taskmanager-2",
      * "window_word_count", "my-mapper" ].
@@ -337,15 +340,15 @@ public abstract class AbstractMetricGroup<A extends 
AbstractMetricGroup<?>> impl
                 metrics.clear();
             }
         }
-        if (parent != null) {
-            parent.removeChildGroup(this);
+        if (parent != null && nameInParent != null) {
+            parent.removeChildGroup(nameInParent, this);
         }
     }
 
-    void removeChildGroup(AbstractMetricGroup<?> childGroup) {
+    void removeChildGroup(String name, AbstractMetricGroup<?> childGroup) {
         synchronized (this) {
             if (!closed) {
-                groups.values().remove(childGroup);
+                groups.remove(name, childGroup);
             }
         }
     }
@@ -518,6 +521,7 @@ public abstract class AbstractMetricGroup<A extends 
AbstractMetricGroup<?>> impl
                 AbstractMetricGroup<?> prior = groups.put(name, newGroup);
                 if (prior == null || prior.isClosed()) {
                     // no prior group or closed group with that name
+                    newGroup.nameInParent = name;
                     return newGroup;
                 } else {
                     // had a prior group with that name, add the prior group 
back
diff --git 
a/flink-runtime/src/test/java/org/apache/flink/runtime/minicluster/MiniClusterITCase.java
 
b/flink-runtime/src/test/java/org/apache/flink/runtime/minicluster/MiniClusterITCase.java
index d91b6de6240..793fe7868ca 100644
--- 
a/flink-runtime/src/test/java/org/apache/flink/runtime/minicluster/MiniClusterITCase.java
+++ 
b/flink-runtime/src/test/java/org/apache/flink/runtime/minicluster/MiniClusterITCase.java
@@ -23,12 +23,15 @@ import org.apache.flink.configuration.Configuration;
 import org.apache.flink.configuration.JobManagerOptions;
 import org.apache.flink.configuration.ResourceManagerOptions;
 import org.apache.flink.core.testutils.FlinkAssertions;
+import org.apache.flink.metrics.MetricGroup;
 import org.apache.flink.runtime.client.JobExecutionException;
+import org.apache.flink.runtime.execution.Environment;
 import org.apache.flink.runtime.io.network.partition.ResultPartitionType;
 import org.apache.flink.runtime.jobgraph.DistributionPattern;
 import org.apache.flink.runtime.jobgraph.JobGraph;
 import org.apache.flink.runtime.jobgraph.JobGraphTestUtils;
 import org.apache.flink.runtime.jobgraph.JobVertex;
+import org.apache.flink.runtime.jobgraph.tasks.AbstractInvokable;
 import org.apache.flink.runtime.jobmanager.Tasks.AgnosticBinaryReceiver;
 import org.apache.flink.runtime.jobmanager.Tasks.AgnosticReceiver;
 import org.apache.flink.runtime.jobmanager.Tasks.AgnosticTertiaryReceiver;
@@ -41,6 +44,7 @@ import 
org.apache.flink.runtime.jobmanager.scheduler.SlotSharingGroup;
 import org.apache.flink.runtime.jobmaster.JobResult;
 import org.apache.flink.runtime.jobmaster.TestingAbstractInvokables.Receiver;
 import org.apache.flink.runtime.jobmaster.TestingAbstractInvokables.Sender;
+import org.apache.flink.runtime.metrics.groups.AbstractMetricGroup;
 import org.apache.flink.runtime.testtasks.BlockingNoOpInvokable;
 import org.apache.flink.runtime.testtasks.NoOpInvokable;
 import org.apache.flink.runtime.testtasks.WaitingNoOpInvokable;
@@ -51,11 +55,15 @@ import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static 
org.apache.flink.runtime.util.JobVertexConnectionUtils.connectNewDataSetAsInput;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Integration test cases for the {@link MiniCluster}. */
@@ -190,6 +198,51 @@ class MiniClusterITCase {
         }
     }
 
+    @Test
+    void slowMetricTeardownStarvesTheNextJob() throws Exception {
+        final Configuration config = new Configuration();
+        final Duration timeout = Duration.ofSeconds(4);
+        config.set(JobManagerOptions.SLOT_REQUEST_TIMEOUT, timeout);
+        
config.set(JobManagerOptions.SCHEDULER_SUBMISSION_RESOURCE_WAIT_TIMEOUT, 
timeout);
+        config.set(ResourceManagerOptions.REQUIREMENTS_CHECK_DELAY, 
Duration.ofMillis(20));
+        config.set(
+                ResourceManagerOptions.STANDALONE_CLUSTER_STARTUP_PERIOD_TIME,
+                Duration.ofMillis(1L));
+
+        final MiniClusterConfiguration cfg =
+                new MiniClusterConfiguration.Builder()
+                        .withRandomPorts()
+                        .setNumTaskManagers(1)
+                        .setNumSlotsPerTaskManager(1)
+                        .setConfiguration(config)
+                        .build();
+
+        // Job A grabs the single slot.
+        final JobVertex slow = new JobVertex("slow-metric-teardown");
+        slow.setParallelism(1);
+        slow.setInvokableClass(SlowMetricTeardownInvokable.class);
+        final JobGraph jobA = JobGraphTestUtils.streamingJobGraph(slow);
+
+        // Job B just needs the slot once A releases it.
+        final JobVertex worker = new JobVertex("worker");
+        worker.setParallelism(1);
+        worker.setInvokableClass(NoOpInvokable.class);
+        final JobGraph jobB = JobGraphTestUtils.streamingJobGraph(worker);
+
+        SlowMetricTeardownInvokable.slotHeld = new CountDownLatch(1);
+
+        try (final MiniCluster miniCluster = new MiniCluster(cfg)) {
+            miniCluster.start();
+
+            miniCluster.submitJob(jobA).get();
+            // Wait until A holds the slot and is entering its teardown.
+            SlowMetricTeardownInvokable.slotHeld.await();
+
+            // Passes only if A releases its slot before the timeout.
+            assertThatCode(() -> 
miniCluster.executeJobBlocking(jobB)).doesNotThrowAnyException();
+        }
+    }
+
     @Test
     void testForwardJob() throws Exception {
         final int parallelism = 31;
@@ -777,4 +830,30 @@ class MiniClusterITCase {
             throw new OutOfMemoryError("Java heap space");
         }
     }
+
+    public static class SlowMetricTeardownInvokable extends AbstractInvokable {
+
+        public static final int NUM_GROUPS = 50_000;
+
+        /** Counted down once the slot is held and the (slow) teardown is 
about to start. */
+        public static volatile CountDownLatch slotHeld = new CountDownLatch(1);
+
+        public SlowMetricTeardownInvokable(Environment environment) {
+            super(environment);
+        }
+
+        @Override
+        public void invoke() throws Exception {
+            final MetricGroup parent = 
getEnvironment().getMetricGroup().addGroup("splits");
+            final List<AbstractMetricGroup<?>> kids = new 
ArrayList<>(NUM_GROUPS);
+            for (int i = 0; i < NUM_GROUPS; i++) {
+                kids.add((AbstractMetricGroup<?>) parent.addGroup("s" + i));
+            }
+            slotHeld.countDown();
+            // per-split teardown: each close() -> parent.removeChildGroup.
+            for (AbstractMetricGroup<?> kid : kids) {
+                kid.close();
+            }
+        }
+    }
 }

Reply via email to