You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2018/08/10 09:14:11 UTC

[31/31] lucene-solr:jira/http2: Resolved merge conflict caused by SOLR-11881

Resolved merge conflict caused by SOLR-11881


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9f554f6d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9f554f6d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9f554f6d

Branch: refs/heads/jira/http2
Commit: 9f554f6de0420e39128ce366e6e8e028c2ff66e7
Parents: 51b2a1b 9c02bf0
Author: Cao Manh Dat <da...@apache.org>
Authored: Fri Aug 10 16:13:18 2018 +0700
Committer: Cao Manh Dat <da...@apache.org>
Committed: Fri Aug 10 16:13:18 2018 +0700

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  14 +
 .../org/apache/lucene/search/BlockMaxDISI.java  |  94 ++++
 .../lucene/search/Boolean2ScorerSupplier.java   |   4 +-
 .../org/apache/lucene/search/BooleanWeight.java |   2 +-
 .../lucene/search/DisjunctionMaxQuery.java      |   2 +-
 .../lucene/search/DisjunctionMaxScorer.java     |  80 ++--
 .../apache/lucene/search/DisjunctionScorer.java |  22 +-
 .../lucene/search/DisjunctionSumScorer.java     |   4 +-
 .../apache/lucene/search/ReqOptSumScorer.java   | 175 ++++---
 .../apache/lucene/index/TestIndexWriter.java    |   2 +
 .../lucene/search/TestDisjunctionMaxQuery.java  |  77 ++-
 .../lucene/search/TestReqOptSumScorer.java      | 241 +++++++++-
 lucene/ivy-versions.properties                  |   1 -
 .../org/apache/lucene/document/LatLonShape.java |  13 +-
 .../document/LatLonShapeBoundingBoxQuery.java   | 420 +++++------------
 .../document/LatLonShapePolygonQuery.java       | 301 ++----------
 .../lucene/document/LatLonShapeQuery.java       | 394 ++++++++++++++++
 .../document/BaseLatLonShapeTestCase.java       |  46 +-
 .../document/TestLatLonLineShapeQueries.java    |  40 +-
 .../document/TestLatLonPointShapeQueries.java   |  20 +-
 .../document/TestLatLonPolygonShapeQueries.java |  36 +-
 .../apache/lucene/document/TestLatLonShape.java |   3 +-
 .../spatial3d/geom/GeoComplexPolygon.java       |   8 +-
 .../org/apache/lucene/spatial3d/geom/Plane.java |   2 +-
 .../lucene/spatial3d/geom/GeoPolygonTest.java   |  63 ++-
 .../lucene/search/RandomApproximationQuery.java |  10 +-
 solr/CHANGES.txt                                |  25 +
 .../configsets/collection1/conf/solrconfig.xml  |  20 +-
 solr/core/ivy.xml                               |   1 -
 .../org/apache/solr/core/ConfigSetService.java  |   7 +-
 .../org/apache/solr/core/CoreContainer.java     |   3 +
 .../apache/solr/handler/ReplicationHandler.java |   4 +-
 .../handler/admin/ZookeeperStatusHandler.java   | 222 +++++++++
 .../handler/component/TermVectorComponent.java  |  89 ++--
 .../solr/handler/export/DoubleValueSortDoc.java |  93 ++++
 .../solr/handler/export/ExportWriter.java       |  13 +-
 .../solr/handler/export/QuadValueSortDoc.java   | 126 +++++
 .../solr/handler/export/SingleValueSortDoc.java |  82 ++++
 .../org/apache/solr/handler/export/SortDoc.java |   7 +-
 .../solr/handler/export/TripleValueSortDoc.java | 110 +++++
 .../apache/solr/handler/loader/XMLLoader.java   |  18 +-
 .../org/apache/solr/handler/sql/SolrSchema.java |   4 +
 .../solr/response/RetrieveFieldsOptimizer.java  |   8 +-
 .../transform/BaseEditorialTransformer.java     |   2 +
 .../apache/solr/update/SolrCmdDistributor.java  | 194 +++++---
 .../processor/DistributedUpdateProcessor.java   |  36 +-
 .../ParseDateFieldUpdateProcessorFactory.java   |  82 +++-
 .../TimeRoutedAliasUpdateProcessor.java         |   4 +-
 .../org/apache/solr/util/TestInjection.java     |  22 +-
 .../solr/collection1/conf/schema-sql.xml        |  27 ++
 .../test-files/solr/collection1/conf/schema.xml |   2 +-
 ...dd-schema-fields-update-processor-chains.xml |  20 +-
 ...lrconfig-parsing-update-processor-chains.xml |  49 +-
 .../collection1/conf/solrconfig-schemaless.xml  |  20 +-
 .../configsets/_default/conf/solrconfig.xml     |  20 +-
 .../org/apache/solr/BasicFunctionalityTest.java | 158 +++----
 .../test/org/apache/solr/CursorPagingTest.java  |  10 +-
 .../solr/TestCursorMarkWithoutUniqueKey.java    |   9 +-
 .../org/apache/solr/TestDistributedSearch.java  |  61 ++-
 .../org/apache/solr/TestTolerantSearch.java     |  18 +-
 .../solr/cloud/BasicDistributedZk2Test.java     |  30 +-
 .../solr/cloud/BasicDistributedZkTest.java      |  14 +-
 .../test/org/apache/solr/cloud/BasicZkTest.java |  14 +-
 .../solr/cloud/CollectionsAPISolrJTest.java     |  46 +-
 .../apache/solr/cloud/DeleteReplicaTest.java    |  19 +-
 .../org/apache/solr/cloud/ForceLeaderTest.java  |  10 +-
 .../apache/solr/cloud/OverseerStatusTest.java   |  17 +-
 .../org/apache/solr/cloud/SolrXmlInZkTest.java  |  10 +-
 .../solr/cloud/TestAuthenticationFramework.java |  10 +-
 .../solr/cloud/TestCloudDeleteByQuery.java      |  11 +-
 .../apache/solr/cloud/TestCloudInspectUtil.java |  19 +-
 .../apache/solr/cloud/TestConfigSetsAPI.java    |  11 +-
 .../solr/cloud/TestConfigSetsAPIZkFailure.java  |  14 +-
 .../solr/cloud/TestDownShardTolerantSearch.java |  17 +-
 .../TestLeaderInitiatedRecoveryThread.java      |  64 ++-
 .../cloud/TestPullReplicaErrorHandling.java     |  13 +-
 .../TestSolrCloudWithDelegationTokens.java      |  25 +-
 .../TestSolrCloudWithSecureImpersonation.java   | 101 ++--
 .../cloud/TestTolerantUpdateProcessorCloud.java | 238 +++++-----
 .../org/apache/solr/cloud/TestZkChroot.java     |  23 +-
 .../test/org/apache/solr/cloud/ZkCLITest.java   |  18 +-
 .../org/apache/solr/cloud/ZkSolrClientTest.java |  59 +--
 .../autoscaling/AutoScalingHandlerTest.java     |  43 +-
 .../org/apache/solr/core/TestLazyCores.java     |  15 +
 .../org/apache/solr/handler/TestSQLHandler.java | 243 ++++++++--
 .../solr/handler/admin/MetricsHandlerTest.java  |   4 +-
 .../admin/ZookeeperStatusHandlerTest.java       |  85 ++++
 .../component/QueryElevationComponentTest.java  |  30 ++
 .../component/TermVectorComponentTest.java      | 340 +++++++------
 .../apache/solr/update/AddBlockUpdateTest.java  | 148 ++++++
 .../solr/update/MockingHttp2SolrClient.java     |  87 +++-
 .../solr/update/SolrCmdDistributorTest.java     | 472 +++++++++++++++----
 ...dSchemaFieldsUpdateProcessorFactoryTest.java |  23 +-
 .../ParsingFieldUpdateProcessorsTest.java       | 157 +++---
 solr/example/files/conf/solrconfig.xml          |  20 +-
 solr/licenses/joda-time-2.2.jar.sha1            |   1 -
 solr/licenses/joda-time-LICENSE-ASL.txt         | 202 --------
 solr/licenses/joda-time-NOTICE.txt              |   5 -
 .../configsets/_default/conf/solrconfig.xml     |  20 +-
 solr/solr-ref-guide/src/cloud-screens.adoc      |   5 +
 .../src/images/cloud-screens/cloud-zkstatus.png | Bin 0 -> 175359 bytes
 solr/solr-ref-guide/src/schemaless-mode.adoc    |  20 +-
 ...solrcloud-autoscaling-auto-add-replicas.adoc |   6 +-
 .../src/solrcloud-autoscaling-overview.adoc     |   2 +-
 ...olrcloud-autoscaling-policy-preferences.adoc | 167 +++++--
 .../src/transforming-result-documents.adoc      |   9 +-
 .../cloud/autoscaling/AddReplicaSuggester.java  |  10 +-
 .../client/solrj/cloud/autoscaling/Clause.java  |  18 +-
 .../cloud/autoscaling/MoveReplicaSuggester.java |   3 +-
 .../solrj/cloud/autoscaling/NodeVariable.java   |   5 +-
 .../client/solrj/cloud/autoscaling/Policy.java  |   2 +-
 .../solrj/cloud/autoscaling/PolicyHelper.java   |  38 +-
 .../solrj/cloud/autoscaling/RangeVal.java       |  10 +-
 .../solrj/cloud/autoscaling/ReplicaCount.java   |   6 +-
 .../solrj/cloud/autoscaling/ReplicaInfo.java    |  17 +-
 .../solrj/cloud/autoscaling/Suggester.java      |  14 +-
 .../solrj/cloud/autoscaling/VariableBase.java   |   1 +
 .../solrj/cloud/autoscaling/Violation.java      |  10 +-
 .../solr/client/solrj/impl/Http2SolrClient.java |  14 +-
 .../solrj/impl/SolrClientNodeStateProvider.java |   2 +-
 .../org/apache/solr/client/solrj/io/Lang.java   |   1 +
 .../solr/client/solrj/io/eval/KnnEvaluator.java |  18 +-
 .../solrj/io/eval/KnnRegressionEvaluator.java   | 194 ++++++++
 .../solrj/io/eval/MinMaxScaleEvaluator.java     |   2 +-
 .../client/solrj/io/eval/PolyFitEvaluator.java  |   7 +-
 .../client/solrj/io/eval/PredictEvaluator.java  |  30 +-
 .../solr/client/solrj/util/ClientUtils.java     |  20 +-
 .../solr/common/ConditionalMapWriter.java       |  75 +++
 .../java/org/apache/solr/common/MapWriter.java  |   6 +
 .../apache/solr/common/params/CommonParams.java |   1 +
 .../java/org/apache/solr/common/util/XML.java   |  98 +---
 .../solrj/cloud/autoscaling/TestPolicy.java     |  16 +-
 .../solrj/cloud/autoscaling/TestPolicy2.java    | 240 ++++++++++
 .../apache/solr/client/solrj/io/TestLang.java   |   2 +-
 .../solrj/io/stream/MathExpressionTest.java     |  86 +++-
 .../apache/solr/cloud/SolrCloudTestCase.java    |  19 +
 .../org/apache/solr/util/BaseTestHarness.java   |  23 +-
 solr/webapp/web/css/angular/cloud.css           |  71 ++-
 solr/webapp/web/css/angular/menu.css            |   1 +
 solr/webapp/web/index.html                      |   1 +
 solr/webapp/web/js/angular/controllers/cloud.js |  42 +-
 solr/webapp/web/js/angular/services.js          |   6 +
 solr/webapp/web/partials/cloud.html             |  59 +++
 143 files changed, 5253 insertions(+), 2442 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/lucene/ivy-versions.properties
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
----------------------------------------------------------------------
diff --cc solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
index 7437042,d5aafec..710f03f
--- a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
@@@ -17,15 -17,28 +17,18 @@@
  package org.apache.solr.update;
  
  
 -import java.io.Closeable;
  import java.io.IOException;
 -import java.io.InputStream;
  import java.lang.invoke.MethodHandles;
  import java.net.ConnectException;
+ import java.net.SocketException;
+ import java.net.SocketTimeoutException;
  import java.util.ArrayList;
 -import java.util.Collections;
 -import java.util.HashSet;
  import java.util.List;
 -import java.util.Set;
 -import java.util.concurrent.CompletionService;
 -import java.util.concurrent.ExecutorCompletionService;
 -import java.util.concurrent.Future;
 -import org.apache.http.HttpResponse;
 +import java.util.concurrent.Phaser;
 +
+ import org.apache.http.NoHttpResponseException;
 -import org.apache.solr.client.solrj.SolrClient;
  import org.apache.solr.client.solrj.SolrServerException;
 -import org.apache.solr.client.solrj.impl.BinaryResponseParser;
 -import org.apache.solr.client.solrj.impl.ConcurrentUpdateSolrClient; // jdoc
 -import org.apache.solr.client.solrj.impl.HttpSolrClient;
 +import org.apache.solr.client.solrj.impl.Http2SolrClient;
  import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
  import org.apache.solr.client.solrj.request.UpdateRequest;
  import org.apache.solr.common.SolrException;
@@@ -43,28 -56,133 +46,27 @@@ import org.slf4j.LoggerFactory
  /**
   * Used for distributing commands from a shard leader to its replicas.
   */
 -public class SolrCmdDistributor implements Closeable {
 +public class SolrCmdDistributor {
-   private static final int MAX_RETRIES_ON_FORWARD = 25;
    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 -  
 -  private StreamingSolrClients clients;
 -  private boolean finished = false; // see finish()
  
 -  private int retryPause = 500;
 -  
    private final List<Error> allErrors = new ArrayList<>();
 -  private final List<Error> errors = Collections.synchronizedList(new ArrayList<Error>());
 -  
 -  private final CompletionService<Object> completionService;
 -  private final Set<Future<Object>> pending = new HashSet<>();
 -  
 -  public static interface AbortCheck {
 -    public boolean abortCheck();
 -  }
 -  
 +  private Http2SolrClient client;
 +  private Phaser pendingTasksPhaser = new Phaser(1);
-   private int maxRetriesOnForward = MAX_RETRIES_ON_FORWARD;
++  private int retryPause = 500;
 +
    public SolrCmdDistributor(UpdateShardHandler updateShardHandler) {
 -    this.clients = new StreamingSolrClients(updateShardHandler);
 -    this.completionService = new ExecutorCompletionService<>(updateShardHandler.getUpdateExecutor());
 +    this.client = updateShardHandler.getUpdateOnlyHttpClient();
    }
--  
-   public SolrCmdDistributor(Http2SolrClient client, int maxRetriesOnForward) {
 -  public SolrCmdDistributor(StreamingSolrClients clients, int retryPause) {
 -    this.clients = clients;
++
++  public SolrCmdDistributor(Http2SolrClient client, int retryPause) {
 +    this.client = client;
-     this.maxRetriesOnForward = maxRetriesOnForward;
+     this.retryPause = retryPause;
 -    completionService = new ExecutorCompletionService<>(clients.getUpdateExecutor());
    }
    
 -  public void finish() {    
 -    try {
 -      assert ! finished : "lifecycle sanity check";
 -      finished = true;
 -      
 -      blockAndDoRetries();
 -    } finally {
 -      clients.shutdown();
 -    }
 +  public void finish() {
 +    blockUntilFinished();
    }
 -  
 -  public void close() {
 -    clients.shutdown();
 -  }
 -
 -  private void doRetriesIfNeeded() {
 -    // NOTE: retries will be forwards to a single url
 -    
 -    List<Error> errors = new ArrayList<>(this.errors);
 -    errors.addAll(clients.getErrors());
 -    List<Error> resubmitList = new ArrayList<>();
 -    
 -    if (log.isInfoEnabled() && errors.size() > 0) {
 -      log.info("SolrCmdDistributor found {} errors", errors.size());
 -    }
 -    
 -    if (log.isDebugEnabled() && errors.size() > 0) {
 -      StringBuilder builder = new StringBuilder("SolrCmdDistributor found:");
 -      int maxErrorsToShow = 10;
 -      for (Error e:errors) {
 -        if (maxErrorsToShow-- <= 0) break;
 -        builder.append("\n" + e);
 -      }
 -      if (errors.size() > 10) {
 -        builder.append("\n... and ");
 -        builder.append(errors.size() - 10);
 -        builder.append(" more");
 -      }
 -      log.debug(builder.toString());
 -    }
  
 -    for (Error err : errors) {
 -      try {
 -        String oldNodeUrl = err.req.node.getUrl();
 -        
 -        /*
 -         * if this is a retryable request we may want to retry, depending on the error we received and
 -         * the number of times we have already retried
 -         */
 -        boolean isRetry = err.req.shouldRetry(err);
 -        
 -        if (testing_errorHook != null) Diagnostics.call(testing_errorHook,
 -            err.e);
 -        
 -        // this can happen in certain situations such as close
 -        if (isRetry) {
 -          err.req.retries++;
 -
 -          if (err.req.node instanceof ForwardNode) {
 -            SolrException.log(SolrCmdDistributor.log, "forwarding update to "
 -                + oldNodeUrl + " failed - retrying ... retries: "
 -                + err.req.retries + "/" + err.req.node.getMaxRetries() + ". "
 -                + err.req.cmd.toString() + " params:"
 -                + err.req.uReq.getParams() + " rsp:" + err.statusCode, err.e);
 -          } else {
 -            SolrException.log(SolrCmdDistributor.log, "FROMLEADER request to "
 -                + oldNodeUrl + " failed - retrying ... retries: "
 -                + err.req.retries + "/" + err.req.node.getMaxRetries() + ". "
 -                + err.req.cmd.toString() + " params:"
 -                + err.req.uReq.getParams() + " rsp:" + err.statusCode, err.e);
 -          }
 -          try {
 -            Thread.sleep(retryPause); //TODO: Do we want this wait for every error?
 -          } catch (InterruptedException e) {
 -            Thread.currentThread().interrupt();
 -            log.warn(null, e);
 -          }
 -          resubmitList.add(err);
 -        } else {
 -          allErrors.add(err);
 -        }
 -      } catch (Exception e) {
 -        // continue on
 -        log.error("Unexpected Error while doing request retries", e);
 -      }
 -    }
 -    
 -    clients.clearErrors();
 -    this.errors.clear();
 -    for (Error err : resubmitList) {
 -      submit(err.req, false);
 -    }
 -    
 -    if (resubmitList.size() > 0) {
 -      blockAndDoRetries();
 -    }
 -  }
 -  
    public void distribDelete(DeleteUpdateCommand cmd, List<Node> nodes, ModifiableSolrParams params) throws IOException {
      distribDelete(cmd, nodes, params, false, null, null);
    }
@@@ -73,6 -191,10 +75,10 @@@
                              RollupRequestReplicationTracker rollupTracker,
                              LeaderRequestReplicationTracker leaderTracker) throws IOException {
      
+     if (!cmd.isDeleteById()) {
 -      blockAndDoRetries(); // For DBQ, flush all writes before submitting
++      blockUntilFinished(); // For DBQ, flush all writes before submitting
+     }
+     
      for (Node node : nodes) {
        UpdateRequest uReq = new UpdateRequest();
        uReq.setParams(params);
@@@ -153,96 -302,36 +158,97 @@@
            + req.node.getUrl() + " retry:"
            + req.retries + " " + req.cmd + " params:" + req.uReq.getParams());
      }
 -    
 -    if (isCommit) {
 -      // a commit using ConncurrentUpdateSolrServer is not async,
 -      // so we make it async to prevent commits from happening
 -      // serially across multiple nodes
 -      pending.add(completionService.submit(() -> {
 -        doRequest(req);
 -        return null;
 -      }));
 +
 +    try {
 +      req.uReq.setBasePath(req.node.getUrl());
 +      if (req.synchronous) {
 +        NamedList rsp = client.request(req.uReq);
 +        req.trackRequestResult(rsp, true);
 +        pendingTasksPhaser.arriveAndDeregister();
 +      } else {
 +        //TODO write add cmds in single outputstream
 +        client.request(req.uReq, null, new Http2SolrClient.OnComplete<NamedList>() {
 +          @Override
 +          public void onSuccess(NamedList result) {
 +            req.trackRequestResult(result, true);
 +            pendingTasksPhaser.arriveAndDeregister();
 +          }
 +
 +          @Override
 +          public void onFailure(Throwable t) {
 +            handleAndRetry(req, t, isCommit);
 +          }
 +        });
 +      }
 +    } catch (Exception e) {
 +      handleAndRetry(req, e, isCommit);
 +    }
 +  }
 +
 +  private void handleAndRetry(Req req, Throwable t, boolean isCommit) {
 +    SolrException.log(log, t);
 +    Error error = new Error();
 +    error.t = t;
 +    error.req = req;
 +    if (t instanceof SolrException) {
 +      error.statusCode = ((SolrException) t).code();
 +    }
 +    if (checkRetry(error)) {
 +      submit0(req, isCommit);
      } else {
 -      doRequest(req);
 +      req.trackRequestResult(null, false);
 +      allErrors.add(error);
 +      pendingTasksPhaser.arriveAndDeregister();
      }
    }
 -  
 -  private void doRequest(final Req req) {
 +
 +  private boolean checkRetry(Error err) {
-     String oldNodeUrl = err.req.node.getUrl();
- 
-     // if there is a retry url, we want to retry...
-     boolean isRetry = err.req.node.checkRetry();
- 
-     boolean doRetry = false;
-     int rspCode = err.statusCode;
- 
-     if (testing_errorHook != null) Diagnostics.call(testing_errorHook,
-         err.t);
- 
-     // this can happen in certain situations such as close
-     if (isRetry) {
-       if (rspCode == 404 || rspCode == 403 || rspCode == 503) {
-         doRetry = true;
-       }
- 
-       // if it's a connect exception, lets try again
-       if (err.t instanceof SolrServerException) {
-         if (((SolrServerException) err.t).getRootCause() instanceof ConnectException) {
-           doRetry = true;
-         }
-       }
++    log.info("SolrCmdDistributor got error", err);
+     try {
 -      SolrClient solrClient = clients.getSolrClient(req);
 -      solrClient.request(req.uReq);
 -    } catch (Exception e) {
 -      SolrException.log(log, e);
 -      Error error = new Error();
 -      error.e = e;
 -      error.req = req;
 -      if (e instanceof SolrException) {
 -        error.statusCode = ((SolrException) e).code();
++      String oldNodeUrl = err.req.node.getUrl();
 +
-       if (err.t instanceof ConnectException) {
-         doRetry = true;
-       }
++      /*
++       * if this is a retryable request we may want to retry, depending on the error we received and
++       * the number of times we have already retried
++       */
++      boolean isRetry = err.req.shouldRetry(err);
++      if (testing_errorHook != null) Diagnostics.call(testing_errorHook, err.t);
 +
-       if (err.req.retries < maxRetriesOnForward && doRetry) {
++      // this can happen in certain situations such as close
++      if (isRetry) {
 +        err.req.retries++;
 +
-         SolrException.log(SolrCmdDistributor.log, "forwarding update to "
-             + oldNodeUrl + " failed - retrying ... retries: "
-             + err.req.retries + " " + err.req.cmd.toString() + " params:"
-             + err.req.uReq.getParams() + " rsp:" + rspCode, err.t);
- 
++        if (err.req.node instanceof ForwardNode) {
++          SolrException.log(SolrCmdDistributor.log, "forwarding update to "
++              + oldNodeUrl + " failed - retrying ... retries: "
++              + err.req.retries + "/" + err.req.node.getMaxRetries() + ". "
++              + err.req.cmd.toString() + " params:"
++              + err.req.uReq.getParams() + " rsp:" + err.statusCode, err.t);
++        } else {
++          SolrException.log(SolrCmdDistributor.log, "FROMLEADER request to "
++              + oldNodeUrl + " failed - retrying ... retries: "
++              + err.req.retries + "/" + err.req.node.getMaxRetries() + ". "
++              + err.req.cmd.toString() + " params:"
++              + err.req.uReq.getParams() + " rsp:" + err.statusCode, err.t);
++        }
++        try {
++          Thread.sleep(retryPause); //TODO: Do we want this wait for every error?
++        } catch (InterruptedException e) {
++          Thread.currentThread().interrupt();
++          log.warn(null, e);
++        }
 +        return true;
 +      } else {
 +        return false;
        }
-     } else {
 -      errors.add(error);
++    } catch (Exception e) {
++      // continue on
++      log.error("Unexpected Error while doing request retries", e);
++      // avoid infinite loop
 +      return false;
      }
    }
 -  
 +
    public static class Req {
      public Node node;
      public UpdateRequest uReq;
@@@ -267,6 -356,16 +273,16 @@@
        this.leaderTracker = leaderTracker;
      }
      
+     /**
+      * @return true if this request should be retried after receiving a particular error
+      *         false otherwise
+      */
 -    public boolean shouldRetry(Error err) {
++    boolean shouldRetry(Error err) {
+       boolean isRetry = node.checkRetry(err);
+       isRetry &= uReq.getDeleteQuery() == null || uReq.getDeleteQuery().isEmpty(); //Don't retry DBQs 
+       return isRetry && retries < node.getMaxRetries();
+     }
+     
      public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("SolrCmdDistributor$Req: cmd=").append(cmd.toString());
@@@ -380,9 -511,33 +405,33 @@@
      }
  
      @Override
-     public boolean checkRetry() {
+     public boolean checkRetry(Error err) {
+       if (!retry) return false;
 -      
++
+       if (err.statusCode == 404 || err.statusCode == 403 || err.statusCode == 503) {
+         return true;
+       }
 -      
++
+       // if it's a connect exception, lets try again
 -      if (err.e instanceof SolrServerException) {
 -        if (isRetriableException(((SolrServerException) err.e).getRootCause())) {
++      if (err.t instanceof SolrServerException) {
++        if (isRetriableException(((SolrServerException) err.t).getRootCause())) {
+           return true;
+         }
+       } else {
 -        if (isRetriableException(err.e)) {
++        if (isRetriableException(err.t)) {
+           return true;
+         }
+       }
        return false;
      }
+     
+     /**
+      * @return true if Solr should retry in case of hitting this exception
+      *         false otherwise
+      */
+     private boolean isRetriableException(Throwable t) {
+       return t instanceof SocketException || t instanceof NoHttpResponseException || t instanceof SocketTimeoutException;
+     }
  
      @Override
      public String getBaseUrl() {
@@@ -448,23 -612,35 +506,35 @@@
      }
  
      @Override
-     public boolean checkRetry() {
-       ZkCoreNodeProps leaderProps;
-       try {
-         leaderProps = new ZkCoreNodeProps(zkStateReader.getLeaderRetry(
-             collection, shardId));
-       } catch (InterruptedException e) {
-         Thread.currentThread().interrupt();
-         return false;
-       } catch (Exception e) {
-         // we retry with same info
-         log.warn(null, e);
-         return true;
+     public boolean checkRetry(Error err) {
+       boolean doRetry = false;
+       if (err.statusCode == 404 || err.statusCode == 403 || err.statusCode == 503) {
+         doRetry = true;
        }
-      
-       this.nodeProps = leaderProps;
        
-       return true;
+       // if it's a connect exception, lets try again
 -      if (err.e instanceof SolrServerException && ((SolrServerException) err.e).getRootCause() instanceof ConnectException) {
++      if (err.t instanceof SolrServerException && ((SolrServerException) err.t).getRootCause() instanceof ConnectException) {
+         doRetry = true;
 -      } else if (err.e instanceof ConnectException) {
++      } else if (err.t instanceof ConnectException) {
+         doRetry = true;
+       }
+       if (doRetry) {
+         ZkCoreNodeProps leaderProps;
+         try {
+           leaderProps = new ZkCoreNodeProps(zkStateReader.getLeaderRetry(
+               collection, shardId));
+         } catch (InterruptedException e) {
+           Thread.currentThread().interrupt();
+           return false;
+         } catch (Exception e) {
+           // we retry with same info
+           log.warn(null, e);
+           return true;
+         }
+        
+         this.nodeProps = leaderProps;
+       }
+       return doRetry;
      }
  
      @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/solr/core/src/java/org/apache/solr/update/processor/TimeRoutedAliasUpdateProcessor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithSecureImpersonation.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/solr/core/src/test/org/apache/solr/update/MockingHttp2SolrClient.java
----------------------------------------------------------------------
diff --cc solr/core/src/test/org/apache/solr/update/MockingHttp2SolrClient.java
index b843709,0000000..2b8d8a2
mode 100644,000000..100644
--- a/solr/core/src/test/org/apache/solr/update/MockingHttp2SolrClient.java
+++ b/solr/core/src/test/org/apache/solr/update/MockingHttp2SolrClient.java
@@@ -1,95 -1,0 +1,154 @@@
 +/*
 + * 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.update;
 +
 +import java.io.IOException;
 +import java.net.ConnectException;
 +import java.net.SocketException;
++import java.util.HashSet;
++import java.util.Set;
 +
 +import org.apache.lucene.util.LuceneTestCase;
 +import org.apache.solr.client.solrj.SolrRequest;
 +import org.apache.solr.client.solrj.SolrServerException;
 +import org.apache.solr.client.solrj.impl.Http2SolrClient;
++import org.apache.solr.client.solrj.request.UpdateRequest;
++import org.apache.solr.common.SolrException;
 +import org.apache.solr.common.util.NamedList;
 +
 +public class MockingHttp2SolrClient extends Http2SolrClient {
 +
-   public enum Exp {CONNECT_EXCEPTION, SOCKET_EXCEPTION};
++  public enum Exp {CONNECT_EXCEPTION, SOCKET_EXCEPTION, BAD_REQUEST};
 +
 +  private volatile Exp exp = null;
++  private boolean oneExpPerReq;
++  private Set<SolrRequest> reqGotException;
 +
 +  public MockingHttp2SolrClient(String baseSolrUrl, Builder builder) {
 +    super(baseSolrUrl, builder);
++    this.oneExpPerReq = builder.oneExpPerReq;
++    this.reqGotException = new HashSet<>();
 +  }
 +
 +  public static class Builder extends Http2SolrClient.Builder {
++    private boolean oneExpPerReq = false;
 +
-     public Builder(String baseSolrUrl, UpdateShardHandlerConfig config) {
-       super(baseSolrUrl);
++    public Builder(UpdateShardHandlerConfig config) {
++      super();
 +      this.connectionTimeout(config.getDistributedConnectionTimeout());
 +      this.idleTimeout(config.getDistributedSocketTimeout());
 +    }
 +
 +    public MockingHttp2SolrClient build() {
-       return new MockingHttp2SolrClient(baseSolrUrl, this);
++      return new MockingHttp2SolrClient(null, this);
++    }
++
++    // DBQ won't cause exception
++    Builder oneExpPerReq() {
++      oneExpPerReq = true;
++      return this;
 +    }
 +  }
 +
 +
 +  public void setExp(Exp exp) {
 +    this.exp = exp;
 +  }
 +
-   private IOException exception() {
++  private Exception exception() {
 +    switch (exp) {
 +      case CONNECT_EXCEPTION:
 +        return new ConnectException();
 +      case SOCKET_EXCEPTION:
 +        return new SocketException();
++      case BAD_REQUEST:
++        return new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Bad Request");
 +      default:
 +        break;
 +    }
 +    return null;
 +  }
 +
 +  @Override
 +  public NamedList<Object> request(SolrRequest request, String collection)
 +      throws SolrServerException, IOException {
++    if (request instanceof UpdateRequest) {
++      UpdateRequest ur = (UpdateRequest) request;
++      if (!ur.getDeleteQuery().isEmpty())
++        return super.request(request, collection);
++    }
++
 +    if (exp != null) {
-       if (LuceneTestCase.random().nextBoolean()) {
-         throw exception();
++      if (oneExpPerReq) {
++        if (reqGotException.contains(request))
++          return super.request(request, collection);
++        else
++          reqGotException.add(request);
++      }
++
++      Exception e = exception();
++      if (e instanceof IOException) {
++        if (LuceneTestCase.random().nextBoolean()) {
++          throw (IOException) e;
++        } else {
++          throw new SolrServerException(e);
++        }
++      } else if (e instanceof SolrServerException) {
++        throw (SolrServerException) e;
 +      } else {
-         throw new SolrServerException(exception());
++        throw new SolrServerException(e);
 +      }
 +    }
 +
-     return super.request(request);
++    return super.request(request, collection);
 +  }
 +
-   public Http2ClientResponse request(SolrRequest solrRequest, String collection, OnComplete onComplete)
++  public Http2ClientResponse request(SolrRequest request, String collection, OnComplete onComplete)
 +      throws SolrServerException, IOException {
++    if (request instanceof UpdateRequest) {
++      UpdateRequest ur = (UpdateRequest) request;
++      // won't throw exception if request is DBQ
++      if (ur.getDeleteQuery() != null && !ur.getDeleteQuery().isEmpty())
++        return super.request(request, collection, onComplete);
++    }
++
 +    if (exp != null) {
-       if (LuceneTestCase.random().nextBoolean()) {
-         throw exception();
++      if (oneExpPerReq) {
++        if (reqGotException.contains(request))
++          return super.request(request, collection, onComplete);
++        else
++          reqGotException.add(request);
++      }
++
++      Exception e = exception();
++      if (e instanceof IOException) {
++        if (LuceneTestCase.random().nextBoolean()) {
++          throw (IOException) e;
++        } else {
++          throw new SolrServerException(e);
++        }
++      } else if (e instanceof SolrServerException) {
++        throw (SolrServerException) e;
 +      } else {
-         throw new SolrServerException(exception());
++        throw new SolrServerException(e);
 +      }
 +    }
-     return super.request(solrRequest, collection, onComplete);
++
++    return super.request(request, collection, onComplete);
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9f554f6d/solr/core/src/test/org/apache/solr/update/SolrCmdDistributorTest.java
----------------------------------------------------------------------
diff --cc solr/core/src/test/org/apache/solr/update/SolrCmdDistributorTest.java
index 551aed5,24cf717..08b8ef9
--- a/solr/core/src/test/org/apache/solr/update/SolrCmdDistributorTest.java
+++ b/solr/core/src/test/org/apache/solr/update/SolrCmdDistributorTest.java
@@@ -16,9 -16,9 +16,10 @@@
   */
  package org.apache.solr.update;
  
 +import javax.xml.parsers.ParserConfigurationException;
  import java.io.File;
  import java.io.IOException;
+ import java.net.SocketException;
  import java.nio.file.Path;
  import java.util.ArrayList;
  import java.util.Collections;
@@@ -45,21 -46,26 +47,29 @@@ import org.apache.solr.core.SolrCore
  import org.apache.solr.core.SolrEventListener;
  import org.apache.solr.index.LogDocMergePolicyFactory;
  import org.apache.solr.search.SolrIndexSearcher;
 -import org.apache.solr.update.MockStreamingSolrClients.Exp;
  import org.apache.solr.update.SolrCmdDistributor.Error;
+ import org.apache.solr.update.SolrCmdDistributor.ForwardNode;
  import org.apache.solr.update.SolrCmdDistributor.Node;
- import org.apache.solr.update.SolrCmdDistributor.RetryNode;
  import org.apache.solr.update.SolrCmdDistributor.StdNode;
  import org.apache.solr.update.processor.DistributedUpdateProcessor;
+ import org.apache.solr.update.processor.DistributedUpdateProcessor.LeaderRequestReplicationTracker;
+ import org.apache.solr.update.processor.DistributedUpdateProcessor.RollupRequestReplicationTracker;
  import org.junit.AfterClass;
  import org.junit.BeforeClass;
  import org.junit.Test;
  import org.xml.sax.SAXException;
  
++import static org.apache.solr.update.MockingHttp2SolrClient.Exp.BAD_REQUEST;
++import static org.apache.solr.update.MockingHttp2SolrClient.Exp.CONNECT_EXCEPTION;
++import static org.apache.solr.update.MockingHttp2SolrClient.Exp.SOCKET_EXCEPTION;
++
  // See: https://issues.apache.org/jira/browse/SOLR-12028 Tests cannot remove files on Windows machines occasionally
  public class SolrCmdDistributorTest extends BaseDistributedSearchTestCase {
 -  
 -  private static enum NodeType {FORWARD, STANDARD};
 -  
 +
++  private enum NodeType {FORWARD, STANDARD};
++
    private AtomicInteger id = new AtomicInteger();
 -  
 +
    @BeforeClass
    public static void beforeClass() throws Exception {
      // we can't use the Randomized merge policy because the test depends on
@@@ -323,33 -330,178 +334,181 @@@
              ((NamedList<Object>) resp.get("index")).get("maxDoc"));
        }
      }
 -    
 +
-     testMaxRetries();
-     testOneRetry();
+     testMaxRetries(NodeType.FORWARD);
+     testMaxRetries(NodeType.STANDARD);
+     testOneRetry(NodeType.FORWARD);
+     testOneRetry(NodeType.STANDARD);
      testRetryNodeAgainstBadAddress();
-     testRetryNodeWontRetrySocketError();
- 
+     testStdNodeRetriesSocketError();
+     testForwardNodeWontRetrySocketError();
+     testNodeWontRetryBadRequest(NodeType.FORWARD);
+     testNodeWontRetryBadRequest(NodeType.STANDARD);
+     testMinRfOnRetries(NodeType.FORWARD);
+     testMinRfOnRetries(NodeType.STANDARD);
      testDistribOpenSearcher();
+     testReqShouldRetryNoRetries();
+     testReqShouldRetryMaxRetries();
+     testReqShouldRetryBadRequest();
+     testReqShouldRetryNotFound();
+     testReqShouldRetryDBQ();
+     testDeletes(false, true);
+     testDeletes(false, false);
+     testDeletes(true, true);
+     testDeletes(true, false);
    }
 -  
 +
-   private void testMaxRetries() throws IOException {
-     final MockingHttp2SolrClient.Builder clientBuilder = new MockingHttp2SolrClient.Builder("", UpdateShardHandlerConfig.DEFAULT);
-     try (MockingHttp2SolrClient solrClient = clientBuilder.build()) {
-       SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(solrClient, 5);
-       solrClient.setExp(MockingHttp2SolrClient.Exp.CONNECT_EXCEPTION);
+   private void testDeletes(boolean dbq, boolean withFailures) throws Exception {
+     final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
+     solrclient.commit(true, true);
+     long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
+         .getNumFound();
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
++    final MockingHttp2SolrClient.Builder clientBuilder = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT)
++        .oneExpPerReq();
++    try (MockingHttp2SolrClient streamingClients = clientBuilder.build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
+       if (withFailures) {
 -        streamingClients.setExp(Exp.CONNECT_EXCEPTION);
++        streamingClients.setExp(CONNECT_EXCEPTION);
+       }
        ArrayList<Node> nodes = new ArrayList<>();
-       final HttpSolrClient solrclient1 = (HttpSolrClient) clients.get(0);
  
 -      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
 -          ZkStateReader.CORE_NAME_PROP, "");
 -
        final AtomicInteger retries = new AtomicInteger();
-       ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient1.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
-       RetryNode retryNode = new RetryNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1") {
 -      nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
++      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
+       Node retryNode = new StdNode(new ZkCoreNodeProps(nodeProps), "collection1", "shard1", 5) {
          @Override
-         public boolean checkRetry() {
+         public boolean checkRetry(Error err) {
 -          streamingClients.setExp(null);
            retries.incrementAndGet();
-           return true;
+           return super.checkRetry(err);
          }
        };
  
+ 
+       nodes.add(retryNode);
+ 
+       for (int i = 0 ; i < 5 ; i++) {
+         AddUpdateCommand cmd = new AddUpdateCommand(null);
+         int currentId = id.incrementAndGet();
+         cmd.solrDoc = sdoc("id", currentId);
+         ModifiableSolrParams params = new ModifiableSolrParams();
+         cmdDistrib.distribAdd(cmd, nodes, params);
++        cmdDistrib.blockUntilFinished();
++
+         DeleteUpdateCommand dcmd = new DeleteUpdateCommand(null);
+         if (dbq) {
+           dcmd.setQuery("id:" + currentId);
+         } else {
+           dcmd.setId(String.valueOf(currentId));
+         }
+         cmdDistrib.distribDelete(dcmd, nodes, params, false, null, null);
+       }
 -      
+ 
++
++      streamingClients.setExp(null);
+       CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
+       cmdDistrib.distribCommit(ccmd, nodes, new ModifiableSolrParams());
+       cmdDistrib.finish();
 -      
++
+       int expectedRetryCount = 0;
+       if (withFailures) {
+         if (dbq) {
 -          expectedRetryCount = 1; // just the first cmd would be retried
++          expectedRetryCount = 5;
+         } else {
+           expectedRetryCount = 10;
+         }
+       }
+       assertEquals(expectedRetryCount, retries.get());
+ 
+ 
+       long numFoundAfter = solrclient.query(new SolrQuery("*:*")).getResults()
+           .getNumFound();
+ 
+       // we will get java.net.ConnectException which we retry on
+       assertEquals(numFoundBefore, numFoundAfter);
+       assertEquals(0, cmdDistrib.getErrors().size());
+     }
+   }
+ 
+   private void testMinRfOnRetries(NodeType nodeType) throws Exception {
+     final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
 -      streamingClients.setExp(Exp.CONNECT_EXCEPTION);
++    final MockingHttp2SolrClient.Builder clientBuilder = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT);
++    try (MockingHttp2SolrClient streamingClients = clientBuilder.build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
++      streamingClients.setExp(CONNECT_EXCEPTION);
+       ArrayList<Node> nodes = new ArrayList<>();
+ 
+       ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
+           ZkStateReader.CORE_NAME_PROP, "");
+ 
+       final AtomicInteger retries = new AtomicInteger();
+       nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
+       if (nodeType == NodeType.FORWARD) {
+         nodes.add(new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 5) {
+           @Override
+           public boolean checkRetry(Error err) {
+             if (retries.incrementAndGet() >= 3) {
+               streamingClients.setExp(null);
+             }
+             return super.checkRetry(err);
+           }
+         });
+       } else {
+         nodes.add(new StdNode(new ZkCoreNodeProps(nodeProps), "collection1", "shard1", 5) {
+           @Override
+           public boolean checkRetry(Error err) {
+             if (retries.incrementAndGet() >= 3) {
+               streamingClients.setExp(null);
+             }
+             return super.checkRetry(err);
+           }
+         });
+       }
+ 
+ 
+       AddUpdateCommand cmd = new AddUpdateCommand(null);
+       cmd.solrDoc = sdoc("id", id.incrementAndGet());
+       ModifiableSolrParams params = new ModifiableSolrParams();
+       RollupRequestReplicationTracker rollupReqTracker = new RollupRequestReplicationTracker("2");
+       LeaderRequestReplicationTracker leaderReqTracker = new LeaderRequestReplicationTracker("shard1", 2);
+ 
+       cmdDistrib.distribAdd(cmd, nodes, params, false, rollupReqTracker, leaderReqTracker);
+       cmdDistrib.finish();
+       assertEquals(3, retries.get());
+       assertEquals(2, leaderReqTracker.getAchievedRf());// "2" here is because one would be the leader, that creates the instance of LeaderRequestReplicationTracker, the second one is the node
+ 
+       assertEquals(0, cmdDistrib.getErrors().size());
+     }
+   }
+ 
+   private void testMaxRetries(NodeType nodeType) throws IOException {
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
 -      streamingClients.setExp(Exp.CONNECT_EXCEPTION);
++    final MockingHttp2SolrClient.Builder clientBuilder = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT);
++    try (MockingHttp2SolrClient streamingClients = clientBuilder.build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
++      streamingClients.setExp(CONNECT_EXCEPTION);
+       ArrayList<Node> nodes = new ArrayList<>();
+       final HttpSolrClient solrclient1 = (HttpSolrClient) clients.get(0);
+ 
+       final AtomicInteger retries = new AtomicInteger();
+       ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient1.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
+       Node retryNode;
+       if (nodeType == NodeType.FORWARD) {
+         retryNode = new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 6) {
+           @Override
+           public boolean checkRetry(Error err) {
+             retries.incrementAndGet();
+             return super.checkRetry(err);
+           }
+         };
+       } else {
+         retryNode = new StdNode(new ZkCoreNodeProps(nodeProps), "collection1", "shard1", 6) {
+           @Override
+           public boolean checkRetry(Error err) {
+             retries.incrementAndGet();
+             return super.checkRetry(err);
+           }
+         };
+       }
 -      
++
+ 
        nodes.add(retryNode);
  
        AddUpdateCommand cmd = new AddUpdateCommand(null);
@@@ -364,30 -516,84 +523,81 @@@
        assertEquals(1, cmdDistrib.getErrors().size());
      }
    }
 -  
 +
-   private void testOneRetry() throws Exception {
+   private void testReqShouldRetryNoRetries() {
 -    Error err = getError(new SocketException()); 
++    Error err = getError(new SocketException());
+     SolrCmdDistributor.Req req = new SolrCmdDistributor.Req(null, new StdNode(null, "collection1", "shard1", 0), new UpdateRequest(), true);
+     assertFalse(req.shouldRetry(err));
+   }
 -  
++
+   private void testReqShouldRetryDBQ() {
 -    Error err = getError(new SocketException()); 
++    Error err = getError(new SocketException());
+     UpdateRequest dbqReq = new UpdateRequest();
+     dbqReq.deleteByQuery("*:*");
+     SolrCmdDistributor.Req req = new SolrCmdDistributor.Req(null, new StdNode(null, "collection1", "shard1", 1), dbqReq, true);
+     assertFalse(req.shouldRetry(err));
+   }
 -  
++
+   private void testReqShouldRetryMaxRetries() {
 -    Error err = getError(new SocketException()); 
++    Error err = getError(new SocketException());
+     SolrCmdDistributor.Req req = new SolrCmdDistributor.Req(null, new StdNode(null, "collection1", "shard1", 1), new UpdateRequest(), true);
+     assertTrue(req.shouldRetry(err));
+     req.retries++;
+     assertFalse(req.shouldRetry(err));
+   }
 -  
++
+   private void testReqShouldRetryBadRequest() {
 -    Error err = getError(new SolrException(SolrException.ErrorCode.BAD_REQUEST, "bad request")); 
++    Error err = getError(new SolrException(SolrException.ErrorCode.BAD_REQUEST, "bad request"));
+     SolrCmdDistributor.Req req = new SolrCmdDistributor.Req(null, new StdNode(null, "collection1", "shard1", 1), new UpdateRequest(), true);
+     assertFalse(req.shouldRetry(err));
+   }
 -  
++
+   private void testReqShouldRetryNotFound() {
+     Error err = getError(new SolrException(SolrException.ErrorCode.NOT_FOUND, "not found"));
+     SolrCmdDistributor.Req req = new SolrCmdDistributor.Req(null, new StdNode(null, "collection1", "shard1", 1), new UpdateRequest(), true);
+     assertTrue(req.shouldRetry(err));
+   }
 -  
++
+   private Error getError(Exception e) {
+     Error err = new Error();
 -    err.e = e;
++    err.t = e;
+     if (e instanceof SolrException) {
+       err.statusCode = ((SolrException)e).code();
+     }
+     return err;
+   }
 -  
++
+   private void testOneRetry(NodeType nodeType) throws Exception {
      final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
      long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
          .getNumFound();
-     final MockingHttp2SolrClient.Builder clientBuilder = new MockingHttp2SolrClient.Builder("", UpdateShardHandlerConfig.DEFAULT);
-     try (MockingHttp2SolrClient solrClient = clientBuilder.build()) {
-       SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(solrClient, 5);
-       solrClient.setExp(MockingHttp2SolrClient.Exp.CONNECT_EXCEPTION);
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
 -      streamingClients.setExp(Exp.CONNECT_EXCEPTION);
++    try (MockingHttp2SolrClient streamingClients = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT).build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
++      streamingClients.setExp(CONNECT_EXCEPTION);
        ArrayList<Node> nodes = new ArrayList<>();
  
--      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
--          ZkStateReader.CORE_NAME_PROP, "");
--
        final AtomicInteger retries = new AtomicInteger();
--      nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
-       RetryNode retryNode = new RetryNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1") {
-         @Override
-         public boolean checkRetry() {
-           solrClient.setExp(null);
-           retries.incrementAndGet();
-           return true;
-         }
-       };
++      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
+       Node retryNode;
+       if (nodeType == NodeType.FORWARD) {
+         retryNode = new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 5) {
+           @Override
+           public boolean checkRetry(Error err) {
+             streamingClients.setExp(null);
+             retries.incrementAndGet();
+             return super.checkRetry(err);
+           }
+         };
+       } else {
+         retryNode = new StdNode(new ZkCoreNodeProps(nodeProps), "collection1", "shard1", 5) {
+           @Override
+           public boolean checkRetry(Error err) {
+             streamingClients.setExp(null);
+             retries.incrementAndGet();
+             return super.checkRetry(err);
+           }
+         };
+       }
  
  
        nodes.add(retryNode);
@@@ -417,22 -624,78 +628,75 @@@
      final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
      long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
          .getNumFound();
-     final MockingHttp2SolrClient.Builder clientBuilder = new MockingHttp2SolrClient.Builder("", UpdateShardHandlerConfig.DEFAULT);
-     try (MockingHttp2SolrClient solrClient = clientBuilder.build()) {
-       SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(solrClient, 5);
-       solrClient.setExp(MockingHttp2SolrClient.Exp.SOCKET_EXCEPTION);
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
 -      streamingClients.setExp(Exp.BAD_REQUEST);
++    try (MockingHttp2SolrClient streamingClients = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT).build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
++      streamingClients.setExp(BAD_REQUEST);
        ArrayList<Node> nodes = new ArrayList<>();
- 
        ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
            ZkStateReader.CORE_NAME_PROP, "");
  
        final AtomicInteger retries = new AtomicInteger();
-       nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
-       RetryNode retryNode = new RetryNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1") {
+       Node retryNode;
+       if (nodeType == NodeType.FORWARD) {
+         retryNode = new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 5) {
+           @Override
+           public boolean checkRetry(Error err) {
+             retries.incrementAndGet();
+             return super.checkRetry(err);
+           }
+         };
+       } else {
+         retryNode = new StdNode(new ZkCoreNodeProps(nodeProps), "collection1", "shard1", 5) {
+           @Override
+           public boolean checkRetry(Error err) {
+             retries.incrementAndGet();
+             return super.checkRetry(err);
+           }
+         };
+       }
+       nodes.add(retryNode);
+ 
+       AddUpdateCommand cmd = new AddUpdateCommand(null);
+       cmd.solrDoc = sdoc("id", id.incrementAndGet());
+       ModifiableSolrParams params = new ModifiableSolrParams();
+ 
+       CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
+       cmdDistrib.distribAdd(cmd, nodes, params);
+ 
+       streamingClients.setExp(null);
+       cmdDistrib.distribCommit(ccmd, nodes, params);
+       cmdDistrib.finish();
+ 
+       // it will checkRetry, but not actually do it...
+       assertEquals(1, retries.get());
+ 
+ 
+       long numFoundAfter = solrclient.query(new SolrQuery("*:*")).getResults()
+           .getNumFound();
+ 
+       // we will get java.net.SocketException: Network is unreachable, which we don't retry on
+       assertEquals(numFoundBefore, numFoundAfter);
+       assertEquals(1, cmdDistrib.getErrors().size());
+       unIgnoreException("Bad Request");
+     }
+   }
 -  
++
+   private void testForwardNodeWontRetrySocketError() throws Exception {
+     final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
+     long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
+         .getNumFound();
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
 -      streamingClients.setExp(Exp.SOCKET_EXCEPTION);
++    try (MockingHttp2SolrClient streamingClients = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT).build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
++      streamingClients.setExp(SOCKET_EXCEPTION);
+       ArrayList<Node> nodes = new ArrayList<>();
+ 
 -      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
 -          ZkStateReader.CORE_NAME_PROP, "");
 -
+       final AtomicInteger retries = new AtomicInteger();
 -      nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
++      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
+       ForwardNode retryNode = new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 5) {
          @Override
-         public boolean checkRetry() {
+         public boolean checkRetry(Error err) {
            retries.incrementAndGet();
-           return true;
+           return super.checkRetry(err);
          }
        };
  
@@@ -462,26 -725,24 +726,21 @@@
        assertEquals(1, cmdDistrib.getErrors().size());
      }
    }
 -  
 +
-   private void testRetryNodeAgainstBadAddress() throws SolrServerException, IOException {
-     // Test RetryNode
-     {
-       SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(updateShardHandler);
-       final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
-       long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
-           .getNumFound();
- 
+   private void testStdNodeRetriesSocketError() throws Exception {
+     final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
 -    final MockStreamingSolrClients streamingClients = new MockStreamingSolrClients(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0)) {
 -      streamingClients.setExp(Exp.SOCKET_EXCEPTION);
++    try (MockingHttp2SolrClient streamingClients = new MockingHttp2SolrClient.Builder(UpdateShardHandlerConfig.DEFAULT).build()) {
++      SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(streamingClients, 0);
++      streamingClients.setExp(SOCKET_EXCEPTION);
        ArrayList<Node> nodes = new ArrayList<>();
  
-       ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, "[ff01::114]:33332" + context, ZkStateReader.CORE_NAME_PROP, "");
-       RetryNode retryNode = new RetryNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1") {
 -      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
 -          ZkStateReader.CORE_NAME_PROP, "");
 -
+       final AtomicInteger retries = new AtomicInteger();
 -      nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
++      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(), ZkStateReader.CORE_NAME_PROP, "");
+       Node retryNode = new StdNode(new ZkCoreNodeProps(nodeProps), "collection1", "shard1", 5) {
          @Override
-         public boolean checkRetry() {
-           ZkNodeProps leaderProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
-               ZkStateReader.CORE_NAME_PROP, "");
-           this.nodeProps = new ZkCoreNodeProps(leaderProps);
- 
-           return true;
+         public boolean checkRetry(Error err) {
+           retries.incrementAndGet();
+           return super.checkRetry(err);
          }
        };
  
@@@ -494,29 -754,66 +752,65 @@@
        ModifiableSolrParams params = new ModifiableSolrParams();
  
        cmdDistrib.distribAdd(cmd, nodes, params);
- 
-       CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
-       params = new ModifiableSolrParams();
-       params.set(DistributedUpdateProcessor.COMMIT_END_POINT, true);
-       cmdDistrib.distribCommit(ccmd, nodes, params);
        cmdDistrib.finish();
  
-       long numFoundAfter = solrclient.query(new SolrQuery("*:*")).getResults()
-           .getNumFound();
+       // it will checkRetry, but not actually do it...
+       assertEquals(6, retries.get());
+     }
+   }
  
-       // different OS's will throw different exceptions for the bad address above
-       if (numFoundBefore != numFoundAfter) {
-         assertEquals(0, cmdDistrib.getErrors().size());
-         assertEquals(numFoundBefore + 1, numFoundAfter);
-       } else {
-         // we will get java.net.SocketException: Network is unreachable and not retry
-         assertEquals(numFoundBefore, numFoundAfter);
+   private void testRetryNodeAgainstBadAddress() throws SolrServerException, IOException {
+     // Test RetryNode
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(updateShardHandler)) {
 -      final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
 -      long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
 -          .getNumFound();
++    SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(updateShardHandler);
++    final HttpSolrClient solrclient = (HttpSolrClient) clients.get(0);
++    long numFoundBefore = solrclient.query(new SolrQuery("*:*")).getResults()
++        .getNumFound();
+ 
 -      ArrayList<Node> nodes = new ArrayList<>();
++    ArrayList<Node> nodes = new ArrayList<>();
  
-         assertEquals(1, cmdDistrib.getErrors().size());
 -      ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, "[ff01::114]:33332" + context, ZkStateReader.CORE_NAME_PROP, "");
 -      ForwardNode retryNode = new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 5) {
 -        @Override
 -        public boolean checkRetry(Error err) {
 -          ZkNodeProps leaderProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
 -              ZkStateReader.CORE_NAME_PROP, "");
 -          this.nodeProps = new ZkCoreNodeProps(leaderProps);
++    ZkNodeProps nodeProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, "[ff01::114]:33332" + context, ZkStateReader.CORE_NAME_PROP, "");
++    ForwardNode retryNode = new ForwardNode(new ZkCoreNodeProps(nodeProps), null, "collection1", "shard1", 5) {
++      @Override
++      public boolean checkRetry(Error err) {
++        ZkNodeProps leaderProps = new ZkNodeProps(ZkStateReader.BASE_URL_PROP, solrclient.getBaseURL(),
++            ZkStateReader.CORE_NAME_PROP, "");
++        this.nodeProps = new ZkCoreNodeProps(leaderProps);
+ 
 -          return super.checkRetry(err);
 -        }
 -      };
++        return super.checkRetry(err);
 +      }
++    };
+ 
+ 
 -      nodes.add(retryNode);
++    nodes.add(retryNode);
+ 
+ 
 -      AddUpdateCommand cmd = new AddUpdateCommand(null);
 -      cmd.solrDoc = sdoc("id", id.incrementAndGet());
 -      ModifiableSolrParams params = new ModifiableSolrParams();
++    AddUpdateCommand cmd = new AddUpdateCommand(null);
++    cmd.solrDoc = sdoc("id", id.incrementAndGet());
++    ModifiableSolrParams params = new ModifiableSolrParams();
+ 
 -      cmdDistrib.distribAdd(cmd, nodes, params);
++    cmdDistrib.distribAdd(cmd, nodes, params);
+ 
 -      CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
 -      params = new ModifiableSolrParams();
 -      params.set(DistributedUpdateProcessor.COMMIT_END_POINT, true);
 -      cmdDistrib.distribCommit(ccmd, nodes, params);
 -      cmdDistrib.finish();
++    CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
++    params = new ModifiableSolrParams();
++    params.set(DistributedUpdateProcessor.COMMIT_END_POINT, true);
++    cmdDistrib.distribCommit(ccmd, nodes, params);
++    cmdDistrib.finish();
+ 
 -      long numFoundAfter = solrclient.query(new SolrQuery("*:*")).getResults()
 -          .getNumFound();
++    long numFoundAfter = solrclient.query(new SolrQuery("*:*")).getResults()
++        .getNumFound();
+ 
 -      // different OS's will throw different exceptions for the bad address above
 -      if (numFoundBefore != numFoundAfter) {
 -        assertEquals(0, cmdDistrib.getErrors().size());
 -        assertEquals(numFoundBefore + 1, numFoundAfter);
 -      } else {
 -        // we will get java.net.SocketException: Network is unreachable and not retry
 -        assertEquals(numFoundBefore, numFoundAfter);
++    // different OS's will throw different exceptions for the bad address above
++    if (numFoundBefore != numFoundAfter) {
++      assertEquals(0, cmdDistrib.getErrors().size());
++      assertEquals(numFoundBefore + 1, numFoundAfter);
++    } else {
++      // we will get java.net.SocketException: Network is unreachable and not retry
++      assertEquals(numFoundBefore, numFoundAfter);
+ 
 -        assertEquals(1, cmdDistrib.getErrors().size());
 -      }
++      assertEquals(1, cmdDistrib.getErrors().size());
      }
    }
 -  
 +
    @Override
    public void distribTearDown() throws Exception {
      updateShardHandler.close();
@@@ -524,23 -821,22 +818,21 @@@
    }
  
    private void testDistribOpenSearcher() {
-     {
-       SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(updateShardHandler);
 -    try (SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(updateShardHandler)) {
--      UpdateRequest updateRequest = new UpdateRequest();
++    SolrCmdDistributor cmdDistrib = new SolrCmdDistributor(updateShardHandler);
++    UpdateRequest updateRequest = new UpdateRequest();
  
--      CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
++    CommitUpdateCommand ccmd = new CommitUpdateCommand(null, false);
  
--      //test default value (should be true)
--      cmdDistrib.addCommit(updateRequest, ccmd);
--      boolean openSearcher = updateRequest.getParams().getBool(UpdateParams.OPEN_SEARCHER, false);
--      assertTrue(openSearcher);
++    //test default value (should be true)
++    cmdDistrib.addCommit(updateRequest, ccmd);
++    boolean openSearcher = updateRequest.getParams().getBool(UpdateParams.OPEN_SEARCHER, false);
++    assertTrue(openSearcher);
  
--      //test openSearcher = false
--      ccmd.openSearcher = false;
++    //test openSearcher = false
++    ccmd.openSearcher = false;
  
--      cmdDistrib.addCommit(updateRequest, ccmd);
--      openSearcher = updateRequest.getParams().getBool(UpdateParams.OPEN_SEARCHER, true);
--      assertFalse(openSearcher);
--    }
++    cmdDistrib.addCommit(updateRequest, ccmd);
++    openSearcher = updateRequest.getParams().getBool(UpdateParams.OPEN_SEARCHER, true);
++    assertFalse(openSearcher);
    }
  }