All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.client.solrj.impl.CloudLegacySolrClient Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * 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.client.solrj.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.solr.client.solrj.impl.SolrZkClientTimeout.SolrZkClientTimeoutAware;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.DocRouter;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.NamedList;

/**
 * SolrJ client class to communicate with SolrCloud. Instances of this class communicate with
 * Zookeeper to discover Solr endpoints for SolrCloud collections, and then use the {@link
 * LBHttpSolrClient} to issue requests.
 *
 * @deprecated Please use {@link CloudSolrClient}
 */
@Deprecated(since = "9.0")
public class CloudLegacySolrClient extends CloudSolrClient {

  private final ClusterStateProvider stateProvider;
  private final LBHttpSolrClient lbClient;
  private final boolean shutdownLBHttpSolrServer;
  private HttpClient myClient;
  private final boolean clientIsInternal;

  public static final String STATE_VERSION = CloudSolrClient.STATE_VERSION;

  /**
   * Create a new client object that connects to Zookeeper and is always aware of the SolrCloud
   * state. If there is a fully redundant Zookeeper quorum and SolrCloud has enough replicas for
   * every shard in a collection, there is no single point of failure. Updates will be sent to shard
   * leaders by default.
   *
   * @param builder a {@link Builder} with the options used to create the client.
   */
  protected CloudLegacySolrClient(Builder builder) {
    super(builder.shardLeadersOnly, builder.parallelUpdates, builder.directUpdatesToLeadersOnly);
    this.stateProvider = builder.stateProvider;
    this.retryExpiryTimeNano = builder.retryExpiryTimeNano;
    this.defaultCollection = builder.defaultCollection;
    this.collectionStateCache.timeToLiveMs =
        TimeUnit.MILLISECONDS.convert(builder.timeToLiveSeconds, TimeUnit.SECONDS);
    this.clientIsInternal = builder.httpClient == null;
    this.shutdownLBHttpSolrServer = builder.loadBalancedSolrClient == null;
    if (builder.lbClientBuilder != null) {
      propagateLBClientConfigOptions(builder);
      builder.loadBalancedSolrClient = builder.lbClientBuilder.build();
    }
    if (builder.loadBalancedSolrClient != null)
      builder.httpClient = builder.loadBalancedSolrClient.getHttpClient();
    this.myClient =
        (builder.httpClient == null) ? HttpClientUtil.createClient(null) : builder.httpClient;
    if (builder.loadBalancedSolrClient == null)
      builder.loadBalancedSolrClient = createLBHttpSolrClient(builder, myClient);
    this.lbClient = builder.loadBalancedSolrClient;
  }

  private void propagateLBClientConfigOptions(Builder builder) {
    final LBHttpSolrClient.Builder lbBuilder = builder.lbClientBuilder;
    lbBuilder.withConnectionTimeout(builder.connectionTimeoutMillis, TimeUnit.MILLISECONDS);
    lbBuilder.withSocketTimeout(builder.socketTimeoutMillis, TimeUnit.MILLISECONDS);
  }

  @Override
  protected Map createRoutes(
      UpdateRequest updateRequest,
      ModifiableSolrParams routableParams,
      DocCollection col,
      DocRouter router,
      Map> urlMap,
      String idField) {
    return urlMap == null
        ? null
        : updateRequest.getRoutesToCollection(router, col, urlMap, routableParams, idField);
  }

  @Override
  protected RouteException getRouteException(
      SolrException.ErrorCode serverError,
      NamedList exceptions,
      Map routes) {
    return new RouteException(serverError, exceptions, routes);
  }

  @Override
  public void close() throws IOException {
    stateProvider.close();

    if (shutdownLBHttpSolrServer) {
      lbClient.close();
    }

    if (clientIsInternal && myClient != null) {
      HttpClientUtil.close(myClient);
    }

    super.close();
  }

  @Override
  public LBHttpSolrClient getLbClient() {
    return lbClient;
  }

  public HttpClient getHttpClient() {
    return myClient;
  }

  @Override
  public ClusterStateProvider getClusterStateProvider() {
    return stateProvider;
  }

  @Override
  protected boolean wasCommError(Throwable rootCause) {
    return rootCause instanceof ConnectTimeoutException
        || rootCause instanceof NoHttpResponseException;
  }

  private static LBHttpSolrClient createLBHttpSolrClient(
      Builder cloudSolrClientBuilder, HttpClient httpClient) {
    final LBHttpSolrClient.Builder lbBuilder = new LBHttpSolrClient.Builder();
    lbBuilder.withHttpClient(httpClient);
    lbBuilder.withConnectionTimeout(
        cloudSolrClientBuilder.connectionTimeoutMillis, TimeUnit.MILLISECONDS);
    lbBuilder.withSocketTimeout(cloudSolrClientBuilder.socketTimeoutMillis, TimeUnit.MILLISECONDS);
    lbBuilder.withRequestWriter(new BinaryRequestWriter());
    lbBuilder.withResponseParser(new BinaryResponseParser());

    return lbBuilder.build();
  }

  /** Constructs {@link CloudLegacySolrClient} instances from provided configuration. */
  public static class Builder extends SolrClientBuilder {
    protected Collection zkHosts = new ArrayList<>();
    protected List solrUrls = new ArrayList<>();
    protected String zkChroot;
    protected LBHttpSolrClient loadBalancedSolrClient;
    protected LBHttpSolrClient.Builder lbClientBuilder;
    protected boolean shardLeadersOnly = true;
    protected boolean directUpdatesToLeadersOnly = false;
    protected boolean parallelUpdates = true;
    protected long retryExpiryTimeNano =
        TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS); // 3 seconds or 3 million nanos
    protected ClusterStateProvider stateProvider;
    private int zkConnectTimeout = SolrZkClientTimeout.DEFAULT_ZK_CONNECT_TIMEOUT;
    private int zkClientTimeout = SolrZkClientTimeout.DEFAULT_ZK_CLIENT_TIMEOUT;
    private boolean canUseZkACLs = true;

    /** Constructor for use by subclasses. This constructor was public prior to version 9.0 */
    protected Builder() {}

    /**
     * Provide a series of Solr URLs to be used when configuring {@link CloudLegacySolrClient}
     * instances. The solr client will use these urls to understand the cluster topology, which solr
     * nodes are active etc.
     *
     * 

Provided Solr URLs are expected to point to the root Solr path * ("http://hostname:8983/solr"); they should not include any collections, cores, or other path * components. * *

Usage example: * *

     *   final List<String> solrBaseUrls = new ArrayList<String>();
     *   solrBaseUrls.add("http://solr1:8983/solr"); solrBaseUrls.add("http://solr2:8983/solr"); solrBaseUrls.add("http://solr3:8983/solr");
     *   final SolrClient client = new CloudSolrClient.Builder(solrBaseUrls).build();
     * 
*/ public Builder(List solrUrls) { this.solrUrls = solrUrls; } /** Provide an already created {@link ClusterStateProvider} instance */ public Builder(ClusterStateProvider stateProvider) { this.stateProvider = stateProvider; } /** * Provide a series of ZK hosts which will be used when configuring {@link * CloudLegacySolrClient} instances. * *

Usage example when Solr stores data at the ZooKeeper root ('/'): * *

     *   final List<String> zkServers = new ArrayList<String>();
     *   zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
     *   final SolrClient client = new CloudSolrClient.Builder(zkServers, Optional.empty()).build();
     * 
* * Usage example when Solr data is stored in a ZooKeeper chroot: * *
     *    final List<String> zkServers = new ArrayList<String>();
     *    zkServers.add("zookeeper1:2181"); zkServers.add("zookeeper2:2181"); zkServers.add("zookeeper3:2181");
     *    final SolrClient client = new CloudSolrClient.Builder(zkServers, Optional.of("/solr")).build();
     *  
* * @param zkHosts a List of at least one ZooKeeper host and port (e.g. "zookeeper1:2181") * @param zkChroot the path to the root ZooKeeper node containing Solr data. Provide {@code * java.util.Optional.empty()} if no ZK chroot is used. */ public Builder(List zkHosts, Optional zkChroot) { this.zkHosts = zkHosts; if (zkChroot.isPresent()) this.zkChroot = zkChroot.get(); } /** Whether to use the default ZK ACLs when building a ZK Client. */ public Builder canUseZkACLs(boolean canUseZkACLs) { this.canUseZkACLs = canUseZkACLs; return this; } /** Provides a {@link HttpClient} for the builder to use when creating clients. */ public Builder withLBHttpSolrClientBuilder(LBHttpSolrClient.Builder lbHttpSolrClientBuilder) { this.lbClientBuilder = lbHttpSolrClientBuilder; return this; } /** Provides a {@link LBHttpSolrClient} for the builder to use when creating clients. */ public Builder withLBHttpSolrClient(LBHttpSolrClient loadBalancedSolrClient) { this.loadBalancedSolrClient = loadBalancedSolrClient; return this; } /** * Sets the cache ttl for DocCollection Objects cached. * * @param seconds ttl value in seconds */ public Builder withCollectionCacheTtl(int seconds) { assert seconds > 0; this.timeToLiveSeconds = seconds; return this; } /** * Tells {@link Builder} that created clients should be configured such that {@link * CloudSolrClient#isUpdatesToLeaders} returns true. * * @see #sendUpdatesToAnyReplica * @see CloudSolrClient#isUpdatesToLeaders */ public Builder sendUpdatesOnlyToShardLeaders() { shardLeadersOnly = true; return this; } /** * Tells {@link Builder} that created clients should be configured such that {@link * CloudSolrClient#isUpdatesToLeaders} returns false. * * @see #sendUpdatesOnlyToShardLeaders * @see CloudSolrClient#isUpdatesToLeaders */ public Builder sendUpdatesToAnyReplica() { shardLeadersOnly = false; return this; } /** * This method has no effect. * *

In older versions of Solr, this method was an incorrectly named equivalent to {@link * #sendUpdatesToAnyReplica}, which had no effect because that setting was ignored in the * created clients. When the underlying {@link CloudSolrClient} behavior was fixed, this method * was modified to be an explicit No-Op, since the implied behavior of sending updates to * all replicas has never been supported, and was never intended to be supported. * * @see #sendUpdatesOnlyToShardLeaders * @see #sendUpdatesToAnyReplica * @see CloudSolrClient#isUpdatesToLeaders * @see SOLR-6312 * @deprecated Never supported */ @Deprecated public Builder sendUpdatesToAllReplicasInShard() { return this; } /** * Tells {@link Builder} that created clients should send direct updates to shard leaders only. * *

UpdateRequests whose leaders cannot be found will "fail fast" on the client side with a * {@link SolrException} * * @see #sendDirectUpdatesToAnyShardReplica * @see CloudSolrClient#isDirectUpdatesToLeadersOnly */ public Builder sendDirectUpdatesToShardLeadersOnly() { directUpdatesToLeadersOnly = true; return this; } /** * Tells {@link Builder} that created clients can send updates to any shard replica (shard * leaders and non-leaders). * *

Shard leaders are still preferred, but the created clients will fall back to using other * replicas if a leader cannot be found. * * @see #sendDirectUpdatesToShardLeadersOnly * @see CloudSolrClient#isDirectUpdatesToLeadersOnly */ public Builder sendDirectUpdatesToAnyShardReplica() { directUpdatesToLeadersOnly = false; return this; } /** * Tells {@link Builder} whether created clients should send shard updates serially or in * parallel * *

When an {@link UpdateRequest} affects multiple shards, {@link CloudLegacySolrClient} * splits it up and sends a request to each affected shard. This setting chooses whether those * sub-requests are sent serially or in parallel. * *

If not set, this defaults to 'true' and sends sub-requests in parallel. */ public Builder withParallelUpdates(boolean parallelUpdates) { this.parallelUpdates = parallelUpdates; return this; } /** * Sets the Zk connection timeout * * @param zkConnectTimeout timeout value * @param unit time unit */ public Builder withZkConnectTimeout(int zkConnectTimeout, TimeUnit unit) { this.zkConnectTimeout = Math.toIntExact(unit.toMillis(zkConnectTimeout)); return this; } /** * Sets the Zk client session timeout * * @param zkClientTimeout timeout value * @param unit time unit */ public Builder withZkClientTimeout(int zkClientTimeout, TimeUnit unit) { this.zkClientTimeout = Math.toIntExact(unit.toMillis(zkClientTimeout)); return this; } /** Create a {@link CloudLegacySolrClient} based on the provided configuration. */ public CloudLegacySolrClient build() { if (stateProvider == null) { if (!zkHosts.isEmpty() && !solrUrls.isEmpty()) { throw new IllegalArgumentException( "Both zkHost(s) & solrUrl(s) have been specified. Only specify one."); } else if (!zkHosts.isEmpty()) { this.stateProvider = ClusterStateProvider.newZkClusterStateProvider(zkHosts, zkChroot, canUseZkACLs); if (stateProvider instanceof SolrZkClientTimeoutAware) { var timeoutAware = (SolrZkClientTimeoutAware) stateProvider; timeoutAware.setZkClientTimeout(zkClientTimeout); timeoutAware.setZkConnectTimeout(zkConnectTimeout); } } else if (!this.solrUrls.isEmpty()) { try { stateProvider = new HttpClusterStateProvider(solrUrls, httpClient); } catch (Exception e) { throw new RuntimeException( "Couldn't initialize a HttpClusterStateProvider (is/are the " + "Solr server(s), " + solrUrls + ", down?)", e); } } else { throw new IllegalArgumentException("Both zkHosts and solrUrl cannot be null."); } } return new CloudLegacySolrClient(this); } @Override public Builder getThis() { return this; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy