Skip to content

Commit 0ebe0a1

Browse files
authored
SOLR-17874: Stop using HttpSolrClient in production (#3501)
Switch remaining usages of Apache HttpClient to use the internally managed Jetty HttpClient instance. * shard split: the commits * SYNCSHARD command/action * admin handler (e.g. log level change with nodes param) * IterativeMergeStrategy * config edit API * schema edit API * NodesSysPropsCacher: use default client not that of shardHandler Still using it in tests. And removed some deprecated internal options to use Apache HttpClient
1 parent 3ede0ed commit 0ebe0a1

File tree

15 files changed

+148
-300
lines changed

15 files changed

+148
-300
lines changed

solr/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ Other Changes
213213

214214
* SOLR-17286: When proxying requests to another node, use Jetty HttpClient not Apache HttpClient. (David Smiley)
215215

216+
* SOLR-17874: Switch remaining usages of Apache HttpClient to use the internally managed Jetty HttpClient instance. (David Smiley)
217+
216218
================== 9.10.0 ==================
217219
New Features
218220
---------------------

solr/benchmark/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ dependencies {
4646
implementation project(':solr:solrj-streaming')
4747

4848
implementation libs.apache.lucene.core
49-
implementation libs.apache.httpcomponents.httpclient
5049
implementation libs.commonsio.commonsio
5150
implementation libs.dropwizard.metrics.core
5251
implementation libs.apache.commons.math3

solr/benchmark/gradle.lockfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ com.jayway.jsonpath:json-path:2.9.0=jarValidation,runtimeClasspath,testRuntimeCl
3232
com.lmax:disruptor:3.4.4=jarValidation,runtimeClasspath,testRuntimeClasspath
3333
com.tdunning:t-digest:3.3=jarValidation,runtimeClasspath,testRuntimeClasspath
3434
commons-cli:commons-cli:1.9.0=jarValidation,runtimeClasspath,testRuntimeClasspath
35-
commons-codec:commons-codec:1.17.2=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
35+
commons-codec:commons-codec:1.17.2=jarValidation,runtimeClasspath,testRuntimeClasspath
3636
commons-io:commons-io:2.17.0=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
3737
io.dropwizard.metrics:metrics-annotation:4.2.26=jarValidation,runtimeClasspath,testRuntimeClasspath
3838
io.dropwizard.metrics:metrics-core:4.2.26=compileClasspath,jarValidation,runtimeClasspath,testCompileClasspath,testRuntimeClasspath

solr/benchmark/src/java/org/apache/solr/bench/search/StreamingSearch.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.apache.solr.bench.MiniClusterState.MiniClusterBenchState;
2929
import org.apache.solr.client.solrj.SolrServerException;
3030
import org.apache.solr.client.solrj.impl.Http2SolrClient;
31-
import org.apache.solr.client.solrj.impl.HttpClientUtil;
3231
import org.apache.solr.client.solrj.io.SolrClientCache;
3332
import org.apache.solr.client.solrj.io.Tuple;
3433
import org.apache.solr.client.solrj.io.stream.CloudSolrStream;
@@ -98,13 +97,9 @@ public void setup(MiniClusterBenchState miniClusterState) throws Exception {
9897
public void setupIteration(MiniClusterState.MiniClusterBenchState miniClusterState)
9998
throws SolrServerException, IOException {
10099
SolrClientCache solrClientCache;
101-
if (useHttp1) {
102-
var httpClient = HttpClientUtil.createClient(null); // TODO tune params?
103-
solrClientCache = new SolrClientCache(httpClient);
104-
} else {
105-
http2SolrClient = newHttp2SolrClient();
106-
solrClientCache = new SolrClientCache(http2SolrClient);
107-
}
100+
// TODO tune params?
101+
var client = new Http2SolrClient.Builder().useHttp1_1(useHttp1).build();
102+
solrClientCache = new SolrClientCache(client);
108103

109104
streamContext = new StreamContext();
110105
streamContext.setSolrClientCache(solrClientCache);
@@ -145,10 +140,4 @@ private static List<Tuple> getTuples(TupleStream tupleStream) throws IOException
145140
tupleStream.close();
146141
}
147142
}
148-
149-
public static Http2SolrClient newHttp2SolrClient() {
150-
// TODO tune params?
151-
var builder = new Http2SolrClient.Builder();
152-
return builder.build();
153-
}
154143
}

solr/core/src/java/org/apache/solr/cloud/ZkController.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
6262
import org.apache.solr.client.solrj.impl.CloudHttp2SolrClient;
6363
import org.apache.solr.client.solrj.impl.CloudSolrClient;
64-
import org.apache.solr.client.solrj.impl.Http2SolrClient;
6564
import org.apache.solr.client.solrj.impl.HttpSolrClient.Builder;
6665
import org.apache.solr.client.solrj.impl.SolrClientCloudManager;
6766
import org.apache.solr.client.solrj.impl.SolrZkClientTimeout;
@@ -118,7 +117,6 @@
118117
import org.apache.solr.core.SolrCore;
119118
import org.apache.solr.core.SolrCoreInitializationException;
120119
import org.apache.solr.handler.component.HttpShardHandler;
121-
import org.apache.solr.handler.component.HttpShardHandlerFactory;
122120
import org.apache.solr.logging.MDCLoggingContext;
123121
import org.apache.solr.search.SolrIndexSearcher;
124122
import org.apache.solr.update.UpdateLog;
@@ -393,10 +391,7 @@ public ZkController(
393391
}
394392
this.overseerCollectionQueue = overseer.getCollectionQueue(zkClient);
395393
this.overseerConfigSetQueue = overseer.getConfigSetQueue(zkClient);
396-
final var client =
397-
(Http2SolrClient)
398-
((HttpShardHandlerFactory) getCoreContainer().getShardHandlerFactory()).getClient();
399-
this.sysPropsCacher = new NodesSysPropsCacher(client, zkStateReader);
394+
this.sysPropsCacher = new NodesSysPropsCacher(cc.getDefaultHttpSolrClient(), zkStateReader);
400395
assert ObjectReleaseTracker.track(this);
401396
}
402397

solr/core/src/java/org/apache/solr/cloud/api/collections/CollectionHandlingUtils.java

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
import org.apache.solr.client.solrj.SolrClient;
4040
import org.apache.solr.client.solrj.SolrResponse;
4141
import org.apache.solr.client.solrj.SolrServerException;
42-
import org.apache.solr.client.solrj.impl.HttpSolrClient;
42+
import org.apache.solr.client.solrj.impl.Http2SolrClient;
4343
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
4444
import org.apache.solr.client.solrj.request.UpdateRequest;
4545
import org.apache.solr.client.solrj.response.UpdateResponse;
@@ -218,14 +218,19 @@ static void checkResults(String label, NamedList<Object> results, boolean failur
218218
}
219219
}
220220

221-
static void commit(NamedList<Object> results, String slice, Replica parentShardLeader) {
221+
static void commit(
222+
Http2SolrClient solrClient,
223+
NamedList<Object> results,
224+
String slice,
225+
Replica parentShardLeader) {
222226
log.debug("Calling soft commit to make sub shard updates visible");
223227
String coreUrl = parentShardLeader.getCoreUrl();
224228
// HttpShardHandler is hard coded to send a QueryRequest hence we go direct
225229
// and we force open a searcher so that we have documents to show upon switching states
226230
UpdateResponse updateResponse = null;
227231
try {
228-
updateResponse = softCommit(parentShardLeader.getBaseUrl(), parentShardLeader.getCoreName());
232+
updateResponse =
233+
softCommit(solrClient, parentShardLeader.getBaseUrl(), parentShardLeader.getCoreName());
229234
CollectionHandlingUtils.processResponse(
230235
results, null, coreUrl, updateResponse, slice, Collections.emptySet());
231236
} catch (Exception e) {
@@ -238,19 +243,12 @@ static void commit(NamedList<Object> results, String slice, Replica parentShardL
238243
}
239244
}
240245

241-
static UpdateResponse softCommit(String baseUrl, String coreName)
246+
private static UpdateResponse softCommit(
247+
Http2SolrClient solrClient, String baseUrl, String coreName)
242248
throws SolrServerException, IOException {
243-
244-
try (SolrClient client =
245-
new HttpSolrClient.Builder(baseUrl)
246-
.withDefaultCollection(coreName)
247-
.withConnectionTimeout(30000, TimeUnit.MILLISECONDS)
248-
.withSocketTimeout(120000, TimeUnit.MILLISECONDS)
249-
.build()) {
250-
UpdateRequest ureq = new UpdateRequest();
251-
ureq.setAction(AbstractUpdateRequest.ACTION.COMMIT, false, true, true);
252-
return ureq.process(client);
253-
}
249+
UpdateRequest ureq = new UpdateRequest();
250+
ureq.setAction(AbstractUpdateRequest.ACTION.COMMIT, false, true, true);
251+
return solrClient.requestWithBaseUrl(baseUrl, coreName, ureq);
254252
}
255253

256254
public static String waitForCoreNodeName(

solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,11 @@ public boolean split(ClusterState clusterState, ZkNodeProps message, NamedList<O
716716
// state switch as per SOLR-13945 so that sub shards don't come up empty, momentarily, after
717717
// being marked active)
718718
t = timings.sub("finalCommit");
719-
CollectionHandlingUtils.commit(results, slice.get(), parentShardLeader);
719+
CollectionHandlingUtils.commit(
720+
ccc.getCoreContainer().getUpdateShardHandler().getUpdateOnlyHttpClient(),
721+
results,
722+
slice.get(),
723+
parentShardLeader);
720724
t.stop();
721725
// switch sub shard states to 'active'
722726
log.info("Replication factor is 1 so switching shard states");
@@ -796,7 +800,11 @@ public boolean split(ClusterState clusterState, ZkNodeProps message, NamedList<O
796800
// when the sub-shard replicas come up
797801
if (repFactor > 1) {
798802
t = timings.sub("finalCommit");
799-
CollectionHandlingUtils.commit(results, slice.get(), parentShardLeader);
803+
CollectionHandlingUtils.commit(
804+
ccc.getCoreContainer().getUpdateShardHandler().getUpdateOnlyHttpClient(),
805+
results,
806+
slice.get(),
807+
parentShardLeader);
800808
t.stop();
801809
}
802810

solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@
5555
import org.apache.solr.api.Api;
5656
import org.apache.solr.api.ApiBag;
5757
import org.apache.solr.client.solrj.SolrResponse;
58-
import org.apache.solr.client.solrj.impl.HttpSolrClient;
58+
import org.apache.solr.client.solrj.impl.Http2SolrClient;
5959
import org.apache.solr.client.solrj.io.stream.expr.Expressible;
6060
import org.apache.solr.client.solrj.request.CollectionRequiringSolrRequest;
61+
import org.apache.solr.client.solrj.response.SimpleSolrResponse;
6162
import org.apache.solr.cloud.ZkController;
6263
import org.apache.solr.cloud.ZkSolrResourceLoader;
6364
import org.apache.solr.common.MapSerializable;
@@ -843,8 +844,10 @@ private static void waitForAllReplicasState(
843844
// course)
844845
List<PerReplicaCallable> concurrentTasks = new ArrayList<>();
845846

847+
var http2SolrClient = zkController.getCoreContainer().getDefaultHttpSolrClient();
846848
for (Replica replica : getActiveReplicas(zkController, collection)) {
847-
PerReplicaCallable e = new PerReplicaCallable(replica, prop, expectedVersion, maxWaitSecs);
849+
PerReplicaCallable e =
850+
new PerReplicaCallable(http2SolrClient, replica, prop, expectedVersion, maxWaitSecs);
848851
concurrentTasks.add(e);
849852
}
850853
if (concurrentTasks.isEmpty()) return; // nothing to wait for ...
@@ -857,6 +860,7 @@ private static void waitForAllReplicasState(
857860
}
858861

859862
// use an executor service to invoke schema zk version requests in parallel with a max wait time
863+
// TODO use httpSolrClient.requestAsync instead; it has an executor
860864
int poolSize = Math.min(concurrentTasks.size(), 10);
861865
ExecutorService parallelExecutor =
862866
ExecutorUtil.newMDCAwareFixedThreadPool(
@@ -959,14 +963,21 @@ public Name getPermissionName(AuthorizationContext ctx) {
959963

960964
private static class PerReplicaCallable extends CollectionRequiringSolrRequest<SolrResponse>
961965
implements Callable<Boolean> {
966+
private final Http2SolrClient solrClient;
962967
Replica replica;
963968
String prop;
964969
int expectedZkVersion;
965970
Number remoteVersion = null;
966971
int maxWait;
967972

968-
PerReplicaCallable(Replica replica, String prop, int expectedZkVersion, int maxWait) {
973+
PerReplicaCallable(
974+
Http2SolrClient solrClient,
975+
Replica replica,
976+
String prop,
977+
int expectedZkVersion,
978+
int maxWait) {
969979
super(METHOD.GET, "/config/" + ZNODEVER, SolrRequestType.ADMIN);
980+
this.solrClient = solrClient;
970981
this.replica = replica;
971982
this.expectedZkVersion = expectedZkVersion;
972983
this.prop = prop;
@@ -984,42 +995,41 @@ public SolrParams getParams() {
984995
public Boolean call() throws Exception {
985996
final RTimer timer = new RTimer();
986997
int attempts = 0;
987-
try (HttpSolrClient solr =
988-
new HttpSolrClient.Builder(replica.getBaseUrl())
989-
.withDefaultCollection(replica.getCoreName())
990-
.build()) {
991-
// eventually, this loop will get killed by the ExecutorService's timeout
992-
while (true) {
993-
try {
994-
long timeElapsed = (long) timer.getTime() / 1000;
995-
if (timeElapsed >= maxWait) {
996-
return false;
997-
}
998-
log.info("Time elapsed : {} secs, maxWait {}", timeElapsed, maxWait);
999-
Thread.sleep(100);
1000-
NamedList<Object> resp = solr.httpUriRequest(this).future.get();
1001-
if (resp != null) {
1002-
@SuppressWarnings({"rawtypes"})
1003-
Map m = (Map) resp.get(ZNODEVER);
1004-
if (m != null) {
1005-
remoteVersion = (Number) m.get(prop);
1006-
if (remoteVersion != null && remoteVersion.intValue() >= expectedZkVersion) break;
1007-
}
998+
// eventually, this loop will get killed by the ExecutorService's timeout
999+
while (true) {
1000+
try {
1001+
long timeElapsed = (long) timer.getTime() / 1000;
1002+
if (timeElapsed >= maxWait) {
1003+
return false;
1004+
}
1005+
log.info("Time elapsed : {} secs, maxWait {}", timeElapsed, maxWait);
1006+
Thread.sleep(100);
1007+
1008+
NamedList<Object> resp =
1009+
solrClient
1010+
.requestWithBaseUrl(replica.getBaseUrl(), replica.getCoreName(), this)
1011+
.getResponse();
1012+
if (resp != null) {
1013+
@SuppressWarnings({"rawtypes"})
1014+
Map m = (Map) resp.get(ZNODEVER);
1015+
if (m != null) {
1016+
remoteVersion = (Number) m.get(prop);
1017+
if (remoteVersion != null && remoteVersion.intValue() >= expectedZkVersion) break;
10081018
}
1019+
}
10091020

1010-
attempts++;
1011-
if (log.isInfoEnabled()) {
1012-
log.info(
1013-
formatString(
1014-
"Could not get expectedVersion {0} from {1} for prop {2} after {3} attempts",
1015-
expectedZkVersion, replica.getCoreUrl(), prop, attempts));
1016-
}
1017-
} catch (Exception e) {
1018-
if (e instanceof InterruptedException) {
1019-
break; // stop looping
1020-
} else {
1021-
log.warn("Failed to get /schema/zkversion from {} due to: ", replica.getCoreUrl(), e);
1022-
}
1021+
attempts++;
1022+
if (log.isInfoEnabled()) {
1023+
log.info(
1024+
formatString(
1025+
"Could not get expectedVersion {0} from {1} for prop {2} after {3} attempts",
1026+
expectedZkVersion, replica.getCoreUrl(), prop, attempts));
1027+
}
1028+
} catch (Exception e) {
1029+
if (e instanceof InterruptedException) {
1030+
break; // stop looping
1031+
} else {
1032+
log.warn("Failed to get /schema/zkversion from {} due to: ", replica.getCoreUrl(), e);
10231033
}
10241034
}
10251035
}
@@ -1028,7 +1038,7 @@ public Boolean call() throws Exception {
10281038

10291039
@Override
10301040
protected SolrResponse createResponse(NamedList<Object> namedList) {
1031-
return null;
1041+
return new SimpleSolrResponse();
10321042
}
10331043
}
10341044

solr/core/src/java/org/apache/solr/handler/admin/AdminHandlersProxy.java

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,23 @@
2121
import java.lang.invoke.MethodHandles;
2222
import java.net.URI;
2323
import java.util.Arrays;
24-
import java.util.HashMap;
2524
import java.util.HashSet;
25+
import java.util.LinkedHashMap;
2626
import java.util.Map;
2727
import java.util.Set;
28+
import java.util.concurrent.CompletableFuture;
2829
import java.util.concurrent.ExecutionException;
2930
import java.util.concurrent.Future;
3031
import java.util.concurrent.TimeUnit;
3132
import java.util.concurrent.TimeoutException;
32-
import org.apache.solr.client.solrj.SolrClient;
3333
import org.apache.solr.client.solrj.SolrRequest;
3434
import org.apache.solr.client.solrj.SolrServerException;
35-
import org.apache.solr.client.solrj.impl.HttpSolrClient;
3635
import org.apache.solr.client.solrj.request.GenericSolrRequest;
3736
import org.apache.solr.cloud.ZkController;
3837
import org.apache.solr.common.SolrException;
3938
import org.apache.solr.common.params.ModifiableSolrParams;
4039
import org.apache.solr.common.params.SolrParams;
4140
import org.apache.solr.common.util.NamedList;
42-
import org.apache.solr.common.util.Pair;
4341
import org.apache.solr.core.CoreContainer;
4442
import org.apache.solr.request.SolrQueryRequest;
4543
import org.apache.solr.response.SolrQueryResponse;
@@ -101,16 +99,14 @@ public static boolean maybeProxyToNodes(
10199

102100
ModifiableSolrParams params = new ModifiableSolrParams(req.getParams());
103101
params.remove(PARAM_NODES);
104-
Map<String, Pair<Future<NamedList<Object>>, SolrClient>> responses = new HashMap<>();
102+
Map<String, Future<NamedList<Object>>> responses = new LinkedHashMap<>();
105103
for (String node : nodes) {
106104
responses.put(node, callRemoteNode(node, pathStr, params, container.getZkController()));
107105
}
108106

109-
for (Map.Entry<String, Pair<Future<NamedList<Object>>, SolrClient>> entry :
110-
responses.entrySet()) {
107+
for (Map.Entry<String, Future<NamedList<Object>>> entry : responses.entrySet()) {
111108
try {
112-
NamedList<Object> resp = entry.getValue().first().get(10, TimeUnit.SECONDS);
113-
entry.getValue().second().close();
109+
NamedList<Object> resp = entry.getValue().get(10, TimeUnit.SECONDS);
114110
rsp.add(entry.getKey(), resp);
115111
} catch (ExecutionException ee) {
116112
log.warn("Exception when fetching result from node {}", entry.getKey(), ee);
@@ -125,18 +121,17 @@ public static boolean maybeProxyToNodes(
125121
return true;
126122
}
127123

128-
/**
129-
* Makes a remote request and returns a future and the solr client. The caller is responsible for
130-
* closing the client
131-
*/
132-
public static Pair<Future<NamedList<Object>>, SolrClient> callRemoteNode(
133-
String nodeName, String endpoint, SolrParams params, ZkController zkController)
124+
/** Makes a remote request asynchronously. */
125+
public static CompletableFuture<NamedList<Object>> callRemoteNode(
126+
String nodeName, String uriPath, SolrParams params, ZkController zkController)
134127
throws IOException, SolrServerException {
135-
log.debug("Proxying {} request to node {}", endpoint, nodeName);
128+
log.debug("Proxying {} request to node {}", uriPath, nodeName);
136129
URI baseUri = URI.create(zkController.zkStateReader.getBaseUrlForNodeName(nodeName));
137-
HttpSolrClient solr = new HttpSolrClient.Builder(baseUri.toString()).build();
138-
SolrRequest<?> proxyReq = new GenericSolrRequest(SolrRequest.METHOD.GET, endpoint, params);
139-
HttpSolrClient.HttpUriRequestResponse proxyResp = solr.httpUriRequest(proxyReq);
140-
return new Pair<>(proxyResp.future, solr);
130+
SolrRequest<?> proxyReq = new GenericSolrRequest(SolrRequest.METHOD.GET, uriPath, params);
131+
132+
return zkController
133+
.getCoreContainer()
134+
.getDefaultHttpSolrClient()
135+
.requestWithBaseUrl(baseUri.toString(), c -> c.requestAsync(proxyReq));
141136
}
142137
}

0 commit comments

Comments
 (0)