This is an automated email from the ASF dual-hosted git repository.
shauryachats pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new bb83fa618c2 Initialize ServerMetrics in PredownloadScheduler (#18608)
bb83fa618c2 is described below
commit bb83fa618c22d4d59d838e5ec8429d541d7da90f
Author: Pradeep Singh Negi <[email protected]>
AuthorDate: Tue Jul 7 16:18:30 2026 +0530
Initialize ServerMetrics in PredownloadScheduler (#18608)
* Initialize ServerMetrics in PredownloadScheduler for predownload container
The PredownloadScheduler runs in a separate predownload container that
starts before the main Pinot server. Previously, it only initialized
PredownloadMetrics but did not set up the ServerMetrics registry. This
caused PredownloadMetrics (which internally depends on ServerMetrics)
to silently fall back to the NOOP metrics instance, resulting in no
server-level metrics being emitted from the predownload container.
This change initializes ServerMetrics via PinotMetricUtils in the
initializeMetricsReporter() method. The initialization is wrapped in a
try-catch so that if the underlying metrics factory cannot be created
(e.g., because a vendor-specific metrics client is not yet available
in the predownload lifecycle), the predownload process continues with
the default NOOP ServerMetrics rather than crashing.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Add tests for ServerMetrics initialization in PredownloadScheduler
Three test cases covering the initializeMetricsReporter() change:
1. testInitializeMetricsReporterRegistersServerMetrics - verifies that
ServerMetrics is properly initialized and registered when the
metrics factory is available
2. testInitializeMetricsReporterFallsBackOnFailure - verifies that
when PinotMetricUtils throws (e.g., vendor metrics client not
ready), the scheduler continues with the NOOP ServerMetrics
instance instead of crashing
3. testInitializeMetricsReporterAlwaysCreatesPredownloadMetrics -
verifies that PredownloadMetrics is always created and registered
regardless of whether ServerMetrics initialization succeeds or fails
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Address review comments: narrow catch to Exception, log register result
- Changed catch(Throwable) to catch(Exception) to avoid swallowing
JVM-level Errors like OutOfMemoryError
- Check ServerMetrics.register() return value and log success or
error if an instance was already registered
* Extract shared ServerMetricsInitUtils for ServerMetrics
construction/registration
Deduplicates ServerMetrics construction + registration logic between
BaseServerStarter (real server) and PredownloadScheduler (predownload
container). Registration failure now throws IllegalStateException from
the shared method; PredownloadScheduler's existing try/catch keeps
predownload's graceful-degradation behavior unchanged.
* Trigger CI re-run
* Make ServerMetrics registration idempotent to fix integration tests
ServerMetricsInitUtils.initServerMetrics() previously threw
IllegalStateException when ServerMetrics was already registered.
This broke integration tests where multiple servers start in the
same JVM via ClusterTest.startOneServer, causing 38 test failures.
Now returns the existing instance instead of throwing.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: psinghnegi <[email protected]>
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
---
.../server/predownload/PredownloadScheduler.java | 9 +++
.../server/starter/ServerMetricsInitUtils.java | 50 +++++++++++++++
.../server/starter/helix/BaseServerStarter.java | 12 +---
.../predownload/PredownloadSchedulerTest.java | 74 ++++++++++++++++++++++
.../server/starter/ServerMetricsInitUtilsTest.java | 65 +++++++++++++++++++
5 files changed, 201 insertions(+), 9 deletions(-)
diff --git
a/pinot-server/src/main/java/org/apache/pinot/server/predownload/PredownloadScheduler.java
b/pinot-server/src/main/java/org/apache/pinot/server/predownload/PredownloadScheduler.java
index 89fa99effd7..79e395760e8 100644
---
a/pinot-server/src/main/java/org/apache/pinot/server/predownload/PredownloadScheduler.java
+++
b/pinot-server/src/main/java/org/apache/pinot/server/predownload/PredownloadScheduler.java
@@ -45,6 +45,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.pinot.common.utils.TarCompressionUtils;
import org.apache.pinot.common.utils.fetcher.SegmentFetcherFactory;
import org.apache.pinot.server.conf.ServerConf;
+import org.apache.pinot.server.starter.ServerMetricsInitUtils;
import org.apache.pinot.server.starter.helix.HelixInstanceDataManagerConfig;
import org.apache.pinot.spi.config.instance.InstanceDataManagerConfig;
import org.apache.pinot.spi.crypt.PinotCrypterFactory;
@@ -182,6 +183,14 @@ public class PredownloadScheduler {
void initializeMetricsReporter() {
LOGGER.info("Initializing metrics reporter");
+ try {
+ ServerConf serverConf = new ServerConf(_pinotConfig);
+ ServerMetricsInitUtils.initServerMetrics(serverConf);
+ } catch (Exception e) {
+ LOGGER.error("Failed to initialize ServerMetrics in predownload
container; "
+ + "continuing with the currently registered ServerMetrics instance",
e);
+ }
+
_predownloadMetrics = new PredownloadMetrics();
PredownloadStatusRecorder.registerMetrics(_predownloadMetrics);
}
diff --git
a/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerMetricsInitUtils.java
b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerMetricsInitUtils.java
new file mode 100644
index 00000000000..0ec8d8b053d
--- /dev/null
+++
b/pinot-server/src/main/java/org/apache/pinot/server/starter/ServerMetricsInitUtils.java
@@ -0,0 +1,50 @@
+/**
+ * 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.pinot.server.starter;
+
+import org.apache.pinot.common.metrics.ServerMetrics;
+import org.apache.pinot.server.conf.ServerConf;
+import org.apache.pinot.spi.metrics.PinotMetricUtils;
+import org.apache.pinot.spi.metrics.PinotMetricsRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public final class ServerMetricsInitUtils {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(ServerMetricsInitUtils.class);
+
+ private ServerMetricsInitUtils() {
+ }
+
+ public static ServerMetrics initServerMetrics(ServerConf serverConf)
+ throws Exception {
+ PinotMetricsRegistry metricsRegistry =
PinotMetricUtils.getPinotMetricsRegistry(serverConf.getMetricsConfig());
+ ServerMetrics serverMetrics = new
ServerMetrics(serverConf.getMetricsPrefix(), metricsRegistry,
+ serverConf.emitTableLevelMetrics(),
serverConf.getAllowedTablesForEmittingMetrics());
+ serverMetrics.initializeGlobalMeters();
+ boolean registered = ServerMetrics.register(serverMetrics);
+ if (registered) {
+ LOGGER.info("ServerMetrics successfully registered");
+ return serverMetrics;
+ } else {
+ LOGGER.warn("ServerMetrics already registered; returning existing
instance");
+ return ServerMetrics.get();
+ }
+ }
+}
diff --git
a/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/BaseServerStarter.java
b/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/BaseServerStarter.java
index 06febd192c1..66b9d047f3e 100644
---
a/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/BaseServerStarter.java
+++
b/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/BaseServerStarter.java
@@ -114,6 +114,7 @@ import org.apache.pinot.server.conf.ServerConf;
import org.apache.pinot.server.realtime.ControllerLeaderLocator;
import org.apache.pinot.server.realtime.ServerSegmentCompletionProtocolHandler;
import org.apache.pinot.server.starter.ServerInstance;
+import org.apache.pinot.server.starter.ServerMetricsInitUtils;
import org.apache.pinot.server.starter.ServerQueriesDisabledTracker;
import org.apache.pinot.server.worker.WorkerQueryServer;
import org.apache.pinot.spi.accounting.ThreadAccountant;
@@ -126,8 +127,6 @@ import org.apache.pinot.spi.env.PinotConfiguration;
import org.apache.pinot.spi.environmentprovider.PinotEnvironmentProvider;
import
org.apache.pinot.spi.environmentprovider.PinotEnvironmentProviderFactory;
import org.apache.pinot.spi.filesystem.PinotFSFactory;
-import org.apache.pinot.spi.metrics.PinotMetricUtils;
-import org.apache.pinot.spi.metrics.PinotMetricsRegistry;
import org.apache.pinot.spi.plugin.PluginManager;
import org.apache.pinot.spi.services.ServiceRole;
import org.apache.pinot.spi.services.ServiceStartable;
@@ -678,14 +677,9 @@ public abstract class BaseServerStarter implements
ServiceStartable {
LOGGER.info("Initializing server metrics");
ServerConf serverConf = new ServerConf(_serverConf);
- PinotMetricsRegistry metricsRegistry =
PinotMetricUtils.getPinotMetricsRegistry(serverConf.getMetricsConfig());
- _serverMetrics =
- new ServerMetrics(serverConf.getMetricsPrefix(), metricsRegistry,
serverConf.emitTableLevelMetrics(),
- serverConf.getAllowedTablesForEmittingMetrics());
- _serverMetrics.initializeGlobalMeters();
+ _serverMetrics = ServerMetricsInitUtils.initServerMetrics(serverConf);
_serverMetrics.setValueOfGlobalGauge(ServerGauge.VERSION,
PinotVersion.VERSION_METRIC_NAME, 1);
- ServerMetrics.register(_serverMetrics);
- MseMetrics.registerFromConfig(_serverConf, metricsRegistry);
+ MseMetrics.registerFromConfig(_serverConf,
_serverMetrics.getMetricsRegistry());
LOGGER.info("Initializing reload job status cache");
_reloadJobStatusCache = new ServerReloadJobStatusCache(_instanceId);
diff --git
a/pinot-server/src/test/java/org/apache/pinot/server/predownload/PredownloadSchedulerTest.java
b/pinot-server/src/test/java/org/apache/pinot/server/predownload/PredownloadSchedulerTest.java
index 7432681d289..02e89aa7242 100644
---
a/pinot-server/src/test/java/org/apache/pinot/server/predownload/PredownloadSchedulerTest.java
+++
b/pinot-server/src/test/java/org/apache/pinot/server/predownload/PredownloadSchedulerTest.java
@@ -35,17 +35,21 @@ import
org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.helix.model.InstanceConfig;
import org.apache.pinot.common.metadata.segment.SegmentZKMetadata;
+import org.apache.pinot.common.metrics.ServerMetrics;
import org.apache.pinot.common.utils.TarCompressionUtils;
import org.apache.pinot.common.utils.fetcher.SegmentFetcherFactory;
import org.apache.pinot.spi.config.instance.InstanceDataManagerConfig;
import org.apache.pinot.spi.config.table.TableConfig;
import org.apache.pinot.spi.env.CommonsConfigurationUtils;
import org.apache.pinot.spi.filesystem.PinotFSFactory;
+import org.apache.pinot.spi.metrics.PinotMetricUtils;
+import org.apache.pinot.spi.metrics.PinotMetricsRegistry;
import org.apache.pinot.spi.utils.CommonConstants;
import org.apache.pinot.spi.utils.retry.AttemptsExceededException;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import static org.apache.pinot.server.predownload.PredownloadTestUtil.*;
@@ -57,6 +61,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
@@ -95,6 +100,11 @@ public class PredownloadSchedulerTest {
_predownloadScheduler._executor = Runnable::run;
}
+ @AfterMethod
+ public void cleanUpMetrics() {
+ ServerMetrics.deregister();
+ }
+
@AfterClass
public void tearDown()
throws Exception {
@@ -365,6 +375,70 @@ public class PredownloadSchedulerTest {
zeroScheduler.stop();
}
+ @Test
+ public void testInitializeMetricsReporterRegistersServerMetrics()
+ throws Exception {
+ String propertiesFilePath =
this.getClass().getClassLoader().getResource(SAMPLE_PROPERTIES_FILE_NAME).getPath();
+ PropertiesConfiguration properties =
CommonsConfigurationUtils.fromPath(propertiesFilePath);
+ PredownloadScheduler scheduler = new PredownloadScheduler(properties);
+
+ try (MockedStatic<PinotMetricUtils> pinotMetricUtilsMockedStatic =
mockStatic(PinotMetricUtils.class)) {
+ PinotMetricsRegistry mockRegistry = mock(PinotMetricsRegistry.class);
+ pinotMetricUtilsMockedStatic.when(() ->
PinotMetricUtils.getPinotMetricsRegistry(any()))
+ .thenReturn(mockRegistry);
+
+ scheduler.initializeMetricsReporter();
+
+ pinotMetricUtilsMockedStatic.verify(() ->
PinotMetricUtils.getPinotMetricsRegistry(any()), times(1));
+ ServerMetrics registeredMetrics = ServerMetrics.get();
+ assertNotNull(registeredMetrics, "ServerMetrics should be registered
after initializeMetricsReporter");
+ } finally {
+ scheduler.stop();
+ }
+ }
+
+ @Test
+ public void testInitializeMetricsReporterFallsBackOnFailure()
+ throws Exception {
+ String propertiesFilePath =
this.getClass().getClassLoader().getResource(SAMPLE_PROPERTIES_FILE_NAME).getPath();
+ PropertiesConfiguration properties =
CommonsConfigurationUtils.fromPath(propertiesFilePath);
+ PredownloadScheduler scheduler = new PredownloadScheduler(properties);
+
+ try (MockedStatic<PinotMetricUtils> pinotMetricUtilsMockedStatic =
mockStatic(PinotMetricUtils.class)) {
+ pinotMetricUtilsMockedStatic.when(() ->
PinotMetricUtils.getPinotMetricsRegistry(any()))
+ .thenThrow(new RuntimeException("Metrics factory not available"));
+
+ scheduler.initializeMetricsReporter();
+
+ ServerMetrics registeredMetrics = ServerMetrics.get();
+ assertNotNull(registeredMetrics, "ServerMetrics.get() should return NOOP
instance, not null");
+ } finally {
+ scheduler.stop();
+ }
+ }
+
+ @Test
+ public void testInitializeMetricsReporterAlwaysCreatesPredownloadMetrics()
+ throws Exception {
+ String propertiesFilePath =
this.getClass().getClassLoader().getResource(SAMPLE_PROPERTIES_FILE_NAME).getPath();
+ PropertiesConfiguration properties =
CommonsConfigurationUtils.fromPath(propertiesFilePath);
+ PredownloadScheduler scheduler = spy(new PredownloadScheduler(properties));
+
+ try (MockedStatic<PinotMetricUtils> pinotMetricUtilsMockedStatic =
mockStatic(PinotMetricUtils.class);
+ MockedStatic<PredownloadStatusRecorder> statusRecorderMockedStatic =
+ mockStatic(PredownloadStatusRecorder.class)) {
+ pinotMetricUtilsMockedStatic.when(() ->
PinotMetricUtils.getPinotMetricsRegistry(any()))
+ .thenThrow(new RuntimeException("Metrics factory not available"));
+
+ scheduler.initializeMetricsReporter();
+
+ statusRecorderMockedStatic.verify(
+ () ->
PredownloadStatusRecorder.registerMetrics(any(PredownloadMetrics.class)),
times(1));
+ } finally {
+ scheduler.stop();
+ }
+ }
+
// ── Peer download tests
────────────────────────────────────────────────────
private PredownloadScheduler
buildPeerEnabledScheduler(PropertiesConfiguration properties)
diff --git
a/pinot-server/src/test/java/org/apache/pinot/server/starter/ServerMetricsInitUtilsTest.java
b/pinot-server/src/test/java/org/apache/pinot/server/starter/ServerMetricsInitUtilsTest.java
new file mode 100644
index 00000000000..e16330a0066
--- /dev/null
+++
b/pinot-server/src/test/java/org/apache/pinot/server/starter/ServerMetricsInitUtilsTest.java
@@ -0,0 +1,65 @@
+/**
+ * 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.pinot.server.starter;
+
+import org.apache.pinot.common.metrics.ServerMetrics;
+import org.apache.pinot.server.conf.ServerConf;
+import org.apache.pinot.spi.env.PinotConfiguration;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import static
org.apache.pinot.spi.utils.CommonConstants.CONFIG_OF_METRICS_FACTORY_CLASS_NAME;
+import static
org.apache.pinot.spi.utils.CommonConstants.Server.METRICS_CONFIG_PREFIX;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertSame;
+
+
+public class ServerMetricsInitUtilsTest {
+
+ @AfterMethod
+ public void tearDown() {
+ ServerMetrics.deregister();
+ }
+
+ private static ServerConf buildServerConf() {
+ PinotConfiguration pinotConfig = new PinotConfiguration();
+ pinotConfig.setProperty(METRICS_CONFIG_PREFIX + "." +
CONFIG_OF_METRICS_FACTORY_CLASS_NAME,
+ "org.apache.pinot.plugin.metrics.yammer.YammerMetricsFactory");
+ return new ServerConf(pinotConfig);
+ }
+
+ @Test
+ public void testInitServerMetricsConstructsAndRegisters()
+ throws Exception {
+ ServerMetrics serverMetrics =
ServerMetricsInitUtils.initServerMetrics(buildServerConf());
+
+ assertNotNull(serverMetrics);
+ assertSame(ServerMetrics.get(), serverMetrics, "Returned ServerMetrics
should be the registered instance");
+ }
+
+ @Test
+ public void testInitServerMetricsReturnsExistingWhenAlreadyRegistered()
+ throws Exception {
+ ServerConf serverConf = buildServerConf();
+ ServerMetrics first = ServerMetricsInitUtils.initServerMetrics(serverConf);
+ ServerMetrics second =
ServerMetricsInitUtils.initServerMetrics(serverConf);
+
+ assertSame(second, first, "Should return the already-registered instance");
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]