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

sanjaydutt pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 47c4aae0375 SOLR-16503: New home for default http2 client (#2689)
47c4aae0375 is described below

commit 47c4aae0375f6edf82d608147a9d04acf4f42faa
Author: Sanjay Dutt <[email protected]>
AuthorDate: Tue Oct 8 15:44:56 2024 +0530

    SOLR-16503: New home for default http2 client (#2689)
    
    * Introduced `HttpSolrClientProvider` as the new provider for 
`Http2SolrClient`.
    
    * `UpdateShardHandler#getDefaultHttpClient` has been marked deprecated.
    
    * Updated `IOUtils.closeQuietly` to accept `AutoCloseable` instead of 
`Closeable`, enhancing its flexibility.
    
    ---------
    
    Co-authored-by: David Smiley <[email protected]>
---
 .../java/org/apache/solr/core/CoreContainer.java   | 22 +++++-
 .../apache/solr/core/HttpSolrClientProvider.java   | 87 ++++++++++++++++++++++
 .../org/apache/solr/update/UpdateShardHandler.java | 12 ++-
 .../stats/InstrumentedHttpListenerFactory.java     | 14 ++++
 .../solr/core/TestHttpSolrClientProvider.java      | 74 ++++++++++++++++++
 .../java/org/apache/solr/common/util/IOUtils.java  |  3 +-
 6 files changed, 208 insertions(+), 4 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java 
b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 8c5b851dac5..b78475d8ad5 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -67,6 +67,7 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.solr.api.ClusterPluginsSource;
 import org.apache.solr.api.ContainerPluginsRegistry;
 import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.solrj.impl.Http2SolrClient;
 import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.client.solrj.impl.SolrHttpClientBuilder;
 import org.apache.solr.client.solrj.impl.SolrHttpClientContextBuilder;
@@ -236,6 +237,8 @@ public class CoreContainer {
 
   private volatile UpdateShardHandler updateShardHandler;
 
+  private volatile HttpSolrClientProvider solrClientProvider;
+
   private volatile ExecutorService coreContainerWorkExecutor =
       ExecutorUtil.newMDCAwareCachedThreadPool(
           new SolrNamedThreadFactory("coreContainerWorkExecutor"));
@@ -652,6 +655,7 @@ public class CoreContainer {
       
pkiAuthenticationSecurityBuilder.getHttpClientBuilder(HttpClientUtil.getHttpClientBuilder());
       shardHandlerFactory.setSecurityBuilder(pkiAuthenticationSecurityBuilder);
       updateShardHandler.setSecurityBuilder(pkiAuthenticationSecurityBuilder);
+      solrClientProvider.setSecurityBuilder(pkiAuthenticationSecurityBuilder);
     }
   }
 
@@ -835,8 +839,9 @@ public class CoreContainer {
     }
 
     updateShardHandler = new 
UpdateShardHandler(cfg.getUpdateShardHandlerConfig());
+    solrClientProvider =
+        new HttpSolrClientProvider(cfg.getUpdateShardHandlerConfig(), 
solrMetricsContext);
     updateShardHandler.initializeMetrics(solrMetricsContext, 
"updateShardHandler");
-
     solrClientCache = new 
SolrClientCache(updateShardHandler.getDefaultHttpClient());
 
     Map<String, CacheConfig> cachesConfig = cfg.getCachesConfig();
@@ -1400,6 +1405,9 @@ public class CoreContainer {
           if (updateShardHandler != null) {
             customThreadPool.execute(updateShardHandler::close);
           }
+          if (solrClientProvider != null) {
+            customThreadPool.submit(solrClientProvider::close);
+          }
         } finally {
           try {
             // we want to close zk stuff last
@@ -2554,6 +2562,18 @@ public class CoreContainer {
     return this.distributedCollectionCommandRunner;
   }
 
+  /**
+   * Provides the existing general-purpose HTTP/2 Solr client from {@link 
HttpSolrClientProvider}.
+   *
+   * <p>The caller does not need to close the client, as its lifecycle is 
managed by {@link
+   * HttpSolrClientProvider}.
+   *
+   * @return the existing {@link Http2SolrClient}
+   */
+  public Http2SolrClient getDefaultHttpSolrClient() {
+    return solrClientProvider.getSolrClient();
+  }
+
   /**
    * Run an arbitrary task in its own thread. This is an expert option and is 
a method you should
    * use with great care. It would be bad to run something that never stopped 
or run something that
diff --git 
a/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java 
b/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
new file mode 100644
index 00000000000..2bf25a896f6
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/HttpSolrClientProvider.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.core;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.solr.client.solrj.impl.Http2SolrClient;
+import org.apache.solr.common.util.IOUtils;
+import org.apache.solr.metrics.SolrMetricManager;
+import org.apache.solr.metrics.SolrMetricsContext;
+import org.apache.solr.security.HttpClientBuilderPlugin;
+import org.apache.solr.update.UpdateShardHandlerConfig;
+import org.apache.solr.util.stats.InstrumentedHttpListenerFactory;
+
+/**
+ * Provider of the default SolrClient implementation.
+ *
+ * @lucene.internal
+ */
+final class HttpSolrClientProvider implements AutoCloseable {
+
+  static final String METRIC_SCOPE_NAME = "defaultHttpSolrClientProvider";
+
+  private final Http2SolrClient httpSolrClient;
+
+  private final InstrumentedHttpListenerFactory trackHttpSolrMetrics;
+
+  HttpSolrClientProvider(UpdateShardHandlerConfig cfg, SolrMetricsContext 
parentContext) {
+    trackHttpSolrMetrics = new 
InstrumentedHttpListenerFactory(getNameStrategy(cfg));
+    initializeMetrics(parentContext);
+
+    Http2SolrClient.Builder httpClientBuilder =
+        new 
Http2SolrClient.Builder().withListenerFactory(List.of(trackHttpSolrMetrics));
+
+    if (cfg != null) {
+      httpClientBuilder
+          .withConnectionTimeout(cfg.getDistributedConnectionTimeout(), 
TimeUnit.MILLISECONDS)
+          .withIdleTimeout(cfg.getDistributedSocketTimeout(), 
TimeUnit.MILLISECONDS)
+          .withMaxConnectionsPerHost(cfg.getMaxUpdateConnectionsPerHost());
+    }
+    httpSolrClient = httpClientBuilder.build();
+  }
+
+  private InstrumentedHttpListenerFactory.NameStrategy getNameStrategy(
+      UpdateShardHandlerConfig cfg) {
+    String metricNameStrategy =
+        cfg != null && cfg.getMetricNameStrategy() != null
+            ? cfg.getMetricNameStrategy()
+            : UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY;
+    return InstrumentedHttpListenerFactory.getNameStrategy(metricNameStrategy);
+  }
+
+  private void initializeMetrics(SolrMetricsContext parentContext) {
+    var solrMetricsContext = parentContext.getChildContext(this);
+    String expandedScope =
+        SolrMetricManager.mkName(METRIC_SCOPE_NAME, 
SolrInfoBean.Category.HTTP.name());
+    trackHttpSolrMetrics.initializeMetrics(solrMetricsContext, expandedScope);
+  }
+
+  Http2SolrClient getSolrClient() {
+    return httpSolrClient;
+  }
+
+  void setSecurityBuilder(HttpClientBuilderPlugin builder) {
+    builder.setup(httpSolrClient);
+  }
+
+  @Override
+  public void close() {
+    IOUtils.closeQuietly(httpSolrClient);
+    IOUtils.closeQuietly(trackHttpSolrMetrics);
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java 
b/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
index 650a61dc41d..f03ef161441 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java
@@ -236,7 +236,17 @@ public class UpdateShardHandler implements SolrInfoBean {
     return solrMetricsContext;
   }
 
-  // if you are looking for a client to use, it's probably this one.
+  /**
+   * Returns the default HTTP client for general-purpose usage.
+   *
+   * <p>This method is deprecated as the default client is now provided by 
{@link
+   * org.apache.solr.core.CoreContainer#getDefaultHttpSolrClient()}. Users 
should prefer that method
+   * to retrieve the default Solr client.
+   *
+   * @return the default {@link HttpClient}.
+   * @deprecated Use {@link 
org.apache.solr.core.CoreContainer#getDefaultHttpSolrClient()} instead.
+   */
+  @Deprecated
   public HttpClient getDefaultHttpClient() {
     return defaultClient;
   }
diff --git 
a/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
 
b/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
index 31523a5882f..d91d1f83b68 100644
--- 
a/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
+++ 
b/solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java
@@ -24,6 +24,7 @@ import io.opentelemetry.api.trace.Span;
 import java.util.Locale;
 import java.util.Map;
 import org.apache.solr.client.solrj.impl.HttpListenerFactory;
+import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.CollectionUtil;
 import org.apache.solr.metrics.SolrMetricProducer;
 import org.apache.solr.metrics.SolrMetricsContext;
@@ -133,4 +134,17 @@ public class InstrumentedHttpListenerFactory implements 
SolrMetricProducer, Http
   public SolrMetricsContext getSolrMetricsContext() {
     return solrMetricsContext;
   }
+
+  public static NameStrategy getNameStrategy(String name) {
+    var nameStrategy = KNOWN_METRIC_NAME_STRATEGIES.get(name);
+    if (nameStrategy == null) {
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          "Unknown metricNameStrategy: "
+              + name
+              + " found. Must be one of: "
+              + KNOWN_METRIC_NAME_STRATEGIES.keySet());
+    }
+    return nameStrategy;
+  }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java 
b/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java
new file mode 100644
index 00000000000..14b5a504634
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/core/TestHttpSolrClientProvider.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.core;
+
+import static org.apache.solr.SolrTestCaseJ4.assumeWorkingMockito;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
+import org.apache.solr.metrics.SolrMetricsContext;
+import org.apache.solr.update.UpdateShardHandlerConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestHttpSolrClientProvider extends SolrTestCase {
+
+  SolrMetricsContext parentSolrMetricCtx;
+
+  @Override
+  @Before
+  public void setUp() throws Exception {
+    super.setUp();
+    assumeWorkingMockito();
+    parentSolrMetricCtx = Mockito.mock(SolrMetricsContext.class);
+  }
+
+  @Test
+  public void test_when_updateShardHandler_cfg_is_null() {
+    try (var httpSolrClientProvider = new HttpSolrClientProvider(null, 
parentSolrMetricCtx); ) {
+      assertEquals(
+          httpSolrClientProvider.getSolrClient().getIdleTimeout(),
+          HttpClientUtil.DEFAULT_SO_TIMEOUT);
+    }
+  }
+
+  @Test
+  public void test_when_updateShardHandler_cfg_is_not_null() {
+    var idleTimeout = 10000;
+    assertNotEquals(idleTimeout, 
UpdateShardHandlerConfig.DEFAULT.getDistributedSocketTimeout());
+    UpdateShardHandlerConfig cfg = new UpdateShardHandlerConfig(-1, -1, 
idleTimeout, -1, null, -1);
+    try (var httpSolrClientProvider = new HttpSolrClientProvider(cfg, 
parentSolrMetricCtx); ) {
+      assertEquals(httpSolrClientProvider.getSolrClient().getIdleTimeout(), 
idleTimeout);
+    }
+  }
+
+  @Test
+  public void test_closing_solr_metric_context() {
+    SolrMetricsContext childSolrMetricContext = 
Mockito.mock(SolrMetricsContext.class);
+    
Mockito.when(parentSolrMetricCtx.getChildContext(any(HttpSolrClientProvider.class)))
+        .thenReturn(childSolrMetricContext);
+    try (var httpSolrClientProvider = new HttpSolrClientProvider(null, 
parentSolrMetricCtx)) {
+      assertNotNull(httpSolrClientProvider.getSolrClient());
+    } finally {
+      verify(childSolrMetricContext, times(1)).unregister();
+    }
+  }
+}
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 901b8c722e5..b91553a1813 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
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.common.util;
 
-import java.io.Closeable;
 import java.lang.invoke.MethodHandles;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -24,7 +23,7 @@ import org.slf4j.LoggerFactory;
 public class IOUtils {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  public static void closeQuietly(Closeable closeable) {
+  public static void closeQuietly(AutoCloseable closeable) {
     try {
       if (closeable != null) {
         closeable.close();

Reply via email to