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();