This is an automated email from the ASF dual-hosted git repository. mlbiscoc pushed a commit to branch feature/SOLR-17458-rebased in repository https://gitbox.apache.org/repos/asf/solr.git
commit 06775b0deb80757d0e66ec2ed33f7f932fac825a Author: Matthew Biscocho <[email protected]> AuthorDate: Fri Sep 26 11:09:57 2025 -0400 SOLR-17806: Migrate CaffeineCache to OTEL (#3671) * Migrate base metrics to OTEL * Fix broken tests * Cleanup utility methods * Clean up more tests * Change ram used bytes name * Change based on feedback * Address feedback * Make cumulative cache metrics the default * Minor feedback * Pass in metric name for caffeine cache --------- Co-authored-by: Matthew Biscocho <[email protected]> --- .../java/org/apache/solr/blockcache/Metrics.java | 9 +- .../java/org/apache/solr/core/CoreContainer.java | 10 +- .../src/java/org/apache/solr/core/SolrCore.java | 35 +++-- .../apache/solr/handler/ReplicationHandler.java | 23 ++-- .../apache/solr/metrics/SolrCoreMetricManager.java | 20 +-- .../org/apache/solr/metrics/SolrMetricManager.java | 39 +++++- .../apache/solr/metrics/SolrMetricProducer.java | 2 + .../apache/solr/metrics/SolrMetricsContext.java | 35 +++-- .../java/org/apache/solr/search/CaffeineCache.java | 117 +++++++++------- .../org/apache/solr/search/SolrFieldCacheBean.java | 4 +- .../org/apache/solr/search/SolrIndexSearcher.java | 6 +- .../org/apache/solr/update/SolrIndexWriter.java | 21 +-- .../src/test/org/apache/solr/CursorPagingTest.java | 63 +++++---- .../apache/solr/core/TestSolrConfigHandler.java | 38 +++--- .../test/org/apache/solr/core/TimeAllowedTest.java | 92 ++++++------- .../org/apache/solr/search/TestCaffeineCache.java | 129 ++++++++++++------ .../solr/search/TestFiltersQueryCaching.java | 49 +++---- .../apache/solr/search/TestMainQueryCaching.java | 20 +-- .../solr/search/TestReRankQParserPlugin.java | 35 +++-- .../org/apache/solr/search/TestSolr4Spatial2.java | 49 ++++--- .../org/apache/solr/search/TestSolrCachePerf.java | 25 +++- .../apache/solr/search/TestSolrQueryParser.java | 114 ++++++++-------- .../test/org/apache/solr/search/TestThinCache.java | 82 ++++++++---- .../org/apache/solr/search/join/BJQParserTest.java | 66 +++++---- .../solr/search/join/TestNestedDocsSort.java | 22 ++- .../solr/search/join/TestScoreJoinQPScore.java | 147 +++++++++++++++------ .../org/apache/solr/util/SolrMetricTestUtils.java | 52 ++++++++ 27 files changed, 779 insertions(+), 525 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/blockcache/Metrics.java b/solr/core/src/java/org/apache/solr/blockcache/Metrics.java index 98c9b2dbef6..4de589a1103 100644 --- a/solr/core/src/java/org/apache/solr/blockcache/Metrics.java +++ b/solr/core/src/java/org/apache/solr/blockcache/Metrics.java @@ -60,14 +60,15 @@ public class Metrics extends SolrCacheBase implements SolrInfoBean { var baseAttributes = attributes.toBuilder().put(CATEGORY_ATTR, getCategory().toString()).build(); var blockcacheStats = - solrMetricsContext.longMeasurement("solr_block_cache_stats", "Block cache stats"); + solrMetricsContext.longGaugeMeasurement("solr_block_cache_stats", "Block cache stats"); var hitRatio = - solrMetricsContext.doubleMeasurement("solr_block_cache_hit_ratio", "Block cache hit ratio"); + solrMetricsContext.doubleGaugeMeasurement( + "solr_block_cache_hit_ratio", "Block cache hit ratio"); var perSecStats = - solrMetricsContext.doubleMeasurement( + solrMetricsContext.doubleGaugeMeasurement( "solr_block_cache_stats_per_second", "Block cache per second stats"); var bufferCacheStats = - solrMetricsContext.doubleMeasurement( + solrMetricsContext.doubleGaugeMeasurement( "solr_buffer_cache_stats", "Buffer cache per second stats"); this.toClose = 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 cfcdbd451c1..6590349ad87 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -28,6 +28,7 @@ import static org.apache.solr.common.params.CommonParams.ZK_PATH; import static org.apache.solr.common.params.CommonParams.ZK_STATUS_PATH; import static org.apache.solr.metrics.SolrMetricProducer.CATEGORY_ATTR; import static org.apache.solr.metrics.SolrMetricProducer.HANDLER_ATTR; +import static org.apache.solr.metrics.SolrMetricProducer.NAME_ATTR; import static org.apache.solr.search.SolrIndexSearcher.EXECUTOR_MAX_CPU_THREADS; import static org.apache.solr.security.AuthenticationPlugin.AUTHENTICATION_PLUGIN_PROP; @@ -135,6 +136,7 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.search.CacheConfig; +import org.apache.solr.search.CaffeineCache; import org.apache.solr.search.SolrCache; import org.apache.solr.search.SolrFieldCacheBean; import org.apache.solr.search.SolrIndexSearcher; @@ -802,8 +804,12 @@ public class CoreContainer { for (Map.Entry<String, CacheConfig> e : cachesConfig.entrySet()) { SolrCache<?, ?> c = e.getValue().newInstance(); String cacheName = e.getKey(); - // NOCOMMIT SOLR-17458: Add Otel - c.initializeMetrics(solrMetricsContext, Attributes.empty(), "nodeLevelCache/" + cacheName); + if (c instanceof CaffeineCache<?, ?> caffeineCache) { + caffeineCache.initializeMetrics( + solrMetricsContext, + Attributes.builder().put(NAME_ATTR, cacheName).build(), + "solr_node_cache"); + } m.put(cacheName, c); } this.caches = Collections.unmodifiableMap(m); 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 8118ab6c824..d42096c6b6e 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -21,6 +21,7 @@ import static org.apache.solr.handler.admin.MetricsHandler.OPEN_METRICS_WT; import static org.apache.solr.handler.admin.MetricsHandler.PROMETHEUS_METRICS_WT; import static org.apache.solr.metrics.SolrCoreMetricManager.COLLECTION_ATTR; import static org.apache.solr.metrics.SolrCoreMetricManager.CORE_ATTR; +import static org.apache.solr.metrics.SolrCoreMetricManager.REPLICA_ATTR; import static org.apache.solr.metrics.SolrCoreMetricManager.SHARD_ATTR; import com.github.benmanes.caffeine.cache.Cache; @@ -259,7 +260,7 @@ public class SolrCore implements SolrInfoBean, Closeable { private volatile boolean newSearcherReady = false; - private final Attributes coreAttributes; + private Attributes coreAttributes; private AttributedLongCounter newSearcherCounter; private AttributedLongCounter newSearcherMaxReachedCounter; private AttributedLongCounter newSearcherOtherErrorsCounter; @@ -556,6 +557,25 @@ public class SolrCore implements SolrInfoBean, Closeable { assert this.name != null; assert coreDescriptor.getCloudDescriptor() == null : "Cores are not renamed in SolrCloud"; this.name = Objects.requireNonNull(v); + initCoreAttributes(); + } + + public Attributes getCoreAttributes() { + return coreAttributes; + } + + private void initCoreAttributes() { + this.coreAttributes = + (coreContainer.isZooKeeperAware()) + ? Attributes.builder() + .put(COLLECTION_ATTR, coreDescriptor.getCollectionName()) + .put(CORE_ATTR, getName()) + .put(SHARD_ATTR, coreDescriptor.getCloudDescriptor().getShardId()) + .put( + REPLICA_ATTR, + Utils.parseMetricsReplicaName(coreDescriptor.getCollectionName(), getName())) + .build() + : Attributes.builder().put(CORE_ATTR, getName()).build(); } /** @@ -1089,17 +1109,6 @@ 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(); @@ -1120,6 +1129,8 @@ public class SolrCore implements SolrInfoBean, Closeable { checkVersionFieldExistsInSchema(schema, coreDescriptor); setLatestSchema(schema); + initCoreAttributes(); + // initialize core metrics initializeMetrics(solrMetricsContext, coreAttributes, ""); diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index b900dc22ddb..010d1090e48 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -841,7 +841,6 @@ public class ReplicationHandler extends RequestHandlerBase } } - // NOCOMMIT SOLR-17458: Update with OTEL @Override public void initializeMetrics( SolrMetricsContext parentContext, Attributes attributes, String scope) { @@ -853,51 +852,51 @@ public class ReplicationHandler extends RequestHandlerBase super.initializeMetrics(parentContext, replicationAttributes, scope); ObservableLongMeasurement indexSizeMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_index_size", "Size of the index in bytes", OtelUnit.BYTES); ObservableLongMeasurement indexVersionMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_index_version", "Current index version"); ObservableLongMeasurement indexGenerationMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_index_generation", "Current index generation"); ObservableLongMeasurement isLeaderMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_is_leader", "Whether this node is a leader (1) or not (0)"); ObservableLongMeasurement isFollowerMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_is_follower", "Whether this node is a follower (1) or not (0)"); ObservableLongMeasurement replicationEnabledMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_is_enabled", "Whether replication is enabled (1) or not (0)"); ObservableLongMeasurement isPollingDisabledMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_is_polling_disabled", "Whether polling is disabled (1) or not (0)"); ObservableLongMeasurement isReplicatingMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_is_replicating", "Whether replication is in progress (1) or not (0)"); ObservableLongMeasurement timeElapsedMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_time_elapsed", "Time elapsed during replication in seconds", OtelUnit.SECONDS); ObservableLongMeasurement bytesDownloadedMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_downloaded_size", "Total bytes downloaded during replication", OtelUnit.BYTES); ObservableLongMeasurement downloadSpeedMetric = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_replication_download_speed", "Download speed in bytes per second"); metricsCallback = diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java index 6af631d6ebf..86c81c83bd8 100644 --- a/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java +++ b/solr/core/src/java/org/apache/solr/metrics/SolrCoreMetricManager.java @@ -20,7 +20,6 @@ import static org.apache.solr.metrics.SolrMetricProducer.HANDLER_ATTR; import com.codahale.metrics.MetricRegistry; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; @@ -128,19 +127,11 @@ public class SolrCoreMetricManager implements Closeable { // tracked producers. // There is some possible improvement that can be done here to not have to duplicate code in // registerMetricProducer - var attributes = - Attributes.builder() - .put(CORE_ATTR, core.getName()) - .put(COLLECTION_ATTR, collectionName) - .put(SHARD_ATTR, shardName) - .put(REPLICA_ATTR, replicaName) - .build(); - - core.initializeMetrics(solrMetricsContext, attributes, core.getName()); + core.initializeMetrics(solrMetricsContext, core.getCoreAttributes(), core.getName()); registeredProducers.forEach( metricProducer -> { - var producerAttributes = attributes.toBuilder(); + var producerAttributes = core.getCoreAttributes().toBuilder(); if (metricProducer.scope().startsWith("/")) producerAttributes.put(HANDLER_ATTR, metricProducer.scope); metricProducer.producer.initializeMetrics( @@ -173,12 +164,7 @@ public class SolrCoreMetricManager implements Closeable { // reregisterCoreMetrics // There is some possible improvement that can be done here to not have to duplicate code in // reregisterCoreMetrics - var attributesBuilder = - Attributes.builder() - .put(CORE_ATTR, core.getName()) - .put(COLLECTION_ATTR, collectionName) - .put(SHARD_ATTR, shardName) - .put(REPLICA_ATTR, replicaName); + var attributesBuilder = core.getCoreAttributes().toBuilder(); if (scope.startsWith("/")) attributesBuilder.put(HANDLER_ATTR, scope); producer.initializeMetrics(solrMetricsContext, attributesBuilder.build(), scope); } diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java index 98d18cea32f..b4e1e708d86 100644 --- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java +++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricManager.java @@ -389,16 +389,26 @@ public class SolrMetricManager { .batchCallback(callback, measurement, additionalMeasurements); } - ObservableLongMeasurement longMeasurement( + ObservableLongMeasurement longGaugeMeasurement( String registry, String gaugeName, String description, OtelUnit unit) { return longGaugeBuilder(registry, gaugeName, description, unit).buildObserver(); } - ObservableDoubleMeasurement doubleMeasurement( + ObservableDoubleMeasurement doubleGaugeMeasurement( String registry, String gaugeName, String description, OtelUnit unit) { return doubleGaugeBuilder(registry, gaugeName, description, unit).buildObserver(); } + ObservableLongMeasurement longCounterMeasurement( + String registry, String counterName, String description, OtelUnit unit) { + return longCounterBuilder(registry, counterName, description, unit).buildObserver(); + } + + ObservableDoubleMeasurement doubleCounterMeasurement( + String registry, String counterName, String description, OtelUnit unit) { + return doubleCounterBuilder(registry, counterName, description, unit).buildObserver(); + } + private LongGaugeBuilder longGaugeBuilder( String registry, String gaugeName, String description, OtelUnit unit) { LongGaugeBuilder builder = @@ -424,6 +434,31 @@ public class SolrMetricManager { return builder; } + private LongCounterBuilder longCounterBuilder( + String registry, String counterName, String description, OtelUnit unit) { + LongCounterBuilder builder = + meterProvider(registry) + .get(OTEL_SCOPE_NAME) + .counterBuilder(counterName) + .setDescription(description); + if (unit != null) builder.setUnit(unit.getSymbol()); + + return builder; + } + + private DoubleCounterBuilder doubleCounterBuilder( + String registry, String counterName, String description, OtelUnit unit) { + DoubleCounterBuilder builder = + meterProvider(registry) + .get(OTEL_SCOPE_NAME) + .counterBuilder(counterName) + .setDescription(description) + .ofDoubles(); + if (unit != null) builder.setUnit(unit.getSymbol()); + + return builder; + } + // for unit tests public MetricRegistry.MetricSupplier<Counter> getCounterSupplier() { return counterSupplier; diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java index 435b7c0051c..ea046b8fda5 100644 --- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java +++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricProducer.java @@ -27,6 +27,8 @@ public interface SolrMetricProducer extends AutoCloseable { public static final AttributeKey<String> CATEGORY_ATTR = AttributeKey.stringKey("category"); public static final AttributeKey<String> HANDLER_ATTR = AttributeKey.stringKey("handler"); public static final AttributeKey<String> OPERATION_ATTR = AttributeKey.stringKey("ops"); + public static final AttributeKey<String> RESULT_ATTR = AttributeKey.stringKey("result"); + public static final AttributeKey<String> NAME_ATTR = AttributeKey.stringKey("name"); public static final AttributeKey<String> PLUGIN_NAME_ATTR = AttributeKey.stringKey("plugin_name"); /** diff --git a/solr/core/src/java/org/apache/solr/metrics/SolrMetricsContext.java b/solr/core/src/java/org/apache/solr/metrics/SolrMetricsContext.java index 696ace6aa0d..05df56eba88 100644 --- a/solr/core/src/java/org/apache/solr/metrics/SolrMetricsContext.java +++ b/solr/core/src/java/org/apache/solr/metrics/SolrMetricsContext.java @@ -276,22 +276,41 @@ public class SolrMetricsContext { registryName, metricName, description, callback, unit); } - public ObservableLongMeasurement longMeasurement(String metricName, String description) { - return longMeasurement(metricName, description, null); + public ObservableLongMeasurement longGaugeMeasurement(String metricName, String description) { + return longGaugeMeasurement(metricName, description, null); } - public ObservableLongMeasurement longMeasurement( + public ObservableLongMeasurement longGaugeMeasurement( String metricName, String description, OtelUnit unit) { - return metricManager.longMeasurement(registryName, metricName, description, unit); + return metricManager.longGaugeMeasurement(registryName, metricName, description, unit); } - public ObservableDoubleMeasurement doubleMeasurement(String metricName, String description) { - return doubleMeasurement(metricName, description, null); + public ObservableDoubleMeasurement doubleGaugeMeasurement(String metricName, String description) { + return doubleGaugeMeasurement(metricName, description, null); } - public ObservableDoubleMeasurement doubleMeasurement( + public ObservableDoubleMeasurement doubleGaugeMeasurement( String metricName, String description, OtelUnit unit) { - return metricManager.doubleMeasurement(registryName, metricName, description, unit); + return metricManager.doubleGaugeMeasurement(registryName, metricName, description, unit); + } + + public ObservableLongMeasurement longCounterMeasurement(String metricName, String description) { + return longCounterMeasurement(metricName, description, null); + } + + public ObservableLongMeasurement longCounterMeasurement( + String metricName, String description, OtelUnit unit) { + return metricManager.longCounterMeasurement(registryName, metricName, description, unit); + } + + public ObservableDoubleMeasurement doubleCounterMeasurement( + String metricName, String description) { + return doubleCounterMeasurement(metricName, description, null); + } + + public ObservableDoubleMeasurement doubleCounterMeasurement( + String metricName, String description, OtelUnit unit) { + return metricManager.doubleCounterMeasurement(registryName, metricName, description, unit); } public BatchCallback batchCallback( diff --git a/solr/core/src/java/org/apache/solr/search/CaffeineCache.java b/solr/core/src/java/org/apache/solr/search/CaffeineCache.java index 8251cd0be96..601bdf80e11 100644 --- a/solr/core/src/java/org/apache/solr/search/CaffeineCache.java +++ b/solr/core/src/java/org/apache/solr/search/CaffeineCache.java @@ -16,6 +16,10 @@ */ package org.apache.solr.search; +import static org.apache.solr.metrics.SolrMetricProducer.CATEGORY_ATTR; +import static org.apache.solr.metrics.SolrMetricProducer.OPERATION_ATTR; +import static org.apache.solr.metrics.SolrMetricProducer.RESULT_ATTR; + import com.github.benmanes.caffeine.cache.AsyncCache; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; @@ -23,8 +27,8 @@ import com.github.benmanes.caffeine.cache.Policy.Eviction; import com.github.benmanes.caffeine.cache.RemovalCause; import com.github.benmanes.caffeine.cache.RemovalListener; import com.github.benmanes.caffeine.cache.stats.CacheStats; -import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.invoke.MethodHandles; @@ -43,8 +47,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.RamUsageEstimator; -import org.apache.solr.metrics.MetricsMap; +import org.apache.solr.common.util.IOUtils; import org.apache.solr.metrics.SolrMetricsContext; +import org.apache.solr.metrics.otel.OtelUnit; import org.apache.solr.util.IOFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,8 +104,8 @@ public class CaffeineCache<K, V> extends SolrCacheBase private boolean cleanupThread; private boolean async; - private MetricsMap cacheMap; private SolrMetricsContext solrMetricsContext; + private AutoCloseable toClose; private long initialRamBytes = 0; private final LongAdder ramBytes = new LongAdder(); @@ -325,13 +330,14 @@ public class CaffeineCache<K, V> extends SolrCacheBase @Override public void close() throws IOException { - SolrCache.super.close(); cache.invalidateAll(); cache.cleanUp(); if (executor instanceof ExecutorService) { ((ExecutorService) executor).shutdownNow(); } ramBytes.reset(); + IOUtils.closeQuietly(toClose); + SolrCache.super.close(); } @Override @@ -458,12 +464,6 @@ public class CaffeineCache<K, V> extends SolrCacheBase return description; } - // for unit tests only - @VisibleForTesting - MetricsMap getMetricsMap() { - return cacheMap; - } - @Override public SolrMetricsContext getSolrMetricsContext() { return solrMetricsContext; @@ -471,47 +471,72 @@ public class CaffeineCache<K, V> extends SolrCacheBase @Override public String toString() { - return name() + (cacheMap != null ? cacheMap.getValue().toString() : ""); + return name(); } - // TODO SOLR-17458: Migrate to Otel @Override public void initializeMetrics( - SolrMetricsContext parentContext, Attributes attributes, String scope) { + SolrMetricsContext parentContext, Attributes attributes, String metricName) { + Attributes cacheAttributes = + attributes.toBuilder().put(CATEGORY_ATTR, getCategory().toString()).build(); solrMetricsContext = parentContext.getChildContext(this); - cacheMap = - new MetricsMap( - map -> { - if (cache != null) { - CacheStats stats = cache.stats(); - long hitCount = stats.hitCount() + hits.sum(); - long insertCount = inserts.sum(); - long lookupCount = stats.requestCount() + lookups.sum(); - - map.put(LOOKUPS_PARAM, lookupCount); - map.put(HITS_PARAM, hitCount); - map.put(HIT_RATIO_PARAM, hitRate(hitCount, lookupCount)); - map.put(INSERTS_PARAM, insertCount); - map.put(EVICTIONS_PARAM, stats.evictionCount()); - map.put(SIZE_PARAM, cache.asMap().size()); - map.put("warmupTime", warmupTime); - map.put(RAM_BYTES_USED_PARAM, ramBytesUsed()); - map.put(MAX_RAM_MB_PARAM, getMaxRamMB()); - - CacheStats cumulativeStats = priorStats.plus(stats); - long cumLookups = priorLookups + lookupCount; - long cumHits = priorHits + hitCount; - map.put("cumulative_lookups", cumLookups); - map.put("cumulative_hits", cumHits); - map.put("cumulative_hitratio", hitRate(cumHits, cumLookups)); - map.put("cumulative_inserts", priorInserts + insertCount); - map.put("cumulative_evictions", cumulativeStats.evictionCount()); - } - }); - solrMetricsContext.gauge(cacheMap, true, scope, getCategory().toString()); - } - private static double hitRate(long hitCount, long lookupCount) { - return lookupCount == 0 ? 1.0 : (double) hitCount / lookupCount; + ObservableLongMeasurement cacheLookupsMetric = + solrMetricsContext.longCounterMeasurement( + metricName + "_lookups", "Number of cumulative cache lookup results (hits and misses)"); + + ObservableLongMeasurement cacheOperationMetric = + solrMetricsContext.longCounterMeasurement( + metricName + "_ops", "Number of cumulative cache operations (inserts and evictions)"); + + ObservableLongMeasurement sizeMetric = + solrMetricsContext.longGaugeMeasurement( + metricName + "_size", "Current number cache entries"); + + ObservableLongMeasurement ramBytesUsedMetric = + solrMetricsContext.longGaugeMeasurement( + metricName + "_ram_used", "RAM bytes used by cache", OtelUnit.BYTES); + + ObservableLongMeasurement warmupTimeMetric = + solrMetricsContext.longGaugeMeasurement( + metricName + "_warmup_time", "Cache warmup time (most recent)", OtelUnit.MILLISECONDS); + + this.toClose = + solrMetricsContext.batchCallback( + () -> { + if (cache == null) { + return; + } + CacheStats stats = cache.stats(); + long hitCount = stats.hitCount() + hits.sum(); + long lookupCount = stats.requestCount() + lookups.sum(); + long insertCount = inserts.sum(); + + sizeMetric.record(cache.asMap().size(), cacheAttributes); + ramBytesUsedMetric.record(ramBytesUsed(), cacheAttributes); + warmupTimeMetric.record(warmupTime, cacheAttributes); + + CacheStats cumulativeStats = priorStats.plus(stats); + long cumLookups = priorLookups + lookupCount; + long cumHits = priorHits + hitCount; + + cacheLookupsMetric.record( + cumHits, cacheAttributes.toBuilder().put(RESULT_ATTR, "hit").build()); + cacheLookupsMetric.record( + cumLookups - cumHits, + cacheAttributes.toBuilder().put(RESULT_ATTR, "miss").build()); + + cacheOperationMetric.record( + priorInserts + insertCount, + cacheAttributes.toBuilder().put(OPERATION_ATTR, "inserts").build()); + cacheOperationMetric.record( + cumulativeStats.evictionCount(), + cacheAttributes.toBuilder().put(OPERATION_ATTR, "evictions").build()); + }, + cacheLookupsMetric, + cacheOperationMetric, + sizeMetric, + ramBytesUsedMetric, + warmupTimeMetric); } } 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 d341ed743c2..3289d42cf25 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java +++ b/solr/core/src/java/org/apache/solr/search/SolrFieldCacheBean.java @@ -60,10 +60,10 @@ public class SolrFieldCacheBean implements SolrInfoBean { SolrMetricsContext parentContext, Attributes attributes, String scope) { this.solrMetricsContext = parentContext; var solrCacheStats = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_field_cache_entries", "Number of field cache entries"); var solrCacheSize = - solrMetricsContext.longMeasurement( + solrMetricsContext.longGaugeMeasurement( "solr_field_cache_size", "Size of field cache in bytes", OtelUnit.BYTES); this.toClose = solrMetricsContext.batchCallback( diff --git a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java index e92b1001b79..235160ec28b 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java +++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java @@ -106,7 +106,6 @@ import org.apache.solr.core.SolrCore; import org.apache.solr.core.SolrInfoBean; import org.apache.solr.index.SlowCompositeReaderWrapper; import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetricsContext; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestInfo; @@ -603,10 +602,9 @@ public class SolrIndexSearcher extends IndexSearcher implements Closeable, SolrI this.solrMetricsContext = core.getSolrMetricsContext().getChildContext(this); for (SolrCache<?, ?> cache : cacheList) { cache.initializeMetrics( - // TODO SOLR-17458: Add Otel solrMetricsContext, - Attributes.empty(), - SolrMetricManager.mkName(cache.name(), STATISTICS_KEY)); + core.getCoreAttributes().toBuilder().put(NAME_ATTR, cache.name()).build(), + "solr_searcher_cache"); } // TODO SOLR-17458: Add Otel initializeMetrics(solrMetricsContext, Attributes.empty(), STATISTICS_KEY); diff --git a/solr/core/src/java/org/apache/solr/update/SolrIndexWriter.java b/solr/core/src/java/org/apache/solr/update/SolrIndexWriter.java index 7fd5e434d6f..75bd8ddc311 100644 --- a/solr/core/src/java/org/apache/solr/update/SolrIndexWriter.java +++ b/solr/core/src/java/org/apache/solr/update/SolrIndexWriter.java @@ -16,15 +16,10 @@ */ package org.apache.solr.update; -import static org.apache.solr.metrics.SolrCoreMetricManager.COLLECTION_ATTR; -import static org.apache.solr.metrics.SolrCoreMetricManager.CORE_ATTR; -import static org.apache.solr.metrics.SolrCoreMetricManager.REPLICA_ATTR; -import static org.apache.solr.metrics.SolrCoreMetricManager.SHARD_ATTR; import static org.apache.solr.metrics.SolrMetricProducer.CATEGORY_ATTR; import static org.apache.solr.metrics.SolrMetricProducer.TYPE_ATTR; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.ObservableLongGauge; import java.io.IOException; import java.lang.invoke.MethodHandles; @@ -44,7 +39,6 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.util.InfoStream; import org.apache.solr.common.util.IOUtils; import org.apache.solr.common.util.SuppressForbidden; -import org.apache.solr.common.util.Utils; import org.apache.solr.core.DirectoryFactory; import org.apache.solr.core.DirectoryFactory.DirContext; import org.apache.solr.core.SolrCore; @@ -191,19 +185,10 @@ public class SolrIndexWriter extends IndexWriter { } else { mergeTotals = false; } - String coreName = core.getCoreDescriptor().getName(); - var baseAttributesBuilder = - Attributes.builder() + var baseAttributes = + core.getCoreAttributes().toBuilder() .put(CATEGORY_ATTR, SolrInfoBean.Category.INDEX.toString()) - .put(CORE_ATTR, coreName); - if (core.getCoreContainer().isZooKeeperAware()) { - String collectionName = core.getCoreDescriptor().getCollectionName(); - baseAttributesBuilder - .put(COLLECTION_ATTR, collectionName) - .put(SHARD_ATTR, core.getCoreDescriptor().getCloudDescriptor().getShardId()) - .put(REPLICA_ATTR, Utils.parseMetricsReplicaName(collectionName, coreName)); - } - var baseAttributes = baseAttributesBuilder.build(); + .build(); if (mergeDetails) { mergeTotals = true; // override majorMergedDocs = diff --git a/solr/core/src/test/org/apache/solr/CursorPagingTest.java b/solr/core/src/test/org/apache/solr/CursorPagingTest.java index 45689e2cf21..0babb49496c 100644 --- a/solr/core/src/test/org/apache/solr/CursorPagingTest.java +++ b/solr/core/src/test/org/apache/solr/CursorPagingTest.java @@ -45,11 +45,11 @@ import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.CursorMarkParams; import org.apache.solr.common.params.GroupParams; import org.apache.solr.common.params.SolrParams; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; +import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.CursorMark; import org.apache.solr.util.LogLevel; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.After; import org.junit.BeforeClass; @@ -715,30 +715,19 @@ public class CursorPagingTest extends SolrTestCaseJ4 { final Collection<String> allFieldNames = getAllSortFieldNames(); - final MetricsMap filterCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.filterCache")) - .getGauge(); - assertNotNull(filterCacheStats); - final MetricsMap queryCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.queryResultCache")) - .getGauge(); - assertNotNull(queryCacheStats); - - final long preQcIn = (Long) queryCacheStats.getValue().get("inserts"); - final long preFcIn = (Long) filterCacheStats.getValue().get("inserts"); - final long preFcHits = (Long) filterCacheStats.getValue().get("hits"); + SolrCore solrCore = h.getCore(); + + // Get initial metrics for filter and query cache + var preFilterInserts = + SolrMetricTestUtils.getCacheSearcherOpsInserts(solrCore, SolrMetricTestUtils.FILTER_CACHE) + .getValue(); + var preFilterHits = + SolrMetricTestUtils.getCacheSearcherOpsHits(solrCore, SolrMetricTestUtils.FILTER_CACHE) + .getValue(); + var preQueryInserts = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + solrCore, SolrMetricTestUtils.QUERY_RESULT_CACHE) + .getValue(); SentinelIntSet ids = assertFullWalkNoDups( @@ -753,13 +742,21 @@ public class CursorPagingTest extends SolrTestCaseJ4 { assertEquals(6, ids.size()); - final long postQcIn = (Long) queryCacheStats.getValue().get("inserts"); - final long postFcIn = (Long) filterCacheStats.getValue().get("inserts"); - final long postFcHits = (Long) filterCacheStats.getValue().get("hits"); - - assertEquals("query cache inserts changed", preQcIn, postQcIn); - assertEquals("filter cache did not grow correctly", 2, postFcIn - preFcIn); - assertTrue("filter cache did not have any new cache hits", 0 < postFcHits - preFcHits); + var postFilterInserts = + SolrMetricTestUtils.getCacheSearcherOpsInserts(solrCore, SolrMetricTestUtils.FILTER_CACHE) + .getValue(); + var postFilterHits = + SolrMetricTestUtils.getCacheSearcherOpsHits(solrCore, SolrMetricTestUtils.FILTER_CACHE) + .getValue(); + var postQueryInserts = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + solrCore, SolrMetricTestUtils.QUERY_RESULT_CACHE) + .getValue(); + + assertEquals("query cache inserts changed", preQueryInserts, postQueryInserts, 0.0); + assertEquals( + "filter cache did not grow correctly", 2, postFilterInserts - preFilterInserts, 0.0); + assertTrue("filter cache did not have any new cache hits", 0 < postFilterHits - preFilterHits); } /** randomized testing of a non-trivial number of docs using assertFullWalkNoDups */ diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java index cbadc2afdd9..0eeb3b463db 100644 --- a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java +++ b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java @@ -1012,14 +1012,15 @@ public class TestSolrConfigHandler extends RestTestBase { public void testCacheDisableSolrConfig() throws Exception { RESTfulServerProvider oldProvider = restTestHarness.getServerProvider(); restTestHarness.setServerProvider(() -> getBaseUrl()); - MapWriter confMap = getRespMap("/admin/metrics", restTestHarness); - assertNotNull( - confMap._get( - asList("metrics", "solr.core.collection1", "CACHE.searcher.fieldValueCache"), null)); + String prometheusMetrics = restTestHarness.query("/admin/metrics?wt=prometheus"); + assertTrue( + "fieldValueCache metrics should be present", + prometheusMetrics.contains("name=\"fieldValueCache\"")); // Here documentCache is disabled at initialization in SolrConfig - assertNull( - confMap._get( - asList("metrics", "solr.core.collection1", "CACHE.searcher.documentCache"), null)); + assertFalse( + "documentCache metrics should be absent", + prometheusMetrics.contains("name=\"documentCache\"")); + restTestHarness.setServerProvider(oldProvider); } @@ -1032,10 +1033,10 @@ public class TestSolrConfigHandler extends RestTestBase { assertEquals("399", overlay._getStr("overlay/props/query/documentCache/size")); // Setting size only will not enable the cache restTestHarness.setServerProvider(() -> getBaseUrl()); - MapWriter confMap = getRespMap("/admin/metrics", restTestHarness); - assertNull( - confMap._get( - asList("metrics", "solr.core.collection1", "CACHE.searcher.documentCache"), null)); + + String prometheusMetrics = restTestHarness.query("/admin/metrics?wt=prometheus"); + assertFalse(prometheusMetrics.contains("cache_name=\"documentCache\"")); + restTestHarness.setServerProvider(oldProvider); } @@ -1045,20 +1046,19 @@ public class TestSolrConfigHandler extends RestTestBase { String payload = "{'set-property' : { 'query.documentCache.enabled': true} }"; runConfigCommand(restTestHarness, "/config", payload); restTestHarness.setServerProvider(() -> getBaseUrl()); - MapWriter confMap = getRespMap("/admin/metrics", restTestHarness); - assertNotNull( - confMap._get( - asList("metrics", "solr.core.collection1", "CACHE.searcher.documentCache"), null)); + + String prometheusMetrics = restTestHarness.query("/admin/metrics?wt=prometheus"); + assertTrue(prometheusMetrics.contains("name=\"documentCache\"")); // Disabling Cache payload = "{ 'set-property' : { 'query.documentCache.enabled': false } }"; restTestHarness.setServerProvider(oldProvider); + runConfigCommand(restTestHarness, "/config", payload); restTestHarness.setServerProvider(() -> getBaseUrl()); - confMap = getRespMap("/admin/metrics", restTestHarness); - assertNull( - confMap._get( - asList("metrics", "solr.core.collection1", "CACHE.searcher.documentCache"), null)); + + prometheusMetrics = restTestHarness.query("/admin/metrics?wt=prometheus"); + assertFalse(prometheusMetrics.contains("name=\"documentCache\"")); restTestHarness.setServerProvider(oldProvider); } diff --git a/solr/core/src/test/org/apache/solr/core/TimeAllowedTest.java b/solr/core/src/test/org/apache/solr/core/TimeAllowedTest.java index 32521802d13..7a88a470a90 100644 --- a/solr/core/src/test/org/apache/solr/core/TimeAllowedTest.java +++ b/solr/core/src/test/org/apache/solr/core/TimeAllowedTest.java @@ -18,11 +18,11 @@ package org.apache.solr.core; import static org.apache.solr.common.util.Utils.fromJSONString; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; import java.util.Map; import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.BeforeClass; import org.junit.Test; @@ -110,25 +110,15 @@ public class TimeAllowedTest extends SolrTestCaseJ4 { public void testCacheAssumptions() throws Exception { String fq = "name:d*"; SolrCore core = h.getCore(); - MetricsMap filterCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - core.getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.filterCache")) - .getGauge(); - long fqInserts = (long) filterCacheStats.getValue().get("inserts"); - - MetricsMap queryCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - core.getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.queryResultCache")) - .getGauge(); - long qrInserts = (long) queryCacheStats.getValue().get("inserts"); + + CounterSnapshot.CounterDataPointSnapshot fqInsertsPre = + SolrMetricTestUtils.getCacheSearcherOpsInserts(core, SolrMetricTestUtils.FILTER_CACHE); + long fqInserts = (long) fqInsertsPre.getValue(); + + CounterSnapshot.CounterDataPointSnapshot qrInsertsPre = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + core, SolrMetricTestUtils.QUERY_RESULT_CACHE); + long qrInserts = (long) qrInsertsPre.getValue(); // This gets 0 docs back. Use 10000 instead of 1 for timeAllowed, and it gets 100 back and the // for loop below succeeds. @@ -143,25 +133,29 @@ public class TimeAllowedTest extends SolrTestCaseJ4 { "Should have partial results", (Boolean) (header.get(SolrQueryResponse.RESPONSE_HEADER_PARTIAL_RESULTS_KEY))); + CounterSnapshot.CounterDataPointSnapshot qrInsertsPost = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + core, SolrMetricTestUtils.QUERY_RESULT_CACHE); assertEquals( "Should NOT have inserted partial results in the cache!", - (long) queryCacheStats.getValue().get("inserts"), + (long) qrInsertsPost.getValue(), qrInserts); - assertEquals( - "Should NOT have another insert", - fqInserts, - (long) filterCacheStats.getValue().get("inserts")); + CounterSnapshot.CounterDataPointSnapshot fqInsertsPost = + SolrMetricTestUtils.getCacheSearcherOpsInserts(core, SolrMetricTestUtils.FILTER_CACHE); + assertEquals("Should NOT have another insert", fqInserts, (long) fqInsertsPost.getValue()); // At the end of all this, we should have no hits in the queryResultCache. response = JQ(req("q", "*:*", "fq", fq, "indent", "true", "timeAllowed", longTimeout)); // Check that we did insert this one. - assertEquals("Hits should still be 0", (long) filterCacheStats.getValue().get("hits"), 0L); - assertEquals( - "Inserts should be bumped", - (long) filterCacheStats.getValue().get("inserts"), - fqInserts + 1); + CounterSnapshot.CounterDataPointSnapshot fqHitsPost = + SolrMetricTestUtils.getCacheSearcherOpsHits(core, SolrMetricTestUtils.FILTER_CACHE); + assertEquals("Hits should still be 0", (long) fqHitsPost.getValue(), 0L); + + CounterSnapshot.CounterDataPointSnapshot fqInsertsPostSecond = + SolrMetricTestUtils.getCacheSearcherOpsInserts(core, SolrMetricTestUtils.FILTER_CACHE); + assertEquals("Inserts should be bumped", (long) fqInsertsPostSecond.getValue(), fqInserts + 1); res = (Map<?, ?>) fromJSONString(response); body = (Map<?, ?>) (res.get("response")); @@ -179,22 +173,24 @@ public class TimeAllowedTest extends SolrTestCaseJ4 { public void testQueryResults() throws Exception { String q = "name:e*"; SolrCore core = h.getCore(); - MetricsMap queryCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - core.getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.queryResultCache")) - .getGauge(); - Map<String, Object> nl = queryCacheStats.getValue(); - long inserts = (long) nl.get("inserts"); + + CounterSnapshot.CounterDataPointSnapshot insertsPre = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + core, SolrMetricTestUtils.QUERY_RESULT_CACHE); + long inserts = (long) insertsPre.getValue(); + + CounterSnapshot.CounterDataPointSnapshot hitsPre = + SolrMetricTestUtils.getCacheSearcherOpsHits(core, SolrMetricTestUtils.QUERY_RESULT_CACHE); + long hits = (long) hitsPre.getValue(); String response = JQ(req("q", q, "indent", "true", "timeAllowed", "1", "sleep", sleep)); // The queryResultCache should NOT get an entry here. - nl = queryCacheStats.getValue(); - assertEquals("Should NOT have inserted partial results!", inserts, (long) nl.get("inserts")); + CounterSnapshot.CounterDataPointSnapshot insertsPost = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + core, SolrMetricTestUtils.QUERY_RESULT_CACHE); + assertEquals( + "Should NOT have inserted partial results!", inserts, (long) insertsPost.getValue()); Map<?, ?> res = (Map<?, ?>) fromJSONString(response); Map<?, ?> body = (Map<?, ?>) (res.get("response")); @@ -208,9 +204,13 @@ public class TimeAllowedTest extends SolrTestCaseJ4 { response = JQ(req("q", q, "indent", "true", "timeAllowed", longTimeout)); // Check that we did insert this one. - Map<String, Object> nl2 = queryCacheStats.getValue(); - assertEquals("Hits should still be 0", (long) nl.get("hits"), (long) nl2.get("hits")); - assertTrue("Inserts should be bumped", inserts < (long) nl2.get("inserts")); + CounterSnapshot.CounterDataPointSnapshot hitsPost = + SolrMetricTestUtils.getCacheSearcherOpsHits(core, SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPost2 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + core, SolrMetricTestUtils.QUERY_RESULT_CACHE); + assertEquals("Hits should still be 0", hits, (long) hitsPost.getValue()); + assertTrue("Inserts should be bumped", inserts < (long) insertsPost2.getValue()); res = (Map<?, ?>) fromJSONString(response); body = (Map<?, ?>) (res.get("response")); diff --git a/solr/core/src/test/org/apache/solr/search/TestCaffeineCache.java b/solr/core/src/test/org/apache/solr/search/TestCaffeineCache.java index d70ea43a468..12d7813ec2d 100644 --- a/solr/core/src/test/org/apache/solr/search/TestCaffeineCache.java +++ b/solr/core/src/test/org/apache/solr/search/TestCaffeineCache.java @@ -16,10 +16,14 @@ */ package org.apache.solr.search; +import static org.apache.solr.metrics.SolrMetricProducer.NAME_ATTR; + import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.RemovalCause; import io.opentelemetry.api.common.Attributes; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; +import io.prometheus.metrics.model.snapshots.Labels; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -35,6 +39,7 @@ import org.apache.lucene.util.Accountable; import org.apache.solr.SolrTestCase; import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetricsContext; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.Test; /** Test for {@link CaffeineCache}. */ @@ -45,15 +50,18 @@ public class TestCaffeineCache extends SolrTestCase { String scope = TestUtil.randomSimpleString(random(), 2, 10); @Test - public void testSimple() { + public void testSimple() throws IOException { CaffeineCache<Integer, String> lfuCache = new CaffeineCache<>(); + CaffeineCache<Integer, String> newLFUCache = new CaffeineCache<>(); + String lfuCacheName = scope + "-1"; + String newLfuCacheName = scope + "-2"; + SolrMetricsContext solrMetricsContext = new SolrMetricsContext(metricManager, registry, "foo"); - // TODO SOLR-17458: Fix tests for OTEL - lfuCache.initializeMetrics(solrMetricsContext, Attributes.empty(), scope + "-1"); - CaffeineCache<Integer, String> newLFUCache = new CaffeineCache<>(); - // TODO SOLR-17458: Fix tests for OTEL - newLFUCache.initializeMetrics(solrMetricsContext, Attributes.empty(), scope + "-2"); + lfuCache.initializeMetrics( + solrMetricsContext, Attributes.of(NAME_ATTR, lfuCacheName), "solr_cache"); + newLFUCache.initializeMetrics( + solrMetricsContext, Attributes.of(NAME_ATTR, newLfuCacheName), "solr_cache"); Map<String, String> params = new HashMap<>(); params.put("size", "100"); @@ -69,31 +77,46 @@ public class TestCaffeineCache extends SolrTestCase { assertEquals("15", lfuCache.get(15)); assertEquals("75", lfuCache.get(75)); assertNull(lfuCache.get(110)); - Map<String, Object> nl = lfuCache.getMetricsMap().getValue(); - assertEquals(3L, nl.get("lookups")); - assertEquals(2L, nl.get("hits")); - assertEquals(101L, nl.get("inserts")); - assertNull(lfuCache.get(1)); // first item put in should be the first out + var prometheusReader = metricManager.getPrometheusMetricReader(registry); + + var hitDatapoint = getCacheLookup(prometheusReader, lfuCacheName, "hit").getValue(); + var missDatapoint = getCacheLookup(prometheusReader, lfuCacheName, "miss").getValue(); + var insertDatapoint = getCacheOperation(prometheusReader, lfuCacheName, "inserts").getValue(); + var evictionsDatapoint = + getCacheOperation(prometheusReader, lfuCacheName, "evictions").getValue(); + assertEquals(3.0, hitDatapoint + missDatapoint, 0.001); // total lookups + assertEquals(2.0, hitDatapoint, 0.001); + assertEquals(101.0, insertDatapoint, 0.001); + assertNull(lfuCache.get(1)); // Test autowarming newLFUCache.init(params, initObj, regenerator); newLFUCache.warm(null, lfuCache); newLFUCache.setState(SolrCache.State.LIVE); - newLFUCache.put(103, "103"); assertEquals("15", newLFUCache.get(15)); assertEquals("75", newLFUCache.get(75)); assertNull(newLFUCache.get(50)); - nl = newLFUCache.getMetricsMap().getValue(); - assertEquals(3L, nl.get("lookups")); - assertEquals(2L, nl.get("hits")); - assertEquals(1L, nl.get("inserts")); - assertEquals(0L, nl.get("evictions")); - - assertEquals(7L, nl.get("cumulative_lookups")); - assertEquals(4L, nl.get("cumulative_hits")); - assertEquals(102L, nl.get("cumulative_inserts")); + + var cumHitDatapoint = getCacheLookup(prometheusReader, newLfuCacheName, "hit").getValue(); + var cumMissDatapoint = getCacheLookup(prometheusReader, newLfuCacheName, "miss").getValue(); + var cumInsertDatapoint = + getCacheOperation(prometheusReader, newLfuCacheName, "inserts").getValue(); + var cumEvictionsDatapoint = + getCacheOperation(prometheusReader, newLfuCacheName, "evictions").getValue(); + var newHitDatapoint = cumHitDatapoint - hitDatapoint; + var newMissDatapoint = cumMissDatapoint - missDatapoint; + var newInsertDatapoint = cumInsertDatapoint - insertDatapoint; + var newEvictionDatapoint = cumEvictionsDatapoint - evictionsDatapoint; + assertEquals(3.0, newHitDatapoint + missDatapoint, 0.001); // total lookups + assertEquals(2.0, newMissDatapoint, 0.001); + assertEquals(1.0, newInsertDatapoint, 0.001); + assertEquals(0.0, newEvictionDatapoint, 0.001); + + assertEquals(7.0, cumHitDatapoint + cumMissDatapoint, 0.001); // total cumulative lookups + assertEquals(4.0, cumHitDatapoint, 0.001); + assertEquals(102.0, cumInsertDatapoint, 0.001); } @Test @@ -148,7 +171,7 @@ public class TestCaffeineCache extends SolrTestCase { int IDLE_TIME_SEC = 5; CountDownLatch removed = new CountDownLatch(1); AtomicReference<RemovalCause> removalCause = new AtomicReference<>(); - CaffeineCache<String, String> cache = + try (CaffeineCache<String, String> cache = new CaffeineCache<>() { @Override public void onRemoval(String key, String value, RemovalCause cause) { @@ -156,22 +179,23 @@ public class TestCaffeineCache extends SolrTestCase { removalCause.set(cause); removed.countDown(); } - }; - Map<String, String> params = new HashMap<>(); - params.put("size", "6"); - params.put("maxIdleTime", "" + IDLE_TIME_SEC); - cache.init(params, null, new NoOpRegenerator()); - - cache.put("foo", "bar"); - assertEquals("bar", cache.get("foo")); - // sleep for at least the idle time before inserting other entries - // the eviction is piggy-backed on put() - Thread.sleep(TimeUnit.SECONDS.toMillis(IDLE_TIME_SEC * 2)); - cache.put("abc", "xyz"); - boolean await = removed.await(30, TimeUnit.SECONDS); - assertTrue("did not expire entry in in time", await); - assertEquals(RemovalCause.EXPIRED, removalCause.get()); - assertNull(cache.get("foo")); + }) { + Map<String, String> params = new HashMap<>(); + params.put("size", "6"); + params.put("maxIdleTime", "" + IDLE_TIME_SEC); + cache.init(params, null, new NoOpRegenerator()); + + cache.put("foo", "bar"); + assertEquals("bar", cache.get("foo")); + // sleep for at least the idle time before inserting other entries + // the eviction is piggy-backed on put() + Thread.sleep(TimeUnit.SECONDS.toMillis(IDLE_TIME_SEC * 2)); + cache.put("abc", "xyz"); + boolean await = removed.await(30, TimeUnit.SECONDS); + assertTrue("did not expire entry in in time", await); + assertEquals(RemovalCause.EXPIRED, removalCause.get()); + assertNull(cache.get("foo")); + } } @Test @@ -358,4 +382,33 @@ public class TestCaffeineCache extends SolrTestCase { cache.close(); assertEquals(emptySize, cache.ramBytesUsed()); } + + private CounterSnapshot.CounterDataPointSnapshot getCacheOperation( + org.apache.solr.metrics.otel.FilterablePrometheusMetricReader prometheusReader, + String cacheName, + String operation) { + return SolrMetricTestUtils.getCounterDatapoint( + prometheusReader, + "solr_cache_ops", + Labels.builder() + .label("category", "CACHE") + .label("ops", operation) + .label("name", cacheName) + .label("otel_scope_name", "org.apache.solr") + .build()); + } + + private CounterSnapshot.CounterDataPointSnapshot getCacheLookup( + org.apache.solr.metrics.otel.FilterablePrometheusMetricReader prometheusReader, + String cacheName, + String result) { + var builder = + Labels.builder() + .label("category", "CACHE") + .label("name", cacheName) + .label("result", result) + .label("otel_scope_name", "org.apache.solr"); + return SolrMetricTestUtils.getCounterDatapoint( + prometheusReader, "solr_cache_lookups", builder.build()); + } } diff --git a/solr/core/src/test/org/apache/solr/search/TestFiltersQueryCaching.java b/solr/core/src/test/org/apache/solr/search/TestFiltersQueryCaching.java index 48733cca532..e2f47693ec4 100644 --- a/solr/core/src/test/org/apache/solr/search/TestFiltersQueryCaching.java +++ b/solr/core/src/test/org/apache/solr/search/TestFiltersQueryCaching.java @@ -16,17 +16,16 @@ */ package org.apache.solr.search; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.concurrent.Future; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.core.SolrCore; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.BeforeClass; import org.junit.Test; @@ -51,6 +50,14 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { assertU(commit()); } + private static CounterSnapshot.CounterDataPointSnapshot getFilterCacheInserts(SolrCore core) { + return SolrMetricTestUtils.getCacheSearcherOpsInserts(core, SolrMetricTestUtils.FILTER_CACHE); + } + + private static CounterSnapshot.CounterDataPointSnapshot getFilterCacheHits(SolrCore core) { + return SolrMetricTestUtils.getCacheSearcherOpsHits(core, SolrMetricTestUtils.FILTER_CACHE); + } + /** Reload the core to reset non-cumulative cache metrics. */ private void reloadAndWait() throws Exception { h.reload(); @@ -64,19 +71,12 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { waitSearcher[0].get(); } - private static Map<String, Object> lookupFilterCacheMetrics(SolrCore core) { - return ((MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - core.getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.filterCache")) - .getGauge()) - .getValue(); + private static long lookupFilterCacheInserts(SolrCore core) { + return (long) getFilterCacheInserts(core).getValue(); } - private static long lookupFilterCacheInserts(SolrCore core) { - return (long) lookupFilterCacheMetrics(core).get("inserts"); + private static long lookupFilterCacheHits(SolrCore core) { + return (long) getFilterCacheHits(core).getValue(); } @Test @@ -126,7 +126,6 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { reloadAndWait(); SolrCore core = h.getCore(); - Map<String, Object> filterCacheMetrics; final String termQuery2 = "{!term f=field_s v='d1'}"; final String filterTermQuery2 = "filter(" + termQuery2 + ")"; assertJQ( @@ -151,9 +150,8 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { "true", "fq", random().nextBoolean() ? termQuery : filterTermQuery)); - filterCacheMetrics = lookupFilterCacheMetrics(core); - assertEquals(2, (long) filterCacheMetrics.get("inserts")); // unchanged - assertEquals(1, (long) filterCacheMetrics.get("hits")); + assertEquals(2, lookupFilterCacheInserts(core)); // unchanged + assertEquals(1, lookupFilterCacheHits(core)); JQ( req( "q", @@ -162,9 +160,8 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { "true", "fq", random().nextBoolean() ? termQuery2 : filterTermQuery2)); - filterCacheMetrics = lookupFilterCacheMetrics(core); - assertEquals(2, (long) filterCacheMetrics.get("inserts")); // unchanged - assertEquals(2, (long) filterCacheMetrics.get("hits")); + assertEquals(2, lookupFilterCacheInserts(core)); // unchanged + assertEquals(2, lookupFilterCacheHits(core)); JQ( req( "q", @@ -181,9 +178,8 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { "*", "sort", "id asc")); - filterCacheMetrics = lookupFilterCacheMetrics(core); - assertEquals(2, (long) filterCacheMetrics.get("inserts")); // unchanged - assertEquals(4, (long) filterCacheMetrics.get("hits")); + assertEquals(2, lookupFilterCacheInserts(core)); // unchanged + assertEquals(4, lookupFilterCacheHits(core)); JQ( req( "q", @@ -200,9 +196,8 @@ public class TestFiltersQueryCaching extends SolrTestCaseJ4 { "*", "sort", "id asc")); - filterCacheMetrics = lookupFilterCacheMetrics(core); - assertEquals(3, (long) filterCacheMetrics.get("inserts")); // added top-level - assertEquals(6, (long) filterCacheMetrics.get("hits")); + assertEquals(3, lookupFilterCacheInserts(core)); // added top-level + assertEquals(6, lookupFilterCacheHits(core)); } @Test diff --git a/solr/core/src/test/org/apache/solr/search/TestMainQueryCaching.java b/solr/core/src/test/org/apache/solr/search/TestMainQueryCaching.java index 2cebca1ffe1..60cd7b9ab90 100644 --- a/solr/core/src/test/org/apache/solr/search/TestMainQueryCaching.java +++ b/solr/core/src/test/org/apache/solr/search/TestMainQueryCaching.java @@ -30,6 +30,7 @@ import org.apache.solr.common.util.SolrNamedThreadFactory; import org.apache.solr.core.SolrCore; import org.apache.solr.metrics.MetricsMap; import org.apache.solr.metrics.SolrMetricManager; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -96,16 +97,7 @@ public class TestMainQueryCaching extends SolrTestCaseJ4 { } private static long coreToInserts(SolrCore core, String cacheName) { - return (long) - ((MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - core.getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.".concat(cacheName))) - .getGauge()) - .getValue() - .get("inserts"); + return (long) SolrMetricTestUtils.getCacheSearcherOpsInserts(core, cacheName).getValue(); } private static long coreToSortCount(SolrCore core, String skipOrFull) { @@ -119,6 +111,7 @@ public class TestMainQueryCaching extends SolrTestCaseJ4 { .getValue(); } + // NOCOMMIT: Fix this once SolrIndexSearcher is migrated for OTEL private static long coreToMatchAllDocsInsertCount(SolrCore core) { return (long) coreToLiveDocsCacheMetrics(core).get("inserts"); } @@ -267,10 +260,11 @@ public class TestMainQueryCaching extends SolrTestCaseJ4 { Map<?, ?> body = (Map<?, ?>) (res.get("response")); SolrCore core = h.getCore(); assertEquals("Bad matchAllDocs insert count", 1, coreToMatchAllDocsInsertCount(core)); - assertEquals("Bad filterCache insert count", 0, coreToInserts(core, "filterCache")); + assertEquals( + "Bad filterCache insert count", 0, coreToInserts(core, SolrMetricTestUtils.FILTER_CACHE)); assertEquals("Bad full sort count", 0, coreToSortCount(core, "full")); assertEquals("Should have exactly " + ALL_DOCS, ALL_DOCS, (long) (body.get("numFound"))); - long queryCacheInsertCount = coreToInserts(core, "queryResultCache"); + long queryCacheInsertCount = coreToInserts(core, SolrMetricTestUtils.QUERY_RESULT_CACHE); if (queryCacheInsertCount == expectCounters[0]) { // should be a hit, so all insert/sort-count metrics remain unchanged. } else { @@ -422,7 +416,7 @@ public class TestMainQueryCaching extends SolrTestCaseJ4 { assertEquals( "Bad filterCache insert count", expectFilterCacheInsertCount, - coreToInserts(core, "filterCache")); + coreToInserts(core, SolrMetricTestUtils.FILTER_CACHE)); assertEquals("Bad full sort count", expectFullSortCount, coreToSortCount(core, "full")); assertEquals("Bad skip sort count", expectSkipSortCount, coreToSortCount(core, "skip")); assertEquals( diff --git a/solr/core/src/test/org/apache/solr/search/TestReRankQParserPlugin.java b/solr/core/src/test/org/apache/solr/search/TestReRankQParserPlugin.java index b3e05ae5cbb..e6856a19e93 100644 --- a/solr/core/src/test/org/apache/solr/search/TestReRankQParserPlugin.java +++ b/solr/core/src/test/org/apache/solr/search/TestReRankQParserPlugin.java @@ -26,8 +26,7 @@ import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -729,18 +728,11 @@ public class TestReRankQParserPlugin extends SolrTestCaseJ4 { "//result/doc[4]/str[@name='id'][.='3']", "//result/doc[5]/str[@name='id'][.='2']"); - MetricsMap metrics = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.queryResultCache")) - .getGauge(); - Map<String, Object> stats = metrics.getValue(); - - long inserts = (Long) stats.get("inserts"); + long inserts = + (long) + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE) + .getValue(); assertTrue(inserts > 0); @@ -770,9 +762,11 @@ public class TestReRankQParserPlugin extends SolrTestCaseJ4 { "//result/doc[4]/str[@name='id'][.='2']", "//result/doc[5]/str[@name='id'][.='1']"); - stats = metrics.getValue(); - - long inserts1 = (Long) stats.get("inserts"); + long inserts1 = + (long) + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE) + .getValue(); // Last query was added to the cache assertTrue(inserts1 > inserts); @@ -804,8 +798,11 @@ public class TestReRankQParserPlugin extends SolrTestCaseJ4 { "//result/doc[4]/str[@name='id'][.='2']", "//result/doc[5]/str[@name='id'][.='1']"); - stats = metrics.getValue(); - long inserts2 = (Long) stats.get("inserts"); + long inserts2 = + (long) + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE) + .getValue(); // Last query was NOT added to the cache assertEquals(inserts1, inserts2); diff --git a/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java b/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java index 4456934937c..166d17e8e94 100644 --- a/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java +++ b/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java @@ -32,9 +32,8 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.FacetParams; import org.apache.solr.common.params.ModifiableSolrParams; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.util.SolrMetricTestUtils; import org.apache.solr.util.SpatialUtils; import org.apache.solr.util.TestUtils; import org.junit.Before; @@ -369,21 +368,15 @@ public class TestSolr4Spatial2 extends SolrTestCaseJ4 { if (testCache) { // The tricky thing is verifying the cache works correctly... - MetricsMap cacheMetrics = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.perSegSpatialFieldCache_" + fieldName)) - .getGauge(); - assertEquals("1", cacheMetrics.getValue().get("cumulative_inserts").toString()); - assertEquals("0", cacheMetrics.getValue().get("cumulative_hits").toString()); + long inserts = getSpatialFieldCacheInserts(fieldName); + long hits = getSpatialFieldCacheHits(fieldName); + assertEquals(1, inserts); + assertEquals(0, hits); // Repeat the query earlier assertJQ(sameReq, "/response/numFound==1", "/response/docs/[0]/id=='1'"); - assertEquals("1", cacheMetrics.getValue().get("cumulative_hits").toString()); + hits = getSpatialFieldCacheHits(fieldName); + assertEquals(1, hits); assertEquals("1 segment", 1, getSearcher().getRawReader().leaves().size()); // Get key of first leaf reader -- this one contains the match for sure. @@ -404,18 +397,8 @@ public class TestSolr4Spatial2 extends SolrTestCaseJ4 { Object leafKey2 = getFirstLeafReaderKey(); // get the current instance of metrics - the old one may not represent the current cache // instance - cacheMetrics = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.perSegSpatialFieldCache_" + fieldName)) - .getGauge(); - assertEquals( - leafKey1.equals(leafKey2) ? "2" : "1", - cacheMetrics.getValue().get("cumulative_hits").toString()); + hits = getSpatialFieldCacheHits(fieldName); + assertEquals(leafKey1.equals(leafKey2) ? 2 : 1, hits); } if (testHeatmap) { @@ -456,6 +439,20 @@ public class TestSolr4Spatial2 extends SolrTestCaseJ4 { } } + private long getSpatialFieldCacheInserts(String fieldName) { + return (long) + SolrMetricTestUtils.getCacheSearcherOps( + h.getCore(), "perSegSpatialFieldCache_" + fieldName, "inserts") + .getValue(); + } + + private long getSpatialFieldCacheHits(String fieldName) { + return (long) + SolrMetricTestUtils.getCacheSearcherLookups( + h.getCore(), "perSegSpatialFieldCache_" + fieldName, "hit") + .getValue(); + } + protected SolrIndexSearcher getSearcher() { // neat trick; needn't deal with the hassle RefCounted return (SolrIndexSearcher) h.getCore().getInfoRegistry().get("searcher"); diff --git a/solr/core/src/test/org/apache/solr/search/TestSolrCachePerf.java b/solr/core/src/test/org/apache/solr/search/TestSolrCachePerf.java index 989140b28f0..8e9e4717676 100644 --- a/solr/core/src/test/org/apache/solr/search/TestSolrCachePerf.java +++ b/solr/core/src/test/org/apache/solr/search/TestSolrCachePerf.java @@ -16,7 +16,10 @@ */ package org.apache.solr.search; +import static org.apache.solr.metrics.SolrMetricProducer.NAME_ATTR; + import io.opentelemetry.api.common.Attributes; +import io.prometheus.metrics.model.snapshots.Labels; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -30,6 +33,7 @@ import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetricsContext; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.Before; import org.junit.Test; import org.junit.runners.model.MultipleFailureException; @@ -106,9 +110,10 @@ public class TestSolrCachePerf extends SolrTestCaseJ4 { CacheRegenerator cr = new NoOpRegenerator(); Object o = cache.init(params, null, cr); cache.setState(SolrCache.State.LIVE); - // TODO SOLR-17458: Fix test later cache.initializeMetrics( - new SolrMetricsContext(metricManager, "foo", "bar"), Attributes.empty(), "foo"); + new SolrMetricsContext(metricManager, "foo", "bar"), + Attributes.of(NAME_ATTR, "foo"), + "foo"); AtomicBoolean stop = new AtomicBoolean(); SummaryStatistics perImplRatio = ratioStats.computeIfAbsent(clazz.getSimpleName(), c -> new SummaryStatistics()); @@ -161,8 +166,20 @@ public class TestSolrCachePerf extends SolrTestCaseJ4 { throw new MultipleFailureException(new ArrayList<>(exceptions)); } long stopTime = System.nanoTime(); - Map<String, Object> metrics = cache.getSolrMetricsContext().getMetricsSnapshot(); - perImplRatio.addValue(Double.parseDouble(String.valueOf(metrics.get("CACHE.foo.hitratio")))); + + var hitRatioDatapoint = + SolrMetricTestUtils.getGaugeDatapoint( + metricManager.getPrometheusMetricReader( + cache.getSolrMetricsContext().getRegistryName()), + "solr_cache_hit_ratio", + Labels.builder() + .label("category", "CACHE") + .label("name", "foo") + .label("otel_scope_name", "org.apache.solr") + .build()); + + double hitRatio = hitRatioDatapoint != null ? hitRatioDatapoint.getValue() : 0.0; + perImplRatio.addValue(hitRatio); perImplTime.addValue((double) (stopTime - startTime)); cache.close(); } diff --git a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java index 0eef541f39f..e8a8b3562f9 100644 --- a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java +++ b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java @@ -23,6 +23,7 @@ import static org.apache.solr.util.QueryMatchers.phraseQuery; import static org.apache.solr.util.QueryMatchers.termQuery; import static org.hamcrest.core.StringContains.containsString; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -49,14 +50,13 @@ import org.apache.solr.common.params.MapSolrParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.Utils; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.parser.QueryParser; import org.apache.solr.query.FilterQuery; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.NumberType; import org.apache.solr.schema.SchemaField; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -579,54 +579,40 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 { delI("777"); assertU(commit()); // arg... commit no longer "commits" unless there has been a change. - final MetricsMap filterCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.filterCache")) - .getGauge(); - assertNotNull(filterCacheStats); - final MetricsMap queryCacheStats = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.queryResultCache")) - .getGauge(); - - assertNotNull(queryCacheStats); - - long inserts = (Long) filterCacheStats.getValue().get("inserts"); - long hits = (Long) filterCacheStats.getValue().get("hits"); + CounterSnapshot.CounterDataPointSnapshot filterInsertsInitial = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsInitial = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + + long inserts = (long) filterInsertsInitial.getValue(); + long hits = (long) filterHitsInitial.getValue(); assertJQ( req("q", "doesnotexist filter(id:1) filter(qqq_s:X) filter(abcdefg)"), "/response/numFound==2"); inserts += 3; - assertEquals( - "wrong number of inserts", - inserts, - ((Long) filterCacheStats.getValue().get("inserts")).longValue()); - assertEquals( - "wrong number of hits", hits, ((Long) filterCacheStats.getValue().get("hits")).longValue()); + CounterSnapshot.CounterDataPointSnapshot filterInsertsAfter1 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsAfter1 = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + assertEquals("wrong number of inserts", inserts, (long) filterInsertsAfter1.getValue()); + assertEquals("wrong number of hits", hits, (long) filterHitsAfter1.getValue()); assertJQ( req("q", "doesnotexist2 filter(id:1) filter(qqq_s:X) filter(abcdefg)"), "/response/numFound==2"); hits += 3; - assertEquals( - "wrong number of inserts", - inserts, - ((Long) filterCacheStats.getValue().get("inserts")).longValue()); - assertEquals( - "wrong number of hits", hits, ((Long) filterCacheStats.getValue().get("hits")).longValue()); + CounterSnapshot.CounterDataPointSnapshot filterInsertsAfter2 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsAfter2 = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + assertEquals("wrong number of inserts", inserts, (long) filterInsertsAfter2.getValue()); + assertEquals("wrong number of hits", hits, (long) filterHitsAfter2.getValue()); // make sure normal "fq" parameters also hit the cache the same way assertJQ( @@ -634,12 +620,13 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 { "/response/numFound==0"); hits += 3; - assertEquals( - "wrong number of inserts", - inserts, - ((Long) filterCacheStats.getValue().get("inserts")).longValue()); - assertEquals( - "wrong number of hits", hits, ((Long) filterCacheStats.getValue().get("hits")).longValue()); + CounterSnapshot.CounterDataPointSnapshot filterInsertsAfter3 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsAfter3 = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + assertEquals("wrong number of inserts", inserts, (long) filterInsertsAfter3.getValue()); + assertEquals("wrong number of hits", hits, (long) filterHitsAfter3.getValue()); // try a query deeply nested in a FQ assertJQ( @@ -652,12 +639,13 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 { inserts += 1; // +1 for top level fq hits += 3; - assertEquals( - "wrong number of inserts", - inserts, - ((Long) filterCacheStats.getValue().get("inserts")).longValue()); - assertEquals( - "wrong number of hits", hits, ((Long) filterCacheStats.getValue().get("hits")).longValue()); + CounterSnapshot.CounterDataPointSnapshot filterInsertsAfter4 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsAfter4 = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + assertEquals("wrong number of inserts", inserts, (long) filterInsertsAfter4.getValue()); + assertEquals("wrong number of hits", hits, (long) filterHitsAfter4.getValue()); // retry the complex FQ and make sure hashCode/equals works as expected w/ filter queries assertJQ( @@ -669,24 +657,26 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 { "/response/numFound==2"); hits += 1; // top-level fq should have been found. - assertEquals( - "wrong number of inserts", - inserts, - ((Long) filterCacheStats.getValue().get("inserts")).longValue()); - assertEquals( - "wrong number of hits", hits, ((Long) filterCacheStats.getValue().get("hits")).longValue()); + CounterSnapshot.CounterDataPointSnapshot filterInsertsAfter5 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsAfter5 = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + assertEquals("wrong number of inserts", inserts, (long) filterInsertsAfter5.getValue()); + assertEquals("wrong number of hits", hits, (long) filterHitsAfter5.getValue()); // try nested filter with multiple top-level args (i.e. a boolean query) assertJQ(req("q", "*:* +filter(id:1 filter(qqq_s:X) abcdefg)"), "/response/numFound==2"); hits += 1; // the inner filter inserts += 1; // the outer filter - assertEquals( - "wrong number of inserts", - inserts, - ((Long) filterCacheStats.getValue().get("inserts")).longValue()); - assertEquals( - "wrong number of hits", hits, ((Long) filterCacheStats.getValue().get("hits")).longValue()); + CounterSnapshot.CounterDataPointSnapshot filterInsertsAfter6 = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + CounterSnapshot.CounterDataPointSnapshot filterHitsAfter6 = + SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), SolrMetricTestUtils.FILTER_CACHE); + assertEquals("wrong number of inserts", inserts, (long) filterInsertsAfter6.getValue()); + assertEquals("wrong number of hits", hits, (long) filterHitsAfter6.getValue()); // test the score for a filter, and that default score is 0 assertJQ( diff --git a/solr/core/src/test/org/apache/solr/search/TestThinCache.java b/solr/core/src/test/org/apache/solr/search/TestThinCache.java index de14da997ea..00c7d7a02a1 100644 --- a/solr/core/src/test/org/apache/solr/search/TestThinCache.java +++ b/solr/core/src/test/org/apache/solr/search/TestThinCache.java @@ -17,19 +17,19 @@ package org.apache.solr.search; import io.opentelemetry.api.common.Attributes; +import io.prometheus.metrics.model.snapshots.Labels; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.lucene.tests.util.TestUtil; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.metrics.SolrMetricsContext; import org.apache.solr.util.EmbeddedSolrServerTestRule; +import org.apache.solr.util.SolrMetricTestUtils; import org.apache.solr.util.TestHarness; -import org.apache.solr.util.stats.MetricUtils; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -95,13 +95,11 @@ public class TestThinCache extends SolrTestCaseJ4 { ThinCache<Object, Integer, String> lfuCache = new ThinCache<>(); lfuCache.setBacking(cacheScope, backing); SolrMetricsContext solrMetricsContext = new SolrMetricsContext(metricManager, registry, "foo"); - // TODO SOLR-17458: Fix test later lfuCache.initializeMetrics(solrMetricsContext, Attributes.empty(), scope + "-1"); Object cacheScope2 = new Object(); ThinCache<Object, Integer, String> newLFUCache = new ThinCache<>(); newLFUCache.setBacking(cacheScope2, backing); - // TODO SOLR-17458: Fix test later newLFUCache.initializeMetrics(solrMetricsContext, Attributes.empty(), scope + "-2"); Map<String, String> params = new HashMap<>(); @@ -148,6 +146,8 @@ public class TestThinCache extends SolrTestCaseJ4 { @Test public void testInitCore() throws Exception { + String thinCacheName = "myNodeLevelCacheThin"; + String nodeCacheName = "myNodeLevelCache"; for (int i = 0; i < 20; i++) { assertU(adoc("id", Integer.toString(i))); } @@ -155,28 +155,60 @@ public class TestThinCache extends SolrTestCaseJ4 { assertQ(req("q", "*:*", "fq", "id:0")); assertQ(req("q", "*:*", "fq", "id:0")); assertQ(req("q", "*:*", "fq", "id:1")); - Map<String, Object> nodeMetricsSnapshot = - MetricUtils.convertMetrics( - h.getCoreContainer().getMetricManager().registry("solr.node"), - List.of( - "CACHE.nodeLevelCache/myNodeLevelCacheThin", - "CACHE.nodeLevelCache/myNodeLevelCache")); - Map<String, Object> coreMetricsSnapshot = - MetricUtils.convertMetrics( - h.getCore().getCoreMetricManager().getRegistry(), - List.of("CACHE.searcher.filterCache")); - - // check that metrics are accessible, and the core cache writes through to the node-level cache - Map<String, Number> assertions = Map.of("lookups", 3L, "hits", 1L, "inserts", 2L, "size", 2); - for (Map.Entry<String, Number> e : assertions.entrySet()) { - String key = e.getKey(); - Number val = e.getValue(); - assertEquals( - val, nodeMetricsSnapshot.get("CACHE.nodeLevelCache/myNodeLevelCacheThin.".concat(key))); - assertEquals(val, coreMetricsSnapshot.get("CACHE.searcher.filterCache.".concat(key))); - } + + assertEquals( + 3L, + getNodeCacheLookups(thinCacheName, "hit") + + getNodeCacheLookups(thinCacheName, "miss")); // total lookups + assertEquals(1L, getNodeCacheLookups(thinCacheName, "hit")); + assertEquals(2L, getNodeCacheOp(thinCacheName, "inserts")); + + assertEquals(2, getNodeCacheSize(thinCacheName)); // for the other node-level cache, simply check that metrics are accessible - assertEquals(0, nodeMetricsSnapshot.get("CACHE.nodeLevelCache/myNodeLevelCache.size")); + assertEquals(0, getNodeCacheSize(nodeCacheName)); + } + + private long getNodeCacheOp(String cacheName, String operation) { + var reader = h.getCoreContainer().getMetricManager().getPrometheusMetricReader("solr.node"); + return (long) + SolrMetricTestUtils.getCounterDatapoint( + reader, + "solr_node_cache_ops", + Labels.builder() + .label("category", "CACHE") + .label("ops", operation) + .label("name", cacheName) + .label("otel_scope_name", "org.apache.solr") + .build()) + .getValue(); + } + + private long getNodeCacheLookups(String cacheName, String result) { + var reader = h.getCoreContainer().getMetricManager().getPrometheusMetricReader("solr.node"); + var builder = + Labels.builder() + .label("category", "CACHE") + .label("name", cacheName) + .label("otel_scope_name", "org.apache.solr"); + if (result != null) builder.label("result", result); + + return (long) + SolrMetricTestUtils.getCounterDatapoint(reader, "solr_node_cache_lookups", builder.build()) + .getValue(); + } + + private long getNodeCacheSize(String cacheName) { + var reader = h.getCoreContainer().getMetricManager().getPrometheusMetricReader("solr.node"); + return (long) + SolrMetricTestUtils.getGaugeDatapoint( + reader, + "solr_node_cache_size", + Labels.builder() + .label("category", "CACHE") + .label("name", cacheName) + .label("otel_scope_name", "org.apache.solr") + .build()) + .getValue(); } } diff --git a/solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java b/solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java index 66826e34ae8..bed4212bbca 100644 --- a/solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java +++ b/solr/core/src/test/org/apache/solr/search/join/BJQParserTest.java @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.List; import java.util.ListIterator; import java.util.Locale; -import java.util.Map; import java.util.Objects; import javax.xml.xpath.XPathConstants; import org.apache.lucene.search.Query; @@ -31,13 +30,12 @@ import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.join.ScoreMode; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.QParser; import org.apache.solr.search.SyntaxError; import org.apache.solr.util.BaseTestHarness; import org.apache.solr.util.RandomNoReverseMergePolicyFactory; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.After; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -49,6 +47,8 @@ public class BJQParserTest extends SolrTestCaseJ4 { private static final String[] klm = new String[] {"k", "l", "m"}; private static final List<String> xyz = Arrays.asList("x", "y", "z"); private static final String[] abcdef = new String[] {"a", "b", "c", "d", "e", "f"}; + private static final String PER_SEG_FILTER_CACHE_NAME = "perSegFilter"; + private static final String FILTER_CACHE_NAME = "filterCache"; @ClassRule public static final TestRule noReverseMerge = RandomNoReverseMergePolicyFactory.createRule(); @@ -397,28 +397,15 @@ public class BJQParserTest extends SolrTestCaseJ4 { @Test public void testCacheHit() { - MetricsMap parentFilterCache = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.perSegFilter")) - .getGauge(); - MetricsMap filterCache = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper<?>) - h.getCore() - .getCoreMetricManager() - .getRegistry() - .getMetrics() - .get("CACHE.searcher.filterCache")) - .getGauge(); - - Map<String, Object> parentsBefore = parentFilterCache.getValue(); - - Map<String, Object> filtersBefore = filterCache.getValue(); + // Get initial values + long parentLookupsBefore = getCacheLookups(PER_SEG_FILTER_CACHE_NAME); + ; + long parentHitsBefore = getCacheHits(PER_SEG_FILTER_CACHE_NAME); + long parentInsertsBefore = getCacheInserts(PER_SEG_FILTER_CACHE_NAME); + ; + + long filterHitsBefore = getCacheHits(FILTER_CACHE_NAME); + long filterInsertsBefore = getCacheInserts(FILTER_CACHE_NAME); // it should be weird enough to be uniq String parentFilter = "parent_s:([a TO c] [d TO f])"; @@ -433,8 +420,7 @@ public class BJQParserTest extends SolrTestCaseJ4 { req("q", "*:*", "fq", "{!parent which=\"" + parentFilter + "\"}"), "//*[@numFound='6']"); - assertEquals( - "didn't hit fqCache yet ", 0L, delta("hits", filterCache.getValue(), filtersBefore)); + assertEquals("didn't hit fqCache yet ", 0L, getCacheHits(FILTER_CACHE_NAME) - filterHitsBefore); assertQ( "filter by join", @@ -444,28 +430,24 @@ public class BJQParserTest extends SolrTestCaseJ4 { assertEquals( "in cache mode every request lookups", 3, - delta("lookups", parentFilterCache.getValue(), parentsBefore)); + getCacheLookups(PER_SEG_FILTER_CACHE_NAME) - parentLookupsBefore); assertEquals( "last two lookups causes hits", 2, - delta("hits", parentFilterCache.getValue(), parentsBefore)); + getCacheHits(PER_SEG_FILTER_CACHE_NAME) - parentHitsBefore); assertEquals( "the first lookup gets insert", 1, - delta("inserts", parentFilterCache.getValue(), parentsBefore)); + getCacheInserts(PER_SEG_FILTER_CACHE_NAME) - parentInsertsBefore); assertEquals( "true join query was not in fqCache", 0L, - delta("hits", filterCache.getValue(), filtersBefore)); + getCacheHits(FILTER_CACHE_NAME) - filterHitsBefore); assertEquals( "true join query is cached in fqCache", 1L, - delta("inserts", filterCache.getValue(), filtersBefore)); - } - - private long delta(String key, Map<String, Object> a, Map<String, Object> b) { - return (Long) a.get(key) - (Long) b.get(key); + getCacheInserts(FILTER_CACHE_NAME) - filterInsertsBefore); } @Test @@ -662,4 +644,16 @@ public class BJQParserTest extends SolrTestCaseJ4 { assertU("should be noop", delI("12275")); assertU("most of the time", commit()); } + + private long getCacheHits(String cacheName) { + return (long) SolrMetricTestUtils.getCacheSearcherOpsHits(h.getCore(), cacheName).getValue(); + } + + private long getCacheLookups(String cacheName) { + return (long) SolrMetricTestUtils.getCacheSearcherTotalLookups(h.getCore(), cacheName); + } + + private long getCacheInserts(String cacheName) { + return (long) SolrMetricTestUtils.getCacheSearcherOpsInserts(h.getCore(), cacheName).getValue(); + } } diff --git a/solr/core/src/test/org/apache/solr/search/join/TestNestedDocsSort.java b/solr/core/src/test/org/apache/solr/search/join/TestNestedDocsSort.java index 00c58558527..710bdf75975 100644 --- a/solr/core/src/test/org/apache/solr/search/join/TestNestedDocsSort.java +++ b/solr/core/src/test/org/apache/solr/search/join/TestNestedDocsSort.java @@ -16,7 +16,6 @@ */ package org.apache.solr.search.join; -import java.util.Map; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.solr.SolrTestCaseJ4; @@ -26,6 +25,7 @@ import org.apache.solr.search.SolrCache; import org.apache.solr.search.SortSpec; import org.apache.solr.search.SortSpecParsing; import org.apache.solr.util.RandomNoReverseMergePolicyFactory; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; @@ -136,23 +136,21 @@ public class TestNestedDocsSort extends SolrTestCaseJ4 { @SuppressWarnings({"rawtypes"}) final SolrCache cache = req.getSearcher().getCache("perSegFilter"); assertNotNull(cache); - final Map<String, Object> state = cache.getSolrMetricsContext().getMetricsSnapshot(); - String lookupsKey = null; - for (String key : state.keySet()) { - if (key.endsWith(".lookups")) { - lookupsKey = key; - break; - } - } - Number before = (Number) state.get(lookupsKey); + var core = req.getSearcher().getCore(); + double before = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + core, SolrMetricTestUtils.PER_SEG_FILTER_CACHE); + parse("childfield(name_s1,$q) asc"); - Number after = (Number) cache.getSolrMetricsContext().getMetricsSnapshot().get(lookupsKey); + double after = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + core, SolrMetricTestUtils.PER_SEG_FILTER_CACHE); assertEquals( "parsing bjq lookups parent filter," + "parsing sort spec lookups parent and child filters, " + "hopefully for the purpose", 3, - after.intValue() - before.intValue()); + (int) (after - before)); } finally { req.close(); } diff --git a/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java b/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java index e3433329d06..662cda05439 100644 --- a/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java +++ b/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java @@ -16,25 +16,23 @@ */ package org.apache.solr.search.join; -import com.codahale.metrics.Metric; +import io.prometheus.metrics.model.snapshots.CounterSnapshot; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Random; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.ScoreMode; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; -import org.apache.solr.metrics.MetricsMap; -import org.apache.solr.metrics.SolrMetricManager; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestInfo; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.search.QParser; import org.apache.solr.search.SolrCache; +import org.apache.solr.util.SolrMetricTestUtils; import org.junit.BeforeClass; import org.junit.Ignore; @@ -228,19 +226,17 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { public void testCacheHit() throws Exception { indexDataForScoring(); - Map<String, Metric> metrics = - h.getCoreContainer() - .getMetricManager() - .registry(h.getCore().getCoreMetricManager().getRegistryName()) - .getMetrics(); - - @SuppressWarnings("rawtypes") - MetricsMap mm = - (MetricsMap) - ((SolrMetricManager.GaugeWrapper) metrics.get("CACHE.searcher.queryResultCache")) - .getGauge(); { - Map<String, Object> statPre = mm.getValue(); + double lookupsPre = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPre = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPre = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + h.query( req( "q", @@ -249,11 +245,21 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { "id", "omitHeader", "true")); - assertHitOrInsert(mm.getValue(), statPre); + + assertHitOrInsert(lookupsPre, hitsPre, insertsPre); } { - Map<String, Object> statPre = mm.getValue(); + double lookupsPre = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPre = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPre = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + h.query( req( "q", @@ -262,11 +268,20 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { "id", "omitHeader", "true")); - assertHit(mm.getValue(), statPre); + + assertHit(lookupsPre, hitsPre, insertsPre); } { - Map<String, Object> statPre = mm.getValue(); + double lookupsPre = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPre = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPre = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); Random r = random(); boolean changed = false; @@ -300,9 +315,19 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { "id", "omitHeader", "true")); - assertInsert(mm.getValue(), statPre); - statPre = mm.getValue(); + assertInsert(lookupsPre, hitsPre, insertsPre); + + double lookupsPreRepeat = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPreRepeat = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPreRepeat = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + final String repeat = h.query( req( @@ -321,7 +346,8 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { "id", "omitHeader", "true")); - assertHit(mm.getValue(), statPre); + + assertHit(lookupsPreRepeat, hitsPreRepeat, insertsPreRepeat); assertEquals("lowercase shouldn't change anything", resp, repeat); @@ -342,7 +368,8 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { // however, it might be better to extract this method into a separate suite // for a while let's nuke a cache content, in case of repetitions @SuppressWarnings("rawtypes") - SolrCache cache = (SolrCache) h.getCore().getInfoRegistry().get("queryResultCache"); + SolrCache cache = + (SolrCache) h.getCore().getInfoRegistry().get(SolrMetricTestUtils.QUERY_RESULT_CACHE); cache.clear(); } @@ -353,27 +380,71 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 { return l.get(r.nextInt(l.size())); } - private void assertInsert(Map<String, Object> current, final Map<String, Object> statPre) { - assertEquals("it lookups", 1, delta("lookups", current, statPre)); - assertEquals("it doesn't hit", 0, delta("hits", current, statPre)); - assertEquals("it inserts", 1, delta("inserts", current, statPre)); + private void assertInsert( + double lookupsPre, + CounterSnapshot.CounterDataPointSnapshot hitsPre, + CounterSnapshot.CounterDataPointSnapshot insertsPre) { + double lookupsPost = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPost = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPost = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + + assertEquals("it lookups", 1, delta(lookupsPost, lookupsPre)); + assertEquals("it doesn't hit", 0, delta(hitsPost, hitsPre)); + assertEquals("it inserts", 1, delta(insertsPost, insertsPre)); } - private void assertHit(Map<String, Object> current, final Map<String, Object> statPre) { - assertEquals("it lookups", 1, delta("lookups", current, statPre)); - assertEquals("it hits", 1, delta("hits", current, statPre)); - assertEquals("it doesn't insert", 0, delta("inserts", current, statPre)); + private void assertHit( + double lookupsPre, + CounterSnapshot.CounterDataPointSnapshot hitsPre, + CounterSnapshot.CounterDataPointSnapshot insertsPre) { + double lookupsPost = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPost = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPost = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + + assertEquals("it lookups", 1, delta(lookupsPost, lookupsPre)); + assertEquals("it hits", 1, delta(hitsPost, hitsPre)); + assertEquals("it doesn't insert", 0, delta(insertsPost, insertsPre)); } - private void assertHitOrInsert(Map<String, Object> current, final Map<String, Object> statPre) { - assertEquals("it lookups", 1, delta("lookups", current, statPre)); - final long mayHit = delta("hits", current, statPre); + private void assertHitOrInsert( + double lookupsPre, + CounterSnapshot.CounterDataPointSnapshot hitsPre, + CounterSnapshot.CounterDataPointSnapshot insertsPre) { + double lookupsPost = + SolrMetricTestUtils.getCacheSearcherTotalLookups( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot hitsPost = + SolrMetricTestUtils.getCacheSearcherOpsHits( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + CounterSnapshot.CounterDataPointSnapshot insertsPost = + SolrMetricTestUtils.getCacheSearcherOpsInserts( + h.getCore(), SolrMetricTestUtils.QUERY_RESULT_CACHE); + + assertEquals("it lookups", 1, delta(lookupsPost, lookupsPre)); + final long mayHit = delta(hitsPost, hitsPre); assertTrue("it may hit", 0 == mayHit || 1 == mayHit); - assertEquals("or insert on cold", 1, delta("inserts", current, statPre) + mayHit); + assertEquals("or insert on cold", 1, delta(insertsPost, insertsPre) + mayHit); + } + + private long delta( + CounterSnapshot.CounterDataPointSnapshot post, CounterSnapshot.CounterDataPointSnapshot pre) { + return delta(post.getValue(), pre.getValue()); } - private long delta(String key, Map<String, Object> a, Map<String, Object> b) { - return (Long) a.get(key) - (Long) b.get(key); + private long delta(double post, double pre) { + return (long) post - (long) pre; } private void indexDataForScoring() { diff --git a/solr/test-framework/src/java/org/apache/solr/util/SolrMetricTestUtils.java b/solr/test-framework/src/java/org/apache/solr/util/SolrMetricTestUtils.java index 3531094cfc3..1fb3b0082cf 100644 --- a/solr/test-framework/src/java/org/apache/solr/util/SolrMetricTestUtils.java +++ b/solr/test-framework/src/java/org/apache/solr/util/SolrMetricTestUtils.java @@ -45,6 +45,12 @@ public final class SolrMetricTestUtils { private static final int MAX_ITERATIONS = 100; private static final SolrInfoBean.Category CATEGORIES[] = SolrInfoBean.Category.values(); + // Cache name constants + public static final String QUERY_RESULT_CACHE = "queryResultCache"; + public static final String FILTER_CACHE = "filterCache"; + public static final String DOCUMENT_CACHE = "documentCache"; + public static final String PER_SEG_FILTER_CACHE = "perSegFilter"; + public static String getRandomScope(Random random) { return getRandomScope(random, random.nextBoolean()); } @@ -246,6 +252,11 @@ public final class SolrMetricTestUtils { return getDataPoint(reader, metricName, labels, CounterSnapshot.CounterDataPointSnapshot.class); } + public static GaugeSnapshot.GaugeDataPointSnapshot getGaugeDatapoint( + PrometheusMetricReader reader, String metricName, Labels labels) { + return getDataPoint(reader, metricName, labels, GaugeSnapshot.GaugeDataPointSnapshot.class); + } + public static HistogramSnapshot.HistogramDataPointSnapshot getHistogramDatapoint( PrometheusMetricReader reader, String metricName, Labels labels) { return getDataPoint( @@ -304,6 +315,47 @@ public final class SolrMetricTestUtils { .build()); } + public static CounterSnapshot.CounterDataPointSnapshot getCacheSearcherOps( + SolrCore core, String cacheName, String operation) { + return SolrMetricTestUtils.getCounterDatapoint( + core, + "solr_searcher_cache_ops", + SolrMetricTestUtils.newStandaloneLabelsBuilder(core) + .label("category", "CACHE") + .label("ops", operation) + .label("name", cacheName) + .build()); + } + + public static CounterSnapshot.CounterDataPointSnapshot getCacheSearcherLookups( + SolrCore core, String cacheName, String result) { + var builder = + SolrMetricTestUtils.newStandaloneLabelsBuilder(core) + .label("category", "CACHE") + .label("name", cacheName) + .label("result", result); + return SolrMetricTestUtils.getCounterDatapoint( + core, "solr_searcher_cache_lookups", builder.build()); + } + + public static CounterSnapshot.CounterDataPointSnapshot getCacheSearcherOpsHits( + SolrCore core, String cacheName) { + return SolrMetricTestUtils.getCacheSearcherLookups(core, cacheName, "hit"); + } + + public static double getCacheSearcherTotalLookups(SolrCore core, String cacheName) { + // Calculate lookup total as hits + misses + var hitDatapoint = SolrMetricTestUtils.getCacheSearcherOpsHits(core, cacheName); + var missDatapoint = SolrMetricTestUtils.getCacheSearcherLookups(core, cacheName, "miss"); + + return hitDatapoint.getValue() + missDatapoint.getValue(); + } + + public static CounterSnapshot.CounterDataPointSnapshot getCacheSearcherOpsInserts( + SolrCore core, String cacheName) { + return SolrMetricTestUtils.getCacheSearcherOps(core, cacheName, "inserts"); + } + public static class TestSolrMetricProducer implements SolrMetricProducer { SolrMetricsContext solrMetricsContext; private final Map<String, io.opentelemetry.api.metrics.LongCounter> counters = new HashMap<>();
