This is an automated email from the ASF dual-hosted git repository.
mlbiscoc pushed a commit to branch feature/SOLR-17458
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/feature/SOLR-17458 by this
push:
new a933b0496f2 Migrate FieldCacheMetrics to OTEL (#3619)
a933b0496f2 is described below
commit a933b0496f28048162c97f66bc23e42054a3b50d
Author: Matthew Biscocho <[email protected]>
AuthorDate: Fri Sep 19 16:06:06 2025 -0400
Migrate FieldCacheMetrics to OTEL (#3619)
Co-authored-by: Matthew Biscocho <[email protected]>
---
.../java/org/apache/solr/core/CoreContainer.java | 6 +-
.../src/java/org/apache/solr/core/SolrCore.java | 34 +++----
.../org/apache/solr/search/SolrFieldCacheBean.java | 37 ++++---
.../apache/solr/uninverting/UninvertingReader.java | 8 +-
.../apache/solr/search/TestSolrFieldCacheBean.java | 108 ++++++++++++---------
5 files changed, 109 insertions(+), 84 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 737a8628be4..8a322cc8cbd 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -1109,8 +1109,10 @@ public class CoreContainer {
"version");
SolrFieldCacheBean fieldCacheBean = new SolrFieldCacheBean();
- // NOCOMMIT SOLR-17458: Otel migration
- fieldCacheBean.initializeMetrics(solrMetricsContext, Attributes.empty(),
"");
+ fieldCacheBean.initializeMetrics(
+ solrMetricsContext,
+ Attributes.of(CATEGORY_ATTR, SolrInfoBean.Category.CACHE.toString()),
+ "");
if (isZooKeeperAware()) {
metricManager.loadClusterReporters(metricReporters, this);
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java
b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index 97b0f426678..c62563a2209 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -259,6 +259,7 @@ public class SolrCore implements SolrInfoBean, Closeable {
private volatile boolean newSearcherReady = false;
+ private final Attributes coreAttributes;
private AttributedLongCounter newSearcherCounter;
private AttributedLongCounter newSearcherMaxReachedCounter;
private AttributedLongCounter newSearcherOtherErrorsCounter;
@@ -1088,6 +1089,17 @@ public class SolrCore implements SolrInfoBean, Closeable
{
this.solrMetricsContext = coreMetricManager.getSolrMetricsContext();
this.coreMetricManager.loadReporters();
+ if (coreContainer.isZooKeeperAware()) {
+ this.coreAttributes =
+ Attributes.builder()
+ .put(COLLECTION_ATTR, coreDescriptor.getCollectionName())
+ .put(CORE_ATTR, coreDescriptor.getName())
+ .put(SHARD_ATTR,
coreDescriptor.getCloudDescriptor().getShardId())
+ .build();
+ } else {
+ this.coreAttributes = Attributes.builder().put(CORE_ATTR,
coreDescriptor.getName()).build();
+ }
+
if (updateHandler == null) {
directoryFactory = initDirectoryFactory();
recoveryStrategyBuilder = initRecoveryStrategyBuilder();
@@ -1109,21 +1121,7 @@ public class SolrCore implements SolrInfoBean, Closeable
{
setLatestSchema(schema);
// initialize core metrics
- if (coreContainer.isZooKeeperAware()) {
- initializeMetrics(
- solrMetricsContext,
- Attributes.builder()
- .put(COLLECTION_ATTR, coreDescriptor.getCollectionName())
- .put(CORE_ATTR, coreDescriptor.getName())
- .put(SHARD_ATTR,
coreDescriptor.getCloudDescriptor().getShardId())
- .build(),
- "");
- } else {
- initializeMetrics(
- solrMetricsContext,
- Attributes.builder().put(CORE_ATTR,
coreDescriptor.getName()).build(),
- "");
- }
+ initializeMetrics(solrMetricsContext, coreAttributes, "");
// init pluggable circuit breakers, after metrics because some circuit
breakers use metrics
initPlugins(null, CircuitBreaker.class);
@@ -1131,8 +1129,10 @@ public class SolrCore implements SolrInfoBean, Closeable
{
SolrFieldCacheBean solrFieldCacheBean = new SolrFieldCacheBean();
// this is registered at the CONTAINER level because it's not
core-specific - for now we
// also register it here for back-compat
- // NOCOMMIT SOLR-17458: Add Otel
- solrFieldCacheBean.initializeMetrics(solrMetricsContext,
Attributes.empty(), "core");
+ solrFieldCacheBean.initializeMetrics(
+ solrMetricsContext,
+ coreAttributes.toBuilder().put(CATEGORY_ATTR,
Category.CACHE.toString()).build(),
+ "");
infoRegistry.put("fieldCache", solrFieldCacheBean);
this.maxWarmingSearchers = solrConfig.maxWarmingSearchers;
diff --git a/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
b/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
index 58c1bdb55c1..a0dff1245d7 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java
@@ -17,9 +17,10 @@
package org.apache.solr.search;
import io.opentelemetry.api.common.Attributes;
+import org.apache.solr.common.util.IOUtils;
import org.apache.solr.core.SolrInfoBean;
-import org.apache.solr.metrics.MetricsMap;
import org.apache.solr.metrics.SolrMetricsContext;
+import org.apache.solr.metrics.otel.OtelUnit;
import org.apache.solr.uninverting.UninvertingReader;
/** A SolrInfoBean that provides introspection of the Solr FieldCache */
@@ -28,6 +29,7 @@ public class SolrFieldCacheBean implements SolrInfoBean {
private boolean disableEntryList =
Boolean.getBoolean("disableSolrFieldCacheMBeanEntryList");
private boolean disableJmxEntryList =
Boolean.getBoolean("disableSolrFieldCacheMBeanEntryListJmx");
+ private AutoCloseable toClose;
private SolrMetricsContext solrMetricsContext;
@@ -51,28 +53,35 @@ public class SolrFieldCacheBean implements SolrInfoBean {
return solrMetricsContext;
}
- // TODO SOLR-17458: Migrate to Otel
@Override
public void initializeMetrics(
SolrMetricsContext parentContext, Attributes attributes, String scope) {
this.solrMetricsContext = parentContext;
- MetricsMap metricsMap =
- new MetricsMap(
- map -> {
+ var solrCacheStats =
+ solrMetricsContext.longMeasurement(
+ "solr_field_cache_entries", "Number of field cache entries");
+ var solrCacheSize =
+ solrMetricsContext.longMeasurement(
+ "solr_field_cache_size", "Size of field cache in bytes",
OtelUnit.BYTES);
+ this.toClose =
+ solrMetricsContext.batchCallback(
+ () -> {
if (!disableEntryList && !disableJmxEntryList) {
UninvertingReader.FieldCacheStats fieldCacheStats =
UninvertingReader.getUninvertedStats();
String[] entries = fieldCacheStats.info;
- map.put("entries_count", entries.length);
- map.put("total_size", fieldCacheStats.totalSize);
- for (int i = 0; i < entries.length; i++) {
- final String entry = entries[i];
- map.put("entry#" + i, entry);
- }
+ solrCacheStats.record(entries.length, attributes);
+ solrCacheSize.record(fieldCacheStats.totalSize, attributes);
} else {
- map.put("entries_count",
UninvertingReader.getUninvertedStatsSize());
+
solrCacheStats.record(UninvertingReader.getUninvertedStatsSize(), attributes);
}
- });
- solrMetricsContext.gauge(metricsMap, true, "fieldCache",
Category.CACHE.toString(), scope);
+ },
+ solrCacheStats,
+ solrCacheSize);
+ }
+
+ @Override
+ public void close() {
+ IOUtils.closeQuietly(toClose);
}
}
diff --git
a/solr/core/src/java/org/apache/solr/uninverting/UninvertingReader.java
b/solr/core/src/java/org/apache/solr/uninverting/UninvertingReader.java
index c0f4f209921..5f85aac034e 100644
--- a/solr/core/src/java/org/apache/solr/uninverting/UninvertingReader.java
+++ b/solr/core/src/java/org/apache/solr/uninverting/UninvertingReader.java
@@ -38,7 +38,6 @@ import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.SortedDocValues;
import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.util.RamUsageEstimator;
import org.apache.solr.uninverting.FieldCache.CacheEntry;
/**
@@ -467,8 +466,7 @@ public class UninvertingReader extends FilterLeafReader {
info[i] = entries[i].toString();
totalBytesUsed += entries[i].getValue().ramBytesUsed();
}
- String totalSize = RamUsageEstimator.humanReadableUnits(totalBytesUsed);
- return new FieldCacheStats(totalSize, info);
+ return new FieldCacheStats(totalBytesUsed, info);
}
public static int getUninvertedStatsSize() {
@@ -481,10 +479,10 @@ public class UninvertingReader extends FilterLeafReader {
* @lucene.internal
*/
public static class FieldCacheStats {
- public String totalSize;
+ public Long totalSize;
public String[] info;
- public FieldCacheStats(String totalSize, String[] info) {
+ public FieldCacheStats(Long totalSize, String[] info) {
this.totalSize = totalSize;
this.info = info;
}
diff --git
a/solr/core/src/test/org/apache/solr/search/TestSolrFieldCacheBean.java
b/solr/core/src/test/org/apache/solr/search/TestSolrFieldCacheBean.java
index c7d5097a9e6..fea83d51038 100644
--- a/solr/core/src/test/org/apache/solr/search/TestSolrFieldCacheBean.java
+++ b/solr/core/src/test/org/apache/solr/search/TestSolrFieldCacheBean.java
@@ -16,12 +16,14 @@
*/
package org.apache.solr.search;
+import static org.apache.solr.metrics.SolrMetricProducer.CATEGORY_ATTR;
+
import io.opentelemetry.api.common.Attributes;
-import java.util.Map;
-import java.util.Random;
+import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
+import java.util.Optional;
import org.apache.lucene.tests.util.TestUtil;
import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.metrics.MetricsMap;
+import org.apache.solr.core.SolrInfoBean;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.SolrMetricsContext;
import org.junit.BeforeClass;
@@ -29,6 +31,12 @@ import org.junit.Test;
public class TestSolrFieldCacheBean extends SolrTestCaseJ4 {
+ private static final String ENTRIES_METRIC_NAME = "solr_field_cache_entries";
+ private static final String SIZE_BYTES_METRIC_NAME =
"solr_field_cache_size_bytes";
+ private static final String DISABLE_ENTRY_LIST_PROPERTY =
"disableSolrFieldCacheMBeanEntryList";
+ private static final String DISABLE_ENTRY_LIST_JMX_PROPERTY =
+ "disableSolrFieldCacheMBeanEntryListJmx";
+
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml", "schema-minimal.xml");
@@ -36,69 +44,77 @@ public class TestSolrFieldCacheBean extends SolrTestCaseJ4 {
@Test
public void testEntryList() {
- // ensure entries to FieldCache
+ // Ensure entries to FieldCache
assertU(adoc("id", "id0"));
assertU(commit());
assertQ(req("q", "*:*", "sort", "id asc"), "//*[@numFound='1']");
// Test with entry list enabled
- assertEntryListIncluded(false);
+ assertEntryList(true);
- // Test again with entry list disabled
- System.setProperty("disableSolrFieldCacheMBeanEntryList", "true");
+ // Test with entry list disabled
+ System.setProperty(DISABLE_ENTRY_LIST_PROPERTY, "true");
try {
- assertEntryListNotIncluded(false);
+ assertEntryList(false);
} finally {
- System.clearProperty("disableSolrFieldCacheMBeanEntryList");
+ System.clearProperty(DISABLE_ENTRY_LIST_PROPERTY);
}
- // Test with entry list enabled for jmx
- assertEntryListIncluded(true);
+ // Test with entry list enabled again
+ assertEntryList(true);
- // Test with entry list disabled for jmx
- System.setProperty("disableSolrFieldCacheMBeanEntryListJmx", "true");
+ // Test with entry list disabled for JMX
+ System.setProperty(DISABLE_ENTRY_LIST_JMX_PROPERTY, "true");
try {
- assertEntryListNotIncluded(true);
+ assertEntryList(false);
} finally {
- System.clearProperty("disableSolrFieldCacheMBeanEntryListJmx");
+ System.clearProperty(DISABLE_ENTRY_LIST_JMX_PROPERTY);
}
}
- private void assertEntryListIncluded(boolean checkJmx) {
- SolrFieldCacheBean mbean = new SolrFieldCacheBean();
- Random r = random();
- String registryName = TestUtil.randomSimpleString(r, 1, 10);
- SolrMetricManager metricManager = h.getCoreContainer().getMetricManager();
- SolrMetricsContext solrMetricsContext =
- new SolrMetricsContext(metricManager, registryName, "foo");
- mbean.initializeMetrics(solrMetricsContext, Attributes.empty(), null);
- MetricsMap metricsMap =
- (MetricsMap)
- ((SolrMetricManager.GaugeWrapper)
-
metricManager.registry(registryName).getMetrics().get("CACHE.fieldCache"))
- .getGauge();
- Map<String, Object> metrics = checkJmx ? metricsMap.getValue(true) :
metricsMap.getValue();
- assertTrue(((Number) metrics.get("entries_count")).longValue() > 0);
- assertNotNull(metrics.get("total_size"));
- assertNotNull(metrics.get("entry#0"));
+ private void assertEntryList(boolean bytesMetricIncluded) {
+ FieldCacheMetrics metrics = getFieldCacheMetrics();
+ assertTrue(
+ "Field cache entries count should be greater than 0",
+ metrics.entries().get().getDataPoints().getFirst().getValue() > 0);
+
+ if (bytesMetricIncluded) {
+ assertTrue("Size bytes metric should be present",
metrics.sizeBytes().isPresent());
+ } else {
+ assertTrue("Size bytes metric should not be present",
metrics.sizeBytes().isEmpty());
+ }
}
- private void assertEntryListNotIncluded(boolean checkJmx) {
- SolrFieldCacheBean mbean = new SolrFieldCacheBean();
- Random r = random();
- String registryName = TestUtil.randomSimpleString(r, 1, 10);
+ private FieldCacheMetrics getFieldCacheMetrics() {
+ String registryName = TestUtil.randomSimpleString(random(), 1, 10);
SolrMetricManager metricManager = h.getCoreContainer().getMetricManager();
SolrMetricsContext solrMetricsContext =
new SolrMetricsContext(metricManager, registryName, "foo");
- mbean.initializeMetrics(solrMetricsContext, Attributes.empty(), null);
- MetricsMap metricsMap =
- (MetricsMap)
- ((SolrMetricManager.GaugeWrapper)
-
metricManager.registry(registryName).getMetrics().get("CACHE.fieldCache"))
- .getGauge();
- Map<String, Object> metrics = checkJmx ? metricsMap.getValue(true) :
metricsMap.getValue();
- assertTrue(((Number) metrics.get("entries_count")).longValue() > 0);
- assertNull(metrics.get("total_size"));
- assertNull(metrics.get("entry#0"));
+
+ try (SolrFieldCacheBean mbean = new SolrFieldCacheBean()) {
+ mbean.initializeMetrics(
+ solrMetricsContext,
+ Attributes.of(CATEGORY_ATTR, SolrInfoBean.Category.CACHE.toString()),
+ null);
+
+ var metrics =
metricManager.getPrometheusMetricReader(registryName).collect();
+
+ var entryCount =
+ metrics.stream()
+ .filter(ms ->
ENTRIES_METRIC_NAME.equals(ms.getMetadata().getPrometheusName()))
+ .map(GaugeSnapshot.class::cast)
+ .findFirst();
+
+ var sizeBytes =
+ metrics.stream()
+ .filter(ms ->
SIZE_BYTES_METRIC_NAME.equals(ms.getMetadata().getPrometheusName()))
+ .map(GaugeSnapshot.class::cast)
+ .findFirst();
+
+ return new FieldCacheMetrics(entryCount, sizeBytes);
+ }
}
+
+ private record FieldCacheMetrics(
+ Optional<GaugeSnapshot> entries, Optional<GaugeSnapshot> sizeBytes) {}
}