Skip to content

Commit 47c4aae

Browse files
iamsanjaydsmiley
andauthored
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]>
1 parent 33b74e6 commit 47c4aae

File tree

6 files changed

+208
-4
lines changed

6 files changed

+208
-4
lines changed

solr/core/src/java/org/apache/solr/core/CoreContainer.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import org.apache.solr.api.ClusterPluginsSource;
6868
import org.apache.solr.api.ContainerPluginsRegistry;
6969
import org.apache.solr.api.JerseyResource;
70+
import org.apache.solr.client.solrj.impl.Http2SolrClient;
7071
import org.apache.solr.client.solrj.impl.HttpClientUtil;
7172
import org.apache.solr.client.solrj.impl.SolrHttpClientBuilder;
7273
import org.apache.solr.client.solrj.impl.SolrHttpClientContextBuilder;
@@ -236,6 +237,8 @@ public JerseyAppHandlerCache getJerseyAppHandlerCache() {
236237

237238
private volatile UpdateShardHandler updateShardHandler;
238239

240+
private volatile HttpSolrClientProvider solrClientProvider;
241+
239242
private volatile ExecutorService coreContainerWorkExecutor =
240243
ExecutorUtil.newMDCAwareCachedThreadPool(
241244
new SolrNamedThreadFactory("coreContainerWorkExecutor"));
@@ -652,6 +655,7 @@ public Lookup<AuthSchemeProvider> getAuthSchemeRegistry() {
652655
pkiAuthenticationSecurityBuilder.getHttpClientBuilder(HttpClientUtil.getHttpClientBuilder());
653656
shardHandlerFactory.setSecurityBuilder(pkiAuthenticationSecurityBuilder);
654657
updateShardHandler.setSecurityBuilder(pkiAuthenticationSecurityBuilder);
658+
solrClientProvider.setSecurityBuilder(pkiAuthenticationSecurityBuilder);
655659
}
656660
}
657661

@@ -835,8 +839,9 @@ private void loadInternal() {
835839
}
836840

837841
updateShardHandler = new UpdateShardHandler(cfg.getUpdateShardHandlerConfig());
842+
solrClientProvider =
843+
new HttpSolrClientProvider(cfg.getUpdateShardHandlerConfig(), solrMetricsContext);
838844
updateShardHandler.initializeMetrics(solrMetricsContext, "updateShardHandler");
839-
840845
solrClientCache = new SolrClientCache(updateShardHandler.getDefaultHttpClient());
841846

842847
Map<String, CacheConfig> cachesConfig = cfg.getCachesConfig();
@@ -1400,6 +1405,9 @@ public void shutdown() {
14001405
if (updateShardHandler != null) {
14011406
customThreadPool.execute(updateShardHandler::close);
14021407
}
1408+
if (solrClientProvider != null) {
1409+
customThreadPool.submit(solrClientProvider::close);
1410+
}
14031411
} finally {
14041412
try {
14051413
// we want to close zk stuff last
@@ -2554,6 +2562,18 @@ public PlacementPluginFactory<? extends PlacementPluginConfig> getPlacementPlugi
25542562
return this.distributedCollectionCommandRunner;
25552563
}
25562564

2565+
/**
2566+
* Provides the existing general-purpose HTTP/2 Solr client from {@link HttpSolrClientProvider}.
2567+
*
2568+
* <p>The caller does not need to close the client, as its lifecycle is managed by {@link
2569+
* HttpSolrClientProvider}.
2570+
*
2571+
* @return the existing {@link Http2SolrClient}
2572+
*/
2573+
public Http2SolrClient getDefaultHttpSolrClient() {
2574+
return solrClientProvider.getSolrClient();
2575+
}
2576+
25572577
/**
25582578
* Run an arbitrary task in its own thread. This is an expert option and is a method you should
25592579
* use with great care. It would be bad to run something that never stopped or run something that
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.solr.core;
18+
19+
import java.util.List;
20+
import java.util.concurrent.TimeUnit;
21+
import org.apache.solr.client.solrj.impl.Http2SolrClient;
22+
import org.apache.solr.common.util.IOUtils;
23+
import org.apache.solr.metrics.SolrMetricManager;
24+
import org.apache.solr.metrics.SolrMetricsContext;
25+
import org.apache.solr.security.HttpClientBuilderPlugin;
26+
import org.apache.solr.update.UpdateShardHandlerConfig;
27+
import org.apache.solr.util.stats.InstrumentedHttpListenerFactory;
28+
29+
/**
30+
* Provider of the default SolrClient implementation.
31+
*
32+
* @lucene.internal
33+
*/
34+
final class HttpSolrClientProvider implements AutoCloseable {
35+
36+
static final String METRIC_SCOPE_NAME = "defaultHttpSolrClientProvider";
37+
38+
private final Http2SolrClient httpSolrClient;
39+
40+
private final InstrumentedHttpListenerFactory trackHttpSolrMetrics;
41+
42+
HttpSolrClientProvider(UpdateShardHandlerConfig cfg, SolrMetricsContext parentContext) {
43+
trackHttpSolrMetrics = new InstrumentedHttpListenerFactory(getNameStrategy(cfg));
44+
initializeMetrics(parentContext);
45+
46+
Http2SolrClient.Builder httpClientBuilder =
47+
new Http2SolrClient.Builder().withListenerFactory(List.of(trackHttpSolrMetrics));
48+
49+
if (cfg != null) {
50+
httpClientBuilder
51+
.withConnectionTimeout(cfg.getDistributedConnectionTimeout(), TimeUnit.MILLISECONDS)
52+
.withIdleTimeout(cfg.getDistributedSocketTimeout(), TimeUnit.MILLISECONDS)
53+
.withMaxConnectionsPerHost(cfg.getMaxUpdateConnectionsPerHost());
54+
}
55+
httpSolrClient = httpClientBuilder.build();
56+
}
57+
58+
private InstrumentedHttpListenerFactory.NameStrategy getNameStrategy(
59+
UpdateShardHandlerConfig cfg) {
60+
String metricNameStrategy =
61+
cfg != null && cfg.getMetricNameStrategy() != null
62+
? cfg.getMetricNameStrategy()
63+
: UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY;
64+
return InstrumentedHttpListenerFactory.getNameStrategy(metricNameStrategy);
65+
}
66+
67+
private void initializeMetrics(SolrMetricsContext parentContext) {
68+
var solrMetricsContext = parentContext.getChildContext(this);
69+
String expandedScope =
70+
SolrMetricManager.mkName(METRIC_SCOPE_NAME, SolrInfoBean.Category.HTTP.name());
71+
trackHttpSolrMetrics.initializeMetrics(solrMetricsContext, expandedScope);
72+
}
73+
74+
Http2SolrClient getSolrClient() {
75+
return httpSolrClient;
76+
}
77+
78+
void setSecurityBuilder(HttpClientBuilderPlugin builder) {
79+
builder.setup(httpSolrClient);
80+
}
81+
82+
@Override
83+
public void close() {
84+
IOUtils.closeQuietly(httpSolrClient);
85+
IOUtils.closeQuietly(trackHttpSolrMetrics);
86+
}
87+
}

solr/core/src/java/org/apache/solr/update/UpdateShardHandler.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,17 @@ public SolrMetricsContext getSolrMetricsContext() {
236236
return solrMetricsContext;
237237
}
238238

239-
// if you are looking for a client to use, it's probably this one.
239+
/**
240+
* Returns the default HTTP client for general-purpose usage.
241+
*
242+
* <p>This method is deprecated as the default client is now provided by {@link
243+
* org.apache.solr.core.CoreContainer#getDefaultHttpSolrClient()}. Users should prefer that method
244+
* to retrieve the default Solr client.
245+
*
246+
* @return the default {@link HttpClient}.
247+
* @deprecated Use {@link org.apache.solr.core.CoreContainer#getDefaultHttpSolrClient()} instead.
248+
*/
249+
@Deprecated
240250
public HttpClient getDefaultHttpClient() {
241251
return defaultClient;
242252
}

solr/core/src/java/org/apache/solr/util/stats/InstrumentedHttpListenerFactory.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Locale;
2525
import java.util.Map;
2626
import org.apache.solr.client.solrj.impl.HttpListenerFactory;
27+
import org.apache.solr.common.SolrException;
2728
import org.apache.solr.common.util.CollectionUtil;
2829
import org.apache.solr.metrics.SolrMetricProducer;
2930
import org.apache.solr.metrics.SolrMetricsContext;
@@ -133,4 +134,17 @@ public void initializeMetrics(SolrMetricsContext parentContext, String scope) {
133134
public SolrMetricsContext getSolrMetricsContext() {
134135
return solrMetricsContext;
135136
}
137+
138+
public static NameStrategy getNameStrategy(String name) {
139+
var nameStrategy = KNOWN_METRIC_NAME_STRATEGIES.get(name);
140+
if (nameStrategy == null) {
141+
throw new SolrException(
142+
SolrException.ErrorCode.SERVER_ERROR,
143+
"Unknown metricNameStrategy: "
144+
+ name
145+
+ " found. Must be one of: "
146+
+ KNOWN_METRIC_NAME_STRATEGIES.keySet());
147+
}
148+
return nameStrategy;
149+
}
136150
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.solr.core;
18+
19+
import static org.apache.solr.SolrTestCaseJ4.assumeWorkingMockito;
20+
import static org.mockito.ArgumentMatchers.any;
21+
import static org.mockito.Mockito.times;
22+
import static org.mockito.Mockito.verify;
23+
24+
import org.apache.solr.SolrTestCase;
25+
import org.apache.solr.client.solrj.impl.HttpClientUtil;
26+
import org.apache.solr.metrics.SolrMetricsContext;
27+
import org.apache.solr.update.UpdateShardHandlerConfig;
28+
import org.junit.Before;
29+
import org.junit.Test;
30+
import org.mockito.Mockito;
31+
32+
public class TestHttpSolrClientProvider extends SolrTestCase {
33+
34+
SolrMetricsContext parentSolrMetricCtx;
35+
36+
@Override
37+
@Before
38+
public void setUp() throws Exception {
39+
super.setUp();
40+
assumeWorkingMockito();
41+
parentSolrMetricCtx = Mockito.mock(SolrMetricsContext.class);
42+
}
43+
44+
@Test
45+
public void test_when_updateShardHandler_cfg_is_null() {
46+
try (var httpSolrClientProvider = new HttpSolrClientProvider(null, parentSolrMetricCtx); ) {
47+
assertEquals(
48+
httpSolrClientProvider.getSolrClient().getIdleTimeout(),
49+
HttpClientUtil.DEFAULT_SO_TIMEOUT);
50+
}
51+
}
52+
53+
@Test
54+
public void test_when_updateShardHandler_cfg_is_not_null() {
55+
var idleTimeout = 10000;
56+
assertNotEquals(idleTimeout, UpdateShardHandlerConfig.DEFAULT.getDistributedSocketTimeout());
57+
UpdateShardHandlerConfig cfg = new UpdateShardHandlerConfig(-1, -1, idleTimeout, -1, null, -1);
58+
try (var httpSolrClientProvider = new HttpSolrClientProvider(cfg, parentSolrMetricCtx); ) {
59+
assertEquals(httpSolrClientProvider.getSolrClient().getIdleTimeout(), idleTimeout);
60+
}
61+
}
62+
63+
@Test
64+
public void test_closing_solr_metric_context() {
65+
SolrMetricsContext childSolrMetricContext = Mockito.mock(SolrMetricsContext.class);
66+
Mockito.when(parentSolrMetricCtx.getChildContext(any(HttpSolrClientProvider.class)))
67+
.thenReturn(childSolrMetricContext);
68+
try (var httpSolrClientProvider = new HttpSolrClientProvider(null, parentSolrMetricCtx)) {
69+
assertNotNull(httpSolrClientProvider.getSolrClient());
70+
} finally {
71+
verify(childSolrMetricContext, times(1)).unregister();
72+
}
73+
}
74+
}

solr/solrj/src/java/org/apache/solr/common/util/IOUtils.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@
1616
*/
1717
package org.apache.solr.common.util;
1818

19-
import java.io.Closeable;
2019
import java.lang.invoke.MethodHandles;
2120
import org.slf4j.Logger;
2221
import org.slf4j.LoggerFactory;
2322

2423
public class IOUtils {
2524
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
2625

27-
public static void closeQuietly(Closeable closeable) {
26+
public static void closeQuietly(AutoCloseable closeable) {
2827
try {
2928
if (closeable != null) {
3029
closeable.close();

0 commit comments

Comments
 (0)