This is an automated email from the ASF dual-hosted git repository.

mlbiscoc pushed a commit to branch feature/SOLR-17458
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 5cc10d5b7241b1c1195a3088a8709c556e25f0b8
Author: Matthew Biscocho <[email protected]>
AuthorDate: Thu Aug 28 13:52:51 2025 -0400

    Close OTEL Observable instruments (#3517)
    
    * Close observable instruments
    
    * List of closeables
    
    * Use unmodifiable instead of copy
---
 .../src/java/org/apache/solr/core/SolrCore.java    | 160 +++++++++++----------
 .../src/java/org/apache/solr/core/ZkContainer.java | 144 ++++++++++---------
 .../apache/solr/update/DirectUpdateHandler2.java   |  26 ++--
 .../src/java/org/apache/solr/update/UpdateLog.java |  49 ++++---
 .../org/apache/solr/BasicFunctionalityTest.java    |  32 ++---
 .../test/org/apache/solr/core/SolrCoreTest.java    |  34 +++--
 .../apache/solr/update/SolrIndexMetricsTest.java   |  42 +++---
 .../java/org/apache/solr/common/util/IOUtils.java  |   5 +
 8 files changed, 267 insertions(+), 225 deletions(-)

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 3a13af1c07b..4d9ddc03c34 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -261,6 +261,7 @@ public class SolrCore implements SolrInfoBean, Closeable {
   private AttributedLongCounter newSearcherOtherErrorsCounter;
   private AttributedLongTimer newSearcherTimer;
   private AttributedLongTimer newSearcherWarmupTimer;
+  private List<AutoCloseable> toClose;
 
   private final String metricTag = SolrMetricProducer.getUniqueMetricTag(this, 
null);
   private final SolrMetricsContext solrMetricsContext;
@@ -1324,6 +1325,7 @@ public class SolrCore implements SolrInfoBean, Closeable {
   @Override
   public void initializeMetrics(
       SolrMetricsContext parentContext, Attributes attributes, String scope) {
+    final List<AutoCloseable> observables = new ArrayList<>();
 
     Attributes baseSearcherAttributes =
         Attributes.builder()
@@ -1369,86 +1371,93 @@ public class SolrCore implements SolrInfoBean, 
Closeable {
             baseSearcherTimerMetric,
             Attributes.builder().putAll(baseSearcherAttributes).put(TYPE_ATTR, 
"warmup").build());
 
-    parentContext.gauge(() -> getOpenCount(), true, "refCount", 
Category.CORE.toString());
-
-    parentContext.observableLongGauge(
-        "solr_core_ref_count",
-        "The current number of active references to a Solr core",
-        (observableLongMeasurement -> {
-          observableLongMeasurement.record(getOpenCount(), 
baseGaugeCoreAttributes);
-        }));
-
-    parentContext.observableLongGauge(
-        "solr_core_disk_space",
-        "Solr core disk space metrics",
-        (observableLongMeasurement -> {
-
-          // initialize disk total / free metrics
-          Path dataDirPath = Path.of(dataDir);
-          var totalSpaceAttributes =
-              Attributes.builder()
-                  .putAll(baseGaugeCoreAttributes)
-                  .put(TYPE_ATTR, "total_space")
-                  .build();
-          var usableSpaceAttributes =
-              Attributes.builder()
-                  .putAll(baseGaugeCoreAttributes)
-                  .put(TYPE_ATTR, "usable_space")
-                  .build();
-          try {
-            observableLongMeasurement.record(
-                Files.getFileStore(dataDirPath).getTotalSpace(), 
totalSpaceAttributes);
-          } catch (IOException e) {
-            observableLongMeasurement.record(0L, totalSpaceAttributes);
-          }
-          try {
-            observableLongMeasurement.record(
-                Files.getFileStore(dataDirPath).getUsableSpace(), 
usableSpaceAttributes);
-          } catch (IOException e) {
-            observableLongMeasurement.record(0L, usableSpaceAttributes);
-          }
-        }),
-        OtelUnit.BYTES);
-
-    parentContext.observableLongGauge(
-        "solr_core_index_size",
-        "Index size for a Solr core",
-        (observableLongMeasurement -> {
-          if (!isClosed())
-            observableLongMeasurement.record(getIndexSize(), 
baseGaugeCoreAttributes);
-        }),
-        OtelUnit.BYTES);
-
-    parentContext.observableLongGauge(
-        "solr_core_segment_count",
-        "Number of segments in a Solr core",
-        (observableLongMeasurement -> {
-          if (isReady())
-            observableLongMeasurement.record(getSegmentCount(), 
baseGaugeCoreAttributes);
-        }));
+    observables.add(
+        parentContext.observableLongGauge(
+            "solr_core_ref_count",
+            "The current number of active references to a Solr core",
+            (observableLongMeasurement -> {
+              observableLongMeasurement.record(getOpenCount(), 
baseGaugeCoreAttributes);
+            })));
+
+    observables.add(
+        parentContext.observableLongGauge(
+            "solr_core_disk_space",
+            "Solr core disk space metrics",
+            (observableLongMeasurement -> {
+
+              // initialize disk total / free metrics
+              Path dataDirPath = Path.of(dataDir);
+              var totalSpaceAttributes =
+                  Attributes.builder()
+                      .putAll(baseGaugeCoreAttributes)
+                      .put(TYPE_ATTR, "total_space")
+                      .build();
+              var usableSpaceAttributes =
+                  Attributes.builder()
+                      .putAll(baseGaugeCoreAttributes)
+                      .put(TYPE_ATTR, "usable_space")
+                      .build();
+              try {
+                observableLongMeasurement.record(
+                    Files.getFileStore(dataDirPath).getTotalSpace(), 
totalSpaceAttributes);
+              } catch (IOException e) {
+                observableLongMeasurement.record(0L, totalSpaceAttributes);
+              }
+              try {
+                observableLongMeasurement.record(
+                    Files.getFileStore(dataDirPath).getUsableSpace(), 
usableSpaceAttributes);
+              } catch (IOException e) {
+                observableLongMeasurement.record(0L, usableSpaceAttributes);
+              }
+            }),
+            OtelUnit.BYTES));
+
+    observables.add(
+        parentContext.observableLongGauge(
+            "solr_core_index_size",
+            "Index size for a Solr core",
+            (observableLongMeasurement -> {
+              if (!isClosed())
+                observableLongMeasurement.record(getIndexSize(), 
baseGaugeCoreAttributes);
+            }),
+            OtelUnit.BYTES));
+
+    observables.add(
+        parentContext.observableLongGauge(
+            "solr_core_segment_count",
+            "Number of segments in a Solr core",
+            (observableLongMeasurement -> {
+              if (isReady())
+                observableLongMeasurement.record(getSegmentCount(), 
baseGaugeCoreAttributes);
+            })));
 
     // NOCOMMIT: Do we need these start_time metrics? I think at minimum it 
should be optional
     // otherwise we fall into metric bloat for something people may not care 
about.
-    parentContext.observableLongGauge(
-        "solr_core_start_time",
-        "Start time of a Solr core",
-        (observableLongMeasurement -> {
-          observableLongMeasurement.record(
-              startTime.getTime(),
-              Attributes.builder()
-                  .putAll(baseGaugeCoreAttributes)
-                  .put(TYPE_ATTR, "start_time")
-                  .build());
-        }));
+    observables.add(
+        parentContext.observableLongGauge(
+            "solr_core_start_time",
+            "Start time of a Solr core",
+            (observableLongMeasurement -> {
+              observableLongMeasurement.record(
+                  startTime.getTime(),
+                  Attributes.builder()
+                      .putAll(baseGaugeCoreAttributes)
+                      .put(TYPE_ATTR, "start_time")
+                      .build());
+            })));
 
     if (coreContainer.isZooKeeperAware())
-      parentContext.observableLongGauge(
-          "solr_core_is_leader",
-          "Indicates whether this Solr core is currently the leader",
-          (observableLongMeasurement -> {
-            observableLongMeasurement.record(
-                (coreDescriptor.getCloudDescriptor().isLeader()) ? 1 : 0, 
baseGaugeCoreAttributes);
-          }));
+      observables.add(
+          parentContext.observableLongGauge(
+              "solr_core_is_leader",
+              "Indicates whether this Solr core is currently the leader",
+              (observableLongMeasurement -> {
+                observableLongMeasurement.record(
+                    (coreDescriptor.getCloudDescriptor().isLeader()) ? 1 : 0,
+                    baseGaugeCoreAttributes);
+              })));
+
+    this.toClose = Collections.unmodifiableList(observables);
 
     // NOCOMMIT: Temporary to see metrics
     newSearcherCounter.inc();
@@ -1804,6 +1813,7 @@ public class SolrCore implements SolrInfoBean, Closeable {
         assert false : "Too many closes on SolrCore";
       } else if (count == 0) {
         doClose();
+        IOUtils.closeQuietly(toClose);
       }
     } finally {
       MDCLoggingContext.clear(); // balance out from SolrCore open with close
diff --git a/solr/core/src/java/org/apache/solr/core/ZkContainer.java 
b/solr/core/src/java/org/apache/solr/core/ZkContainer.java
index 1de6b0e45fd..a3540593204 100644
--- a/solr/core/src/java/org/apache/solr/core/ZkContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/ZkContainer.java
@@ -23,6 +23,9 @@ import io.opentelemetry.api.common.Attributes;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Predicate;
@@ -35,6 +38,7 @@ import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.cloud.ZooKeeperException;
 import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.SolrNamedThreadFactory;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.logging.MDCLoggingContext;
@@ -68,6 +72,8 @@ public class ZkContainer {
 
   private SolrMetricProducer metricProducer;
 
+  private List<AutoCloseable> toClose;
+
   public ZkContainer() {}
 
   public void initZooKeeper(final CoreContainer cc, CloudConfig config) {
@@ -149,74 +155,83 @@ public class ZkContainer {
               @Override
               public void initializeMetrics(
                   SolrMetricsContext parentContext, Attributes attributes, 
String scope) {
+                final List<AutoCloseable> observables = new ArrayList<>();
                 ctx = parentContext.getChildContext(this);
 
                 var metricsListener = zkController.getZkClient().getMetrics();
 
-                ctx.observableLongCounter(
-                    "solr_zk_ops",
-                    "Total number of ZooKeeper operations",
-                    measurement -> {
-                      measurement.record(
-                          metricsListener.getReads(),
-                          attributes.toBuilder().put(OPERATION_ATTR, 
"read").build());
-                      measurement.record(
-                          metricsListener.getDeletes(),
-                          attributes.toBuilder().put(OPERATION_ATTR, 
"delete").build());
-                      measurement.record(
-                          metricsListener.getWrites(),
-                          attributes.toBuilder().put(OPERATION_ATTR, 
"write").build());
-                      measurement.record(
-                          metricsListener.getMultiOps(),
-                          attributes.toBuilder().put(OPERATION_ATTR, 
"multi").build());
-                      measurement.record(
-                          metricsListener.getExistsChecks(),
-                          attributes.toBuilder().put(OPERATION_ATTR, 
"exists").build());
-                    });
-
-                ctx.observableLongCounter(
-                    "solr_zk_read",
-                    "Total bytes read from ZooKeeper",
-                    measurement -> {
-                      measurement.record(metricsListener.getBytesRead(), 
attributes);
-                    },
-                    OtelUnit.BYTES);
-
-                ctx.observableLongCounter(
-                    "solr_zk_watches_fired",
-                    "Total number of ZooKeeper watches fired",
-                    measurement -> {
-                      measurement.record(metricsListener.getWatchesFired(), 
attributes);
-                    });
-
-                ctx.observableLongCounter(
-                    "solr_zk_written",
-                    "Total bytes written to ZooKeeper",
-                    measurement -> {
-                      measurement.record(metricsListener.getBytesWritten());
-                    },
-                    OtelUnit.BYTES);
-
-                ctx.observableLongCounter(
-                    "solr_zk_cumulative_multi_ops_total",
-                    "Total cumulative multi-operations count",
-                    measurement -> {
-                      
measurement.record(metricsListener.getCumulativeMultiOps());
-                    });
-
-                ctx.observableLongCounter(
-                    "solr_zk_child_fetches",
-                    "Total number of ZooKeeper child node fetches",
-                    measurement -> {
-                      measurement.record(metricsListener.getChildFetches());
-                    });
-
-                ctx.observableLongCounter(
-                    "solr_zk_cumulative_children_fetched",
-                    "Total cumulative children fetched count",
-                    measurement -> {
-                      
measurement.record(metricsListener.getCumulativeChildrenFetched());
-                    });
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_ops",
+                        "Total number of ZooKeeper operations",
+                        measurement -> {
+                          measurement.record(
+                              metricsListener.getReads(),
+                              attributes.toBuilder().put(OPERATION_ATTR, 
"read").build());
+                          measurement.record(
+                              metricsListener.getDeletes(),
+                              attributes.toBuilder().put(OPERATION_ATTR, 
"delete").build());
+                          measurement.record(
+                              metricsListener.getWrites(),
+                              attributes.toBuilder().put(OPERATION_ATTR, 
"write").build());
+                          measurement.record(
+                              metricsListener.getMultiOps(),
+                              attributes.toBuilder().put(OPERATION_ATTR, 
"multi").build());
+                          measurement.record(
+                              metricsListener.getExistsChecks(),
+                              attributes.toBuilder().put(OPERATION_ATTR, 
"exists").build());
+                        }));
+
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_read",
+                        "Total bytes read from ZooKeeper",
+                        measurement -> {
+                          measurement.record(metricsListener.getBytesRead(), 
attributes);
+                        },
+                        OtelUnit.BYTES));
+
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_watches_fired",
+                        "Total number of ZooKeeper watches fired",
+                        measurement -> {
+                          
measurement.record(metricsListener.getWatchesFired(), attributes);
+                        }));
+
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_written",
+                        "Total bytes written to ZooKeeper",
+                        measurement -> {
+                          
measurement.record(metricsListener.getBytesWritten());
+                        },
+                        OtelUnit.BYTES));
+
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_cumulative_multi_ops_total",
+                        "Total cumulative multi-operations count",
+                        measurement -> {
+                          
measurement.record(metricsListener.getCumulativeMultiOps());
+                        }));
+
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_child_fetches",
+                        "Total number of ZooKeeper child node fetches",
+                        measurement -> {
+                          
measurement.record(metricsListener.getChildFetches());
+                        }));
+
+                observables.add(
+                    ctx.observableLongCounter(
+                        "solr_zk_cumulative_children_fetched",
+                        "Total cumulative children fetched count",
+                        measurement -> {
+                          
measurement.record(metricsListener.getCumulativeChildrenFetched());
+                        }));
+                toClose = Collections.unmodifiableList(observables);
               }
 
               @Override
@@ -315,6 +330,7 @@ public class ZkContainer {
           zkServer.stop();
         }
       }
+      IOUtils.closeQuietly(toClose);
     }
   }
 
diff --git 
a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java 
b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
index 90c8bac2285..098c8170fe4 100644
--- a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
+++ b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
@@ -18,12 +18,11 @@ package org.apache.solr.update;
 
 import io.opentelemetry.api.common.AttributeKey;
 import io.opentelemetry.api.common.Attributes;
-import io.opentelemetry.api.metrics.ObservableLongCounter;
-import io.opentelemetry.api.metrics.ObservableLongGauge;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -50,6 +49,7 @@ import org.apache.solr.cloud.ZkController;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.Replica;
 import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.IOUtils;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.core.SolrConfig.UpdateHandlerInfo;
 import org.apache.solr.core.SolrCore;
@@ -120,9 +120,7 @@ public class DirectUpdateHandler2 extends UpdateHandler
 
   AttributedLongCounter rollbackCommands;
   AttributedLongCounter splitCommands;
-  ObservableLongGauge commitStats;
-  ObservableLongGauge updateStats;
-  ObservableLongCounter softAutoCommits;
+  List<AutoCloseable> toClose;
 
   // tracks when auto-commit should occur
   protected final CommitTracker commitTracker;
@@ -239,6 +237,7 @@ public class DirectUpdateHandler2 extends UpdateHandler
     } else {
       this.solrMetricsContext = parentContext.getChildContext(this);
     }
+    final List<AutoCloseable> observables = new ArrayList<>();
 
     var baseAttributes =
         attributes.toBuilder()
@@ -306,7 +305,7 @@ public class DirectUpdateHandler2 extends UpdateHandler
     numErrorsCumulative =
         new AttributedLongCounter(baseErrorsMetric, 
baseAttributes.toBuilder().build());
 
-    softAutoCommits =
+    observables.add(
         solrMetricsContext.observableLongCounter(
             "solr_core_update_auto_commits",
             "Current number of auto commits",
@@ -317,12 +316,12 @@ public class DirectUpdateHandler2 extends UpdateHandler
               observableLongMeasurement.record(
                   softCommitTracker.getCommitCount(),
                   baseAttributes.toBuilder().put(TYPE_ATTR, 
"soft_auto_commits").build());
-            }));
+            })));
 
     // NOCOMMIT: This might not need to be an obseravableLongGauge, but a 
simple long gauge. Seems
     // like a waste to constantly call this callback to get the latest value 
if the upper bounds
     // rarely change.
-    commitStats =
+    observables.add(
         solrMetricsContext.observableLongGauge(
             "solr_core_update_commit_stats",
             "Metrics around commits",
@@ -352,9 +351,9 @@ public class DirectUpdateHandler2 extends UpdateHandler
                     softCommitTracker.getTimeUpperBound(),
                     baseAttributes.toBuilder().put(TYPE_ATTR, 
"soft_auto_commit_max_time").build());
               }
-            }));
+            })));
 
-    updateStats =
+    observables.add(
         solrMetricsContext.observableLongGauge(
             "solr_core_update_docs_pending_commit",
             "Current number of documents pending commit. Value is reset to 0 
on commit.",
@@ -362,7 +361,9 @@ public class DirectUpdateHandler2 extends UpdateHandler
               observableLongMeasurement.record(
                   numDocsPending.longValue(),
                   baseAttributes.toBuilder().put(OPERATION_ATTR, 
"docs_pending").build());
-            });
+            }));
+
+    this.toClose = Collections.unmodifiableList(observables);
 
     var baseSubmittedOpsMetric =
         solrMetricsContext.longCounter(
@@ -1014,8 +1015,7 @@ public class DirectUpdateHandler2 extends UpdateHandler
 
     commitTracker.close();
     softCommitTracker.close();
-    commitStats.close();
-
+    IOUtils.closeQuietly(toClose);
     numDocsPending.reset();
     try {
       super.close();
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateLog.java 
b/solr/core/src/java/org/apache/solr/update/UpdateLog.java
index d392e992f2c..b79331216a8 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateLog.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateLog.java
@@ -268,6 +268,7 @@ public class UpdateLog implements PluginInfoInitialized, 
SolrMetricProducer {
   protected AttributedLongCounter applyingBufferedOpsCounter;
   protected AttributedLongCounter replayOpsCounter;
   protected AttributedLongCounter copyOverOldUpdatesCounter;
+  protected List<AutoCloseable> toClose;
   protected SolrMetricsContext solrMetricsContext;
 
   public static class LogPtr {
@@ -630,6 +631,7 @@ public class UpdateLog implements PluginInfoInitialized, 
SolrMetricProducer {
   @Override
   public void initializeMetrics(
       SolrMetricsContext parentContext, Attributes attributes, String scope) {
+    final List<AutoCloseable> observables = new ArrayList<>();
     solrMetricsContext = parentContext.getChildContext(this);
 
     // NOCOMMIT: We do not need a scope attribute
@@ -640,26 +642,31 @@ public class UpdateLog implements PluginInfoInitialized, 
SolrMetricProducer {
             .put(CATEGORY_ATTR, SolrInfoBean.Category.TLOG.toString())
             .build();
 
-    solrMetricsContext.observableLongGauge(
-        "solr_core_update_log_buffered_ops",
-        "The current number of buffered operations",
-        (observableLongMeasurement ->
-            observableLongMeasurement.record(computeBufferedOps(), 
baseAttributes)));
-
-    solrMetricsContext.observableLongGauge(
-        "solr_core_update_log_replay_logs_remaining",
-        "The current number of tlogs remaining to be replayed",
-        (observableLongMeasurement -> {
-          observableLongMeasurement.record(logs.size(), baseAttributes);
-        }));
-
-    solrMetricsContext.observableLongGauge(
-        "solr_core_update_log_size_remaining",
-        "The total size in bytes of all tlogs remaining to be replayed",
-        (observableLongMeasurement -> {
-          observableLongMeasurement.record(getTotalLogsSize(), baseAttributes);
-        }),
-        OtelUnit.BYTES);
+    observables.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_core_update_log_buffered_ops",
+            "The current number of buffered operations",
+            (observableLongMeasurement ->
+                observableLongMeasurement.record(computeBufferedOps(), 
baseAttributes))));
+
+    observables.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_core_update_log_replay_logs_remaining",
+            "The current number of tlogs remaining to be replayed",
+            (observableLongMeasurement -> {
+              observableLongMeasurement.record(logs.size(), baseAttributes);
+            })));
+
+    observables.add(
+        solrMetricsContext.observableLongGauge(
+            "solr_core_update_log_size_remaining",
+            "The total size in bytes of all tlogs remaining to be replayed",
+            (observableLongMeasurement -> {
+              observableLongMeasurement.record(getTotalLogsSize(), 
baseAttributes);
+            }),
+            OtelUnit.BYTES));
+
+    toClose = Collections.unmodifiableList(observables);
 
     // NOCOMMIT: Do we want to keep this? Metric was just state with the 
numeric enum value.
     // Without context this doesn't mean anything and can be very confusing. 
Maybe keep the numeric
@@ -1703,6 +1710,8 @@ public class UpdateLog implements PluginInfoInitialized, 
SolrMetricProducer {
       }
     } catch (IOException e) {
       log.warn("exception releasing tlog dir", e);
+    } finally {
+      IOUtils.closeQuietly(toClose);
     }
   }
 
diff --git a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java 
b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
index a2d31f6ef82..91cbf47f008 100644
--- a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
+++ b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
@@ -117,23 +117,21 @@ public class BasicFunctionalityTest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testSomeStuff() {
-    clearIndex();
-
-    SolrCore core = h.getCore();
-
-    // test that we got the expected config, not just hardcoded defaults
-    assertNotNull(core.getRequestHandler("/mock"));
-
-    // test stats call
-    var refCount =
-        SolrMetricTestUtils.getGaugeDatapoint(
-            core,
-            "solr_core_ref_count",
-            
SolrMetricTestUtils.newStandaloneLabelsBuilder(core).label("category", 
"CORE").build());
-    assertNotNull(refCount);
-
-    assertTrue(refCount.getValue() > 0);
-
+    try (SolrCore core = h.getCoreContainer().getCore("collection1")) {
+      // test that we got the expected config, not just hardcoded defaults
+      assertNotNull(core.getRequestHandler("/mock"));
+
+      // test stats call
+      var refCount =
+          SolrMetricTestUtils.getGaugeDatapoint(
+              core,
+              "solr_core_ref_count",
+              SolrMetricTestUtils.newStandaloneLabelsBuilder(core)
+                  .label("category", "CORE")
+                  .build());
+      assertNotNull(refCount);
+      assertTrue(refCount.getValue() > 0);
+    }
     assertQ(
         "test query on empty index",
         req("qlkciyopsbgzyvkylsjhchghjrdf"),
diff --git a/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java 
b/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java
index 5fe145de556..5af9bfe0436 100644
--- a/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java
+++ b/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java
@@ -16,6 +16,9 @@
  */
 package org.apache.solr.core;
 
+import io.opentelemetry.exporter.prometheus.PrometheusMetricReader;
+import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
+import io.prometheus.metrics.model.snapshots.MetricSnapshots;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -33,8 +36,6 @@ import org.apache.solr.handler.ReplicationHandler;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.component.QueryComponent;
 import org.apache.solr.handler.component.SpellCheckComponent;
-import org.apache.solr.metrics.SolrMetricManager;
-import org.apache.solr.metrics.SolrMetricTestUtils;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestHandler;
 import org.apache.solr.response.SolrQueryResponse;
@@ -340,7 +341,6 @@ public class SolrCoreTest extends SolrTestCaseJ4 {
    */
   @Test
   public void testCoreInitDeadlockMetrics() throws Exception {
-    SolrMetricManager metricManager = h.getCoreContainer().getMetricManager();
     CoreContainer coreContainer = h.getCoreContainer();
 
     String coreName = "tmpCore";
@@ -353,16 +353,24 @@ public class SolrCoreTest extends SolrTestCaseJ4 {
     executor.execute(
         () -> {
           while (!created.get()) {
-            var datapoint =
-                SolrMetricTestUtils.getGaugeDatapoint(
-                    h.getCore(),
-                    "solr_core_index_size_bytes",
-                    SolrMetricTestUtils.newStandaloneLabelsBuilder(h.getCore())
-                        .label("category", "CORE")
-                        .build());
-
-            if (datapoint != null && datapoint.getValue() >= 0) {
-              atLeastOnePoll.set(true);
+            try {
+              PrometheusMetricReader reader =
+                  coreContainer
+                      .getMetricManager()
+                      .getPrometheusMetricReader("solr.core." + coreName);
+              if (reader != null) {
+                MetricSnapshots snapshots = reader.collect();
+                for (var snapshot : snapshots) {
+                  if (snapshot instanceof GaugeSnapshot gaugeSnapshot) {
+                    var dataPoints = gaugeSnapshot.getDataPoints();
+                    if (!dataPoints.isEmpty()) {
+                      atLeastOnePoll.compareAndSet(false, true);
+                    }
+                  }
+                }
+              }
+            } catch (Exception ignore) {
+              // Ignore in case the core may not be fully initialized
             }
 
             try {
diff --git 
a/solr/core/src/test/org/apache/solr/update/SolrIndexMetricsTest.java 
b/solr/core/src/test/org/apache/solr/update/SolrIndexMetricsTest.java
index 4644c851d97..09aa0489949 100644
--- a/solr/core/src/test/org/apache/solr/update/SolrIndexMetricsTest.java
+++ b/solr/core/src/test/org/apache/solr/update/SolrIndexMetricsTest.java
@@ -23,6 +23,7 @@ import com.codahale.metrics.Timer;
 import java.util.Map;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.core.SolrCore;
 import org.apache.solr.metrics.SolrMetricTestUtils;
 import org.apache.solr.request.SolrQueryRequest;
 import org.junit.After;
@@ -89,30 +90,25 @@ public class SolrIndexMetricsTest extends SolrTestCaseJ4 {
     System.setProperty("solr.tests.metrics.merge", "false");
     System.setProperty("solr.tests.metrics.mergeDetails", "false");
     initCore("solrconfig-indexmetrics.xml", "schema.xml");
-
     addDocs();
-
-    MetricRegistry registry =
-        h.getCoreContainer()
-            .getMetricManager()
-            .registry(h.getCore().getCoreMetricManager().getRegistryName());
-    assertNotNull(registry);
-    var indexSize =
-        SolrMetricTestUtils.getGaugeDatapoint(
-            h.getCore(),
-            "solr_core_index_size_bytes",
-            SolrMetricTestUtils.newStandaloneLabelsBuilder(h.getCore())
-                .label("category", "CORE")
-                .build());
-    var segmentSize =
-        SolrMetricTestUtils.getGaugeDatapoint(
-            h.getCore(),
-            "solr_core_segment_count",
-            SolrMetricTestUtils.newStandaloneLabelsBuilder(h.getCore())
-                .label("category", "CORE")
-                .build());
-    assertNotNull(indexSize);
-    assertNotNull(segmentSize);
+    try (SolrCore core = h.getCoreContainer().getCore("collection1")) {
+      var indexSize =
+          SolrMetricTestUtils.getGaugeDatapoint(
+              core,
+              "solr_core_index_size_bytes",
+              SolrMetricTestUtils.newStandaloneLabelsBuilder(core)
+                  .label("category", "CORE")
+                  .build());
+      var segmentSize =
+          SolrMetricTestUtils.getGaugeDatapoint(
+              core,
+              "solr_core_segment_count",
+              SolrMetricTestUtils.newStandaloneLabelsBuilder(core)
+                  .label("category", "CORE")
+                  .build());
+      assertNotNull(indexSize);
+      assertNotNull(segmentSize);
+    }
   }
 
   @Test
diff --git a/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java 
b/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java
index b91553a1813..93a2c5eb22e 100644
--- a/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java
+++ b/solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java
@@ -23,6 +23,11 @@ import org.slf4j.LoggerFactory;
 public class IOUtils {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  public static void closeQuietly(Iterable<? extends AutoCloseable> 
closeables) {
+    if (closeables == null) return;
+    for (AutoCloseable closeable : closeables) closeQuietly(closeable);
+  }
+
   public static void closeQuietly(AutoCloseable closeable) {
     try {
       if (closeable != null) {

Reply via email to