This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch optimize_prometheus_report
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/optimize_prometheus_report by
this push:
new 63295e6c8bb Fix Prometheus scheduler lifecycle and duplicate allocation
63295e6c8bb is described below
commit 63295e6c8bb8d4b974d1f5b868190e22cb538441
Author: Tian Jiang <[email protected]>
AuthorDate: Wed Sep 2 16:36:19 2026 +0800
Fix Prometheus scheduler lifecycle and duplicate allocation
---
.../reporter/prometheus/PrometheusReporter.java | 43 ++++++++-----
.../prometheus/PrometheusReporterTest.java | 75 ++++++++++++++++++++++
.../commons/service/metric/MetricService.java | 7 +-
3 files changed, 108 insertions(+), 17 deletions(-)
diff --git
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
index fa5695e9621..00ae6c6b988 100644
---
a/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
+++
b/iotdb-core/metrics/interface/src/main/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporter.java
@@ -70,6 +70,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
public class PrometheusReporter implements Reporter {
private static final Logger LOGGER =
LoggerFactory.getLogger(PrometheusReporter.class);
@@ -77,6 +78,7 @@ public class PrometheusReporter implements Reporter {
MetricConfigDescriptor.getInstance().getMetricConfig();
private static final long PROMETHEUS_DEFAULT_SCRAPE_INTERVAL_SECONDS = 15;
private final AbstractMetricManager metricManager;
+ private final Supplier<ScheduledExecutorService>
snapshotUpdateExecutorSupplier;
private volatile ScheduledExecutorService snapshotUpdateExecutor;
private volatile DisposableServer httpServer;
@@ -91,16 +93,30 @@ public class PrometheusReporter implements Reporter {
/**
* Creates a reporter with a self-managed scheduler for compatibility with
standalone users.
- * Server-side code should use the constructor accepting the IoTDB thread
pool.
+ * Server-side code should use the constructor accepting a scheduler factory.
*/
public PrometheusReporter(AbstractMetricManager metricManager) {
- this(metricManager, null);
+ this(metricManager,
PrometheusReporter::newStandaloneSnapshotUpdateExecutor);
}
+ /**
+ * Creates a reporter with a scheduler factory. The factory is invoked on
every start to obtain a
+ * fresh executor for the reporter lifecycle.
+ */
public PrometheusReporter(
- AbstractMetricManager metricManager, ScheduledExecutorService
snapshotUpdateExecutor) {
+ AbstractMetricManager metricManager,
+ Supplier<ScheduledExecutorService> snapshotUpdateExecutorSupplier) {
this.metricManager = metricManager;
- this.snapshotUpdateExecutor = snapshotUpdateExecutor;
+ this.snapshotUpdateExecutorSupplier =
Objects.requireNonNull(snapshotUpdateExecutorSupplier);
+ }
+
+ private static ScheduledExecutorService
newStandaloneSnapshotUpdateExecutor() {
+ return Executors.newSingleThreadScheduledExecutor(
+ runnable -> {
+ Thread thread = new Thread(runnable,
"prometheus-reporter-snapshot-updater");
+ thread.setDaemon(true);
+ return thread;
+ });
}
@Override
@@ -176,14 +192,10 @@ public class PrometheusReporter implements Reporter {
@SuppressWarnings("unsafeThreadSchedule")
private void startSnapshotUpdater() {
// Keep metric collection off Reactor HTTP threads and avoid overlapping
scrapes.
- if (snapshotUpdateExecutor == null) {
- snapshotUpdateExecutor =
- Executors.newSingleThreadScheduledExecutor(
- runnable -> {
- Thread thread = new Thread(runnable,
"prometheus-reporter-snapshot-updater");
- thread.setDaemon(true);
- return thread;
- });
+ if (snapshotUpdateExecutor == null || snapshotUpdateExecutor.isShutdown())
{
+ // Create a fresh executor for every start so a stopped reporter can be
started again with
+ // the same managed thread-pool factory.
+ snapshotUpdateExecutor =
Objects.requireNonNull(snapshotUpdateExecutorSupplier.get());
}
// Delay the first background scrape until metric sets have been bound by
the metric service.
snapshotUpdateFuture =
@@ -227,9 +239,10 @@ public class PrometheusReporter implements Reporter {
snapshotUpdateFuture.cancel(false);
snapshotUpdateFuture = null;
}
- if (snapshotUpdateExecutor != null) {
- snapshotUpdateExecutor.shutdownNow();
- snapshotUpdateExecutor = null;
+ ScheduledExecutorService executor = snapshotUpdateExecutor;
+ snapshotUpdateExecutor = null;
+ if (executor != null) {
+ executor.shutdownNow();
}
}
diff --git
a/iotdb-core/metrics/interface/src/test/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporterTest.java
b/iotdb-core/metrics/interface/src/test/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporterTest.java
new file mode 100644
index 00000000000..7072777ac66
--- /dev/null
+++
b/iotdb-core/metrics/interface/src/test/java/org/apache/iotdb/metrics/reporter/prometheus/PrometheusReporterTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.iotdb.metrics.reporter.prometheus;
+
+import org.apache.iotdb.metrics.config.MetricConfig;
+import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
+import org.apache.iotdb.metrics.impl.DoNothingMetricManager;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrometheusReporterTest {
+
+ @Test
+ public void testManagedExecutorRecreatedAfterRestart() {
+ MetricConfig metricConfig =
MetricConfigDescriptor.getInstance().getMetricConfig();
+ boolean originalAsyncUpdate =
metricConfig.isPrometheusReporterAsyncUpdate();
+ Integer originalPort = metricConfig.getPrometheusReporterPort();
+ metricConfig.setPrometheusReporterAsyncUpdate(true);
+ metricConfig.setPrometheusReporterPort(0);
+
+ AtomicInteger factoryCalls = new AtomicInteger();
+ List<ScheduledExecutorService> executors = new ArrayList<>();
+ PrometheusReporter reporter =
+ new PrometheusReporter(
+ new DoNothingMetricManager(),
+ () -> {
+ factoryCalls.incrementAndGet();
+ ScheduledExecutorService executor =
Executors.newSingleThreadScheduledExecutor();
+ executors.add(executor);
+ return executor;
+ });
+ try {
+ assertTrue(reporter.start());
+ assertEquals(1, factoryCalls.get());
+ assertTrue(reporter.stop());
+ assertTrue(executors.get(0).isShutdown());
+
+ assertTrue(reporter.start());
+ assertEquals(2, factoryCalls.get());
+ assertTrue(reporter.stop());
+ assertTrue(executors.get(1).isShutdown());
+ } finally {
+ reporter.stop();
+ metricConfig.setPrometheusReporterAsyncUpdate(originalAsyncUpdate);
+ metricConfig.setPrometheusReporterPort(originalPort);
+ executors.forEach(ScheduledExecutorService::shutdownNow);
+ }
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/MetricService.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/MetricService.java
index a54d4ca483d..4af78e3d9cf 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/MetricService.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/service/metric/MetricService.java
@@ -77,11 +77,14 @@ public class MetricService extends AbstractMetricService
implements MetricServic
break;
case PROMETHEUS:
if (METRIC_CONFIG.isPrometheusReporterAsyncUpdate()) {
+ // Defer pool creation until start so duplicate reporters rejected
below do not
+ // register an unused pool.
reporter =
new PrometheusReporter(
metricManager,
- IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(
-
ThreadName.PROMETHEUS_REPORTER_SNAPSHOT_UPDATER.getName()));
+ () ->
+
IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(
+
ThreadName.PROMETHEUS_REPORTER_SNAPSHOT_UPDATER.getName()));
} else {
reporter = new PrometheusReporter(metricManager);
}