diff --git a/changelog/unreleased/fix-jvm-cpu-utilization-metric-name.yml b/changelog/unreleased/fix-jvm-cpu-utilization-metric-name.yml new file mode 100644 index 000000000000..1c329ff841d1 --- /dev/null +++ b/changelog/unreleased/fix-jvm-cpu-utilization-metric-name.yml @@ -0,0 +1,10 @@ +title: > + The Prometheus JVM CPU metrics are now named `jvm_system_cpu_utilization` and + `jvm_cpu_recent_utilization` (the `_ratio` suffix was dropped) following the + OpenTelemetry Prometheus exporter update. +type: changed +authors: + - name: Jan Høydahl +links: + - name: PR#4711 + url: https://github.com/apache/solr/pull/4711 diff --git a/solr/core/src/java/org/apache/solr/cluster/placement/impl/NodeMetricImpl.java b/solr/core/src/java/org/apache/solr/cluster/placement/impl/NodeMetricImpl.java index 4f76896a320b..96ba2d1b6764 100644 --- a/solr/core/src/java/org/apache/solr/cluster/placement/impl/NodeMetricImpl.java +++ b/solr/core/src/java/org/apache/solr/cluster/placement/impl/NodeMetricImpl.java @@ -38,7 +38,7 @@ public class NodeMetricImpl extends MetricImpl implements NodeMetric { /** System load average. */ public static final NodeMetricImpl SYSLOAD_AVG = - new NodeMetricImpl<>("sysLoadAvg", "jvm_system_cpu_utilization_ratio"); + new NodeMetricImpl<>("sysLoadAvg", "jvm_system_cpu_utilization"); /** Number of available processors. */ public static final NodeMetricImpl AVAILABLE_PROCESSORS = diff --git a/solr/core/src/java/org/apache/solr/util/circuitbreaker/CPUCircuitBreaker.java b/solr/core/src/java/org/apache/solr/util/circuitbreaker/CPUCircuitBreaker.java index 09248363bcdb..391f88049dce 100644 --- a/solr/core/src/java/org/apache/solr/util/circuitbreaker/CPUCircuitBreaker.java +++ b/solr/core/src/java/org/apache/solr/util/circuitbreaker/CPUCircuitBreaker.java @@ -112,7 +112,7 @@ protected double calculateLiveCPUUsage() { return this.cc .getMetricManager() .getPrometheusMetricReader("solr.jvm") - .collect(name -> name.contains("jvm_system_cpu_utilization_ratio")) + .collect(name -> name.contains("jvm_system_cpu_utilization")) .stream() .filter(GaugeSnapshot.class::isInstance) .map(GaugeSnapshot.class::cast) diff --git a/solr/core/src/test/org/apache/solr/util/TestCircuitBreakers.java b/solr/core/src/test/org/apache/solr/util/TestCircuitBreakers.java index 648ac0358549..88bdee78087c 100644 --- a/solr/core/src/test/org/apache/solr/util/TestCircuitBreakers.java +++ b/solr/core/src/test/org/apache/solr/util/TestCircuitBreakers.java @@ -19,8 +19,10 @@ import static org.hamcrest.CoreMatchers.containsString; +import com.sun.management.OperatingSystemMXBean; import java.io.IOException; import java.lang.invoke.MethodHandles; +import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -30,6 +32,7 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import org.apache.lucene.util.SuppressForbidden; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.common.SolrException; @@ -43,6 +46,7 @@ import org.apache.solr.util.circuitbreaker.LoadAverageCircuitBreaker; import org.apache.solr.util.circuitbreaker.MemoryCircuitBreaker; import org.junit.After; +import org.junit.Assume; import org.junit.BeforeClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -271,6 +275,39 @@ public void testFakeLoadAverageCircuitBreaker() { assertThatHighQueryLoadTrips(circuitBreaker, 5); } + /** + * Reads a real CPU usage value from the metrics, guarding against a regression where a renamed + * JVM metric made {@link CPUCircuitBreaker#calculateLiveCPUUsage()} silently return -1. Gated by + * an independent native-CPU probe so that a name regression fails here rather than being mistaken + * for an unsupported machine (both otherwise yield -1). + */ + public void testCPUCircuitBreakerReadsLiveUsage() { + Assume.assumeTrue("No native CPU measurement on this machine", nativeCpuMeasurementSupported()); + + double usage = new ExposedCPUCircuitBreaker(h.getCoreContainer()).liveCPUUsage(); + assertTrue("Expected CPU usage >= 0 but got " + usage, usage >= 0); + } + + @SuppressForbidden(reason = "Probing com.sun OperatingSystemMXBean for native CPU support") + private static boolean nativeCpuMeasurementSupported() { + if (!(ManagementFactory.getOperatingSystemMXBean() instanceof OperatingSystemMXBean osBean)) { + return false; + } + // getCpuLoad() needs two samples and may return a negative value on the first calls + for (int i = 0; i < 10; i++) { + if (osBean.getCpuLoad() >= 0) { + return true; + } + try { + Thread.sleep(50); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + return false; + } + /** * Common assert method to be reused in tests * @@ -441,6 +478,17 @@ protected double calculateLiveCPUUsage() { } } + /** Exposes the real (protected) CPU usage calculation for testing. */ + private static class ExposedCPUCircuitBreaker extends CPUCircuitBreaker { + public ExposedCPUCircuitBreaker(CoreContainer coreContainer) { + super(coreContainer); + } + + double liveCPUUsage() { + return calculateLiveCPUUsage(); + } + } + private static class FakeLoadAverageCircuitBreaker extends LoadAverageCircuitBreaker { @Override protected double calculateLiveLoadAverage() { diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc index c3ae79d9afa6..a065a7413e1e 100644 --- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc +++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc @@ -420,6 +420,9 @@ Users who consume Solr metrics via OTLP and rely on the 10.0 underscore-format n (e.g., `crossdc.consumer.output.total` → `solr.crossdc.consumer.output.total`) * All CrossDC producer metrics have been renamed: `solr.core.crossdc.producer.*` → `solr.crossdc.producer.*` (e.g., `solr.core.crossdc.producer.submitted` → `solr.crossdc.producer.submitted`) +* The Prometheus JVM CPU utilization metrics lost their `_ratio` suffix, as the updated OpenTelemetry + exporter no longer maps the OTel unit `1` to a `_ratio` suffix: `jvm_system_cpu_utilization_ratio` → + `jvm_system_cpu_utilization` and `jvm_cpu_recent_utilization_ratio` → `jvm_cpu_recent_utilization`. Update your dashboards or other metrics consumers accordingly. diff --git a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java index 3dd91fca5d30..abd31c5d86c8 100644 --- a/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java +++ b/solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/NodeValueFetcher.java @@ -74,7 +74,7 @@ public Object extractFromPrometheus(List prometheusLines) { .sum(); } }, - SYSLOADAVG("sysLoadAvg", "jvm_system_cpu_utilization_ratio"); + SYSLOADAVG("sysLoadAvg", "jvm_system_cpu_utilization"); public final String tagName; public final String metricName;