Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-17541: LBSolrClient implementations should agree on 'getClient()' semantics #2899

Merged
merged 35 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5261cd3
Initial development
jdyer1 Dec 4, 2024
bc8c888
fix bugs
jdyer1 Dec 5, 2024
a249169
javadoc modifications
jdyer1 Dec 5, 2024
1f8a307
tidy / remove nocommit
jdyer1 Dec 5, 2024
90bf9e2
remove obsolete test
jdyer1 Dec 6, 2024
51676f5
Fix bug: (1) Only generate a client per base url, not base url+core.…
jdyer1 Dec 6, 2024
db76064
tidy
jdyer1 Dec 6, 2024
605bdc7
Only hold the internal builder
jdyer1 Dec 6, 2024
508af0d
remove override base url from jdk client
jdyer1 Dec 6, 2024
39177b4
fix test
jdyer1 Dec 9, 2024
35651b8
Merge branch 'main' into feature/SOLR-17541
jdyer1 Dec 9, 2024
19577dc
add missing @Override
jdyer1 Dec 9, 2024
ca59536
implement code review suggestions
jdyer1 Dec 10, 2024
67c8b8f
fix getClient/buildClient
dsmiley Dec 12, 2024
7b90862
more
dsmiley Dec 12, 2024
1539d97
PKI Authentication Plugin to add the listener factory to subsequently…
jdyer1 Dec 13, 2024
8f17279
@lucene.experimental annotations
jdyer1 Dec 13, 2024
effedb9
tidy
jdyer1 Dec 13, 2024
29a3445
remove system.out.println
jdyer1 Dec 13, 2024
50470cd
httpClientBuilder > httpSolrClientBuilder
jdyer1 Dec 16, 2024
f423bd2
API change to Http2SolrClient.Builder:
jdyer1 Dec 16, 2024
6176a99
Remove unnecessary null check
jdyer1 Dec 16, 2024
783f815
tidy
jdyer1 Dec 16, 2024
e8c998d
small change to doc comment
jdyer1 Dec 16, 2024
e035a0c
fix typo
jdyer1 Dec 17, 2024
99282aa
Better variable names
jdyer1 Dec 17, 2024
45b9e10
remove silly duplicate method
jdyer1 Dec 18, 2024
7df3c3b
variable rename
jdyer1 Dec 19, 2024
90bd2ec
Merge branch 'main' into feature/SOLR-17541
jdyer1 Dec 19, 2024
f984d12
TODO on HttpClientBuilderPlugin
jdyer1 Dec 19, 2024
28aec3b
CHANGES.txt
jdyer1 Dec 19, 2024
30dcdb3
tidy
jdyer1 Dec 19, 2024
730aad0
CHANGES.txt deprecation notice for Solr 9.9
jdyer1 Dec 19, 2024
071fb04
CHANGES.txt: mention rename LBHttp2SolrClient.Builder#withListenerFac…
jdyer1 Dec 19, 2024
05eb1ea
Merge branch 'main' into feature/SOLR-17541
jdyer1 Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion solr/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Improvements

* SOLR-17516: `LBHttp2SolrClient` is now generic, adding support for `HttpJdkSolrClient`. (James Dyer)

* SOLR-17541: `LBHttp2SolrClient` now maintains a separate internal/delegate client per Solr Base URL. Both `LBHttp2SolrClient` and `CloudHttp2SolrClient`
always create and manage these internal clients. The ability for callers to provide a pre-built client is removed. Callers may specify the internal client
details by providing an instance of either `Http2SolrClient.Builder` or `HttpJdkSolrClient.Builder`. (James Dyer)

Optimizations
---------------------
* SOLR-17568: The CLI bin/solr export tool now contacts the appropriate nodes directly for data instead of proxying through one.
Expand Down Expand Up @@ -102,6 +106,11 @@ Deprecation Removals

* SOLR-17540: Removed the Hadoop Auth module, and thus Kerberos authentication and other exotic options. (Eric Pugh)

* SOLR-17541: Removed `CloudHttp2SolrClient.Builder#withHttpClient` in favor of `CloudHttp2SolrClient.Builder#withInternalClientBuilder`.
The constructor on `LBHttp2SolrClient.Builder` that took an instance of `HttpSolrClientBase` is updated to instead take an instance of
`HttpSolrClientBuilderBase`. Renamed `LBHttp2SolrClient.Builder#withListenerFactory` to `LBHttp2SolrClient.Builder#withListenerFactories`
(James Dyer)

Dependency Upgrades
---------------------
(No changes)
Expand Down Expand Up @@ -150,7 +159,10 @@ New Features

Improvements
---------------------
(No changes)
* SOLR-17541: Deprecate `CloudHttp2SolrClient.Builder#withHttpClient` in favor of
`CloudHttp2SolrClient.Builder#withInternalClientBuilder`.
Deprecate `LBHttp2SolrClient.Builder#withListenerFactory` in favor of
`LBHttp2SolrClient.Builder#withListenerFactories` (James Dyer)

Optimizations
---------------------
Expand Down
11 changes: 3 additions & 8 deletions solr/core/src/java/org/apache/solr/cloud/ZkController.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,6 @@ public String toString() {
public final ZkStateReader zkStateReader;
private SolrCloudManager cloudManager;

// only for internal usage
private Http2SolrClient http2SolrClient;

private CloudHttp2SolrClient cloudSolrClient;

private final String zkServerAddress; // example: 127.0.0.1:54062/solr
Expand Down Expand Up @@ -754,7 +751,6 @@ public void close() {
sysPropsCacher.close();
customThreadPool.execute(() -> IOUtils.closeQuietly(cloudManager));
customThreadPool.execute(() -> IOUtils.closeQuietly(cloudSolrClient));
customThreadPool.execute(() -> IOUtils.closeQuietly(http2SolrClient));

try {
try {
Expand Down Expand Up @@ -850,15 +846,14 @@ public SolrCloudManager getSolrCloudManager() {
if (cloudManager != null) {
return cloudManager;
}
http2SolrClient =
var httpSolrClientBuilder =
new Http2SolrClient.Builder()
.withHttpClient(cc.getDefaultHttpSolrClient())
.withIdleTimeout(30000, TimeUnit.MILLISECONDS)
.withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
.build();
.withConnectionTimeout(15000, TimeUnit.MILLISECONDS);
cloudSolrClient =
new CloudHttp2SolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
.withHttpClient(http2SolrClient)
.withInternalClientBuilder(httpSolrClientBuilder)
.build();
cloudManager = new SolrClientCloudManager(cloudSolrClient, cc.getObjectCache());
cloudManager.getClusterStateProvider().connect();
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT @iamsanjay ?

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
*/
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;
Expand All @@ -37,22 +36,24 @@ final class HttpSolrClientProvider implements AutoCloseable {

private final Http2SolrClient httpSolrClient;

private final Http2SolrClient.Builder httpSolrClientBuilder;

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));
this.httpSolrClientBuilder =
new Http2SolrClient.Builder().addListenerFactory(trackHttpSolrMetrics);

if (cfg != null) {
httpClientBuilder
httpSolrClientBuilder
.withConnectionTimeout(cfg.getDistributedConnectionTimeout(), TimeUnit.MILLISECONDS)
.withIdleTimeout(cfg.getDistributedSocketTimeout(), TimeUnit.MILLISECONDS)
.withMaxConnectionsPerHost(cfg.getMaxUpdateConnectionsPerHost());
}
httpSolrClient = httpClientBuilder.build();
httpSolrClient = httpSolrClientBuilder.build();
}

private InstrumentedHttpListenerFactory.NameStrategy getNameStrategy(
Expand All @@ -76,7 +77,7 @@ Http2SolrClient getSolrClient() {
}

void setSecurityBuilder(HttpClientBuilderPlugin builder) {
builder.setup(httpSolrClient);
builder.setup(httpSolrClientBuilder, httpSolrClient);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public class HttpShardHandler extends ShardHandler {
protected AtomicInteger pending;

private final Map<String, List<String>> shardToURLs;
protected LBHttp2SolrClient<Http2SolrClient> lbClient;
protected LBHttp2SolrClient<Http2SolrClient.Builder> lbClient;

public HttpShardHandler(HttpShardHandlerFactory httpShardHandlerFactory) {
this.httpShardHandlerFactory = httpShardHandlerFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory
protected ExecutorService commExecutor;

protected volatile Http2SolrClient defaultClient;
protected Http2SolrClient.Builder httpSolrClientBuilder;
protected InstrumentedHttpListenerFactory httpListenerFactory;
protected LBHttp2SolrClient<Http2SolrClient> loadbalancer;
protected LBHttp2SolrClient<Http2SolrClient.Builder> loadbalancer;

int corePoolSize = 0;
int maximumPoolSize = Integer.MAX_VALUE;
Expand Down Expand Up @@ -305,16 +306,16 @@ public void init(PluginInfo info) {
sb);
int soTimeout =
getParameter(args, HttpClientUtil.PROP_SO_TIMEOUT, HttpClientUtil.DEFAULT_SO_TIMEOUT, sb);

this.defaultClient =
this.httpSolrClientBuilder =
new Http2SolrClient.Builder()
.withConnectionTimeout(connectionTimeout, TimeUnit.MILLISECONDS)
.withIdleTimeout(soTimeout, TimeUnit.MILLISECONDS)
.withExecutor(commExecutor)
.withMaxConnectionsPerHost(maxConnectionsPerHost)
.build();
this.defaultClient.addListenerFactory(this.httpListenerFactory);
this.loadbalancer = new LBHttp2SolrClient.Builder<Http2SolrClient>(defaultClient).build();
.addListenerFactory(this.httpListenerFactory);
this.defaultClient = httpSolrClientBuilder.build();

this.loadbalancer = new LBHttp2SolrClient.Builder<>(httpSolrClientBuilder).build();

initReplicaListTransformers(getParameter(args, "replicaRouting", null, sb));

Expand All @@ -324,7 +325,7 @@ public void init(PluginInfo info) {
@Override
public void setSecurityBuilder(HttpClientBuilderPlugin clientBuilderPlugin) {
if (clientBuilderPlugin != null) {
clientBuilderPlugin.setup(defaultClient);
clientBuilderPlugin.setup(httpSolrClientBuilder, defaultClient);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ public interface HttpClientBuilderPlugin {
public SolrHttpClientBuilder getHttpClientBuilder(SolrHttpClientBuilder builder);

public default void setup(Http2SolrClient client) {}

/** TODO: Ideally, we only pass the builder here. */
public default void setup(Http2SolrClient.Builder builder, Http2SolrClient client) {
setup(client);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@ PublicKey fetchPublicKeyFromRemote(String nodename) {

@Override
public void setup(Http2SolrClient client) {
setup(null, client);
}

@Override
public void setup(Http2SolrClient.Builder builder, Http2SolrClient client) {
final HttpListenerFactory.RequestResponseListener listener =
new HttpListenerFactory.RequestResponseListener() {
private static final String CACHED_REQUEST_USER_KEY = "cachedRequestUser";
Expand Down Expand Up @@ -431,7 +436,12 @@ private Optional<String> getUserFromJettyRequest(Request request) {
(String) request.getAttributes().get(CACHED_REQUEST_USER_KEY));
}
};
client.addListenerFactory(() -> listener);
if (client != null) {
client.addListenerFactory(() -> listener);
}
if (builder != null) {
builder.addListenerFactory(() -> listener);
}
}

@Override
Expand Down
1 change: 1 addition & 0 deletions solr/core/src/test/org/apache/solr/cli/PostToolTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public void testBasicRun() throws Exception {

withBasicAuth(CollectionAdminRequest.createCollection(collection, "conf1", 1, 1, 0, 0))
.processAndWait(cluster.getSolrClient(), 10);
waitForState("creating", collection, activeClusterShape(1, 1));

File jsonDoc = File.createTempFile("temp", ".json");

Expand Down
9 changes: 4 additions & 5 deletions solr/core/src/test/org/apache/solr/cloud/OverseerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1945,17 +1945,16 @@ public Void answer(InvocationOnMock invocation) {
}

private SolrCloudManager getCloudDataProvider(ZkStateReader zkStateReader) {
var httpSolrClient =
var httpSolrClientBuilder =
new Http2SolrClient.Builder()
.withIdleTimeout(30000, TimeUnit.MILLISECONDS)
.withConnectionTimeout(15000, TimeUnit.MILLISECONDS)
.build();
.withConnectionTimeout(15000, TimeUnit.MILLISECONDS);
var cloudSolrClient =
new CloudHttp2SolrClient.Builder(new ZkClientClusterStateProvider(zkStateReader))
.withHttpClient(httpSolrClient)
.withInternalClientBuilder(httpSolrClientBuilder)
.build();
solrClients.add(cloudSolrClient);
solrClients.add(httpSolrClient);
solrClients.add(httpSolrClientBuilder.build());
SolrClientCloudManager sccm = new SolrClientCloudManager(cloudSolrClient, null);
sccm.getClusterStateProvider().connect();
return sccm;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

import java.io.IOException;

/** A lambda intended for invoking SolrClient operations */
/**
* A lambda intended for invoking SolrClient operations
*
* @lucene.experimental
*/
@FunctionalInterface
public interface SolrClientFunction<C extends SolrClient, R> {
R apply(C c) throws IOException, SolrServerException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@
public class CloudHttp2SolrClient extends CloudSolrClient {

private final ClusterStateProvider stateProvider;
private final LBHttp2SolrClient<Http2SolrClient> lbClient;
private final LBHttp2SolrClient<Http2SolrClient.Builder> lbClient;
private final Http2SolrClient myClient;
private final boolean clientIsInternal;

/**
* Create a new client object that connects to Zookeeper and is always aware of the SolrCloud
Expand All @@ -54,8 +53,8 @@ public class CloudHttp2SolrClient extends CloudSolrClient {
*/
protected CloudHttp2SolrClient(Builder builder) {
super(builder.shardLeadersOnly, builder.parallelUpdates, builder.directUpdatesToLeadersOnly);
this.clientIsInternal = builder.httpClient == null;
this.myClient = createOrGetHttpClientFromBuilder(builder);
var httpSolrClientBuilder = createOrGetHttpClientBuilder(builder);
this.myClient = httpSolrClientBuilder.build();
this.stateProvider = createClusterStateProvider(builder);
this.retryExpiryTimeNano = builder.retryExpiryTimeNano;
this.defaultCollection = builder.defaultCollection;
Expand All @@ -73,16 +72,14 @@ protected CloudHttp2SolrClient(Builder builder) {
// locks.
this.locks = objectList(builder.parallelCacheRefreshesLocks);

this.lbClient = new LBHttp2SolrClient.Builder<Http2SolrClient>(myClient).build();
this.lbClient = new LBHttp2SolrClient.Builder<>(httpSolrClientBuilder).build();
}

private Http2SolrClient createOrGetHttpClientFromBuilder(Builder builder) {
if (builder.httpClient != null) {
return builder.httpClient;
} else if (builder.internalClientBuilder != null) {
return builder.internalClientBuilder.build();
private Http2SolrClient.Builder createOrGetHttpClientBuilder(Builder builder) {
if (builder.internalClientBuilder != null) {
return builder.internalClientBuilder;
} else {
return new Http2SolrClient.Builder().build();
return new Http2SolrClient.Builder();
}
}

Expand Down Expand Up @@ -129,7 +126,7 @@ private ClusterStateProvider createHttp2ClusterStateProvider(

private void closeMyClientIfNeeded() {
try {
if (clientIsInternal && myClient != null) {
if (myClient != null) {
myClient.close();
}
} catch (Exception e) {
Expand All @@ -148,7 +145,7 @@ public void close() throws IOException {
}

@Override
public LBHttp2SolrClient<Http2SolrClient> getLbClient() {
public LBHttp2SolrClient<Http2SolrClient.Builder> getLbClient() {
return lbClient;
}

Expand All @@ -171,7 +168,6 @@ public static class Builder {
protected Collection<String> zkHosts = new ArrayList<>();
protected List<String> solrUrls = new ArrayList<>();
protected String zkChroot;
protected Http2SolrClient httpClient;
protected boolean shardLeadersOnly = true;
protected boolean directUpdatesToLeadersOnly = false;
protected boolean parallelUpdates = true;
Expand Down Expand Up @@ -404,23 +400,6 @@ public Builder withCollectionCacheTtl(long timeToLive, TimeUnit unit) {
return this;
}

/**
* Set the internal http client.
*
* <p>Note: closing the httpClient instance is at the responsibility of the caller.
*
* @param httpClient http client
* @return this
*/
public Builder withHttpClient(Http2SolrClient httpClient) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure we want to take this away from users

Copy link
Contributor Author

@jdyer1 jdyer1 Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the most controversial part of this PR, and I do not want to merge a potentially disruptive API change like this without buy-in. I do not think this is much of a burden though, because the Builder has withInternalClientBuilder, which users should be able to easily migrate to use.

The purpose of forcing this is that CloudHttp2SolrClient in turn passes this Client(Builder) to LBHttp2SolrClient. But as noted before, LBHttp2SolrClient doesn't really use this Client either; it clones it using Http2SolrClient.builder#withHttpClient. Yet we've made LBHttp2SolrClient generic so this means that any subclass of HttpSolrClientBase needs to also have a way to be cloned. Personally I think clone methods like this are brittle and a maintenance burden, and I don't want to need to write one for HttpJdkSolrClient. It also seems like a better API: by telling the user we want a Builder, we signal that we will create Clients as needed. Passing in an already-built Client signals that we will use that particular one, which is not always the case here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it clones it using Http2SolrClient.builder#withHttpClient

which still exists, so if a user somehow has a client but not a builder but needs one due to the API change you introduce, they have this option.

by telling the user we want a Builder, we signal that we will create Clients as needed

Well written James!

+1

if (this.internalClientBuilder != null) {
throw new IllegalStateException(
"The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided");
}
this.httpClient = httpClient;
return this;
}

/**
* If provided, the CloudHttp2SolrClient will build it's internal Http2SolrClient using this
* builder (instead of the empty default one). Providing this builder allows users to configure
Expand All @@ -430,10 +409,6 @@ public Builder withHttpClient(Http2SolrClient httpClient) {
* @return this
*/
public Builder withInternalClientBuilder(Http2SolrClient.Builder internalClientBuilder) {
if (this.httpClient != null) {
throw new IllegalStateException(
"The builder can't accept an httpClient AND an internalClientBuilder, only one of those can be provided");
}
this.internalClientBuilder = internalClientBuilder;
return this;
}
Expand Down
Loading
Loading