SOLR-10479: Adds support for HttpShardHandlerFactory.loadBalancerRequests(MinimumAbsolute|MaximumFraction) configuration. (Ramsey Haddad, Daniel Collins, Christine Poerschke)

This commit is contained in:
Christine Poerschke 2017-05-25 12:30:30 +01:00
parent 0b47126e5c
commit 2bb6e2caca
5 changed files with 202 additions and 4 deletions

View File

@ -227,6 +227,9 @@ New Features
* SOLR-10721: Provide a way to know when Core Discovery is finished and when all async cores are done loading
(Erick Erickson)
* SOLR-10479: Adds support for HttpShardHandlerFactory.loadBalancerRequests(MinimumAbsolute|MaximumFraction)
configuration. (Ramsey Haddad, Daniel Collins, Christine Poerschke)
Bug Fixes
----------------------
* SOLR-10723 JSON Facet API: resize() implemented incorrectly for CountSlotAcc, HllAgg.NumericAcc

View File

@ -97,6 +97,8 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
int maximumPoolSize = Integer.MAX_VALUE;
int keepAliveTime = 5;
int queueSize = -1;
int permittedLoadBalancerRequestsMinimumAbsolute = 0;
float permittedLoadBalancerRequestsMaximumFraction = 1.0f;
boolean accessPolicy = false;
private String scheme = null;
@ -122,6 +124,12 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
// If the threadpool uses a backing queue, what is its maximum size (-1) to use direct handoff
static final String INIT_SIZE_OF_QUEUE = "sizeOfQueue";
// The minimum number of replicas that may be used
static final String LOAD_BALANCER_REQUESTS_MIN_ABSOLUTE = "loadBalancerRequestsMinimumAbsolute";
// The maximum proportion of replicas to be used
static final String LOAD_BALANCER_REQUESTS_MAX_FRACTION = "loadBalancerRequestsMaximumFraction";
// Configure if the threadpool favours fairness over throughput
static final String INIT_FAIRNESS_POLICY = "fairnessPolicy";
@ -164,6 +172,16 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
this.maximumPoolSize = getParameter(args, INIT_MAX_POOL_SIZE, maximumPoolSize,sb);
this.keepAliveTime = getParameter(args, MAX_THREAD_IDLE_TIME, keepAliveTime,sb);
this.queueSize = getParameter(args, INIT_SIZE_OF_QUEUE, queueSize,sb);
this.permittedLoadBalancerRequestsMinimumAbsolute = getParameter(
args,
LOAD_BALANCER_REQUESTS_MIN_ABSOLUTE,
permittedLoadBalancerRequestsMinimumAbsolute,
sb);
this.permittedLoadBalancerRequestsMaximumFraction = getParameter(
args,
LOAD_BALANCER_REQUESTS_MAX_FRACTION,
permittedLoadBalancerRequestsMaximumFraction,
sb);
this.accessPolicy = getParameter(args, INIT_FAIRNESS_POLICY, accessPolicy,sb);
log.debug("created with {}",sb);
@ -252,7 +270,15 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements org.
*/
public LBHttpSolrClient.Rsp makeLoadBalancedRequest(final QueryRequest req, List<String> urls)
throws SolrServerException, IOException {
return loadbalancer.request(new LBHttpSolrClient.Req(req, urls));
return loadbalancer.request(newLBHttpSolrClientReq(req, urls));
}
protected LBHttpSolrClient.Req newLBHttpSolrClientReq(final QueryRequest req, List<String> urls) {
int numServersToTry = (int)Math.floor(urls.size() * this.permittedLoadBalancerRequestsMaximumFraction);
if (numServersToTry < this.permittedLoadBalancerRequestsMinimumAbsolute) {
numServersToTry = this.permittedLoadBalancerRequestsMinimumAbsolute;
}
return new LBHttpSolrClient.Req(req, urls, numServersToTry);
}
/**

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
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.
-->
<solr>
<shardHandlerFactory name="shardHandlerFactory" class="solr.HttpShardHandlerFactory">
<int name="loadBalancerRequestsMinimumAbsolute">${solr.tests.loadBalancerRequestsMinimumAbsolute:0}</int>
<float name="loadBalancerRequestsMaximumFraction">${solr.tests.loadBalancerRequestsMaximumFraction:1.0}</float>
</shardHandlerFactory>
</solr>

View File

@ -0,0 +1,102 @@
/*
* 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.handler.component;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.impl.LBHttpSolrClient;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.handler.component.HttpShardHandlerFactory;
import org.apache.solr.handler.component.ShardHandlerFactory;
import org.junit.BeforeClass;
import org.junit.AfterClass;
/**
* Tests specifying a custom ShardHandlerFactory
*/
public class TestHttpShardHandlerFactory extends SolrTestCaseJ4 {
private static final String LOAD_BALANCER_REQUESTS_MIN_ABSOLUTE = "solr.tests.loadBalancerRequestsMinimumAbsolute";
private static final String LOAD_BALANCER_REQUESTS_MAX_FRACTION = "solr.tests.loadBalancerRequestsMaximumFraction";
private static int expectedLoadBalancerRequestsMinimumAbsolute = 0;
private static float expectedLoadBalancerRequestsMaximumFraction = 1.0f;
@BeforeClass
public static void beforeTests() throws Exception {
expectedLoadBalancerRequestsMinimumAbsolute = random().nextInt(3); // 0 .. 2
expectedLoadBalancerRequestsMaximumFraction = (1+random().nextInt(10))/10f; // 0.1 .. 1.0
System.setProperty(LOAD_BALANCER_REQUESTS_MIN_ABSOLUTE, Integer.toString(expectedLoadBalancerRequestsMinimumAbsolute));
System.setProperty(LOAD_BALANCER_REQUESTS_MAX_FRACTION, Float.toString(expectedLoadBalancerRequestsMaximumFraction));
}
@AfterClass
public static void afterTests() {
System.clearProperty(LOAD_BALANCER_REQUESTS_MIN_ABSOLUTE);
System.clearProperty(LOAD_BALANCER_REQUESTS_MAX_FRACTION);
}
public void testLoadBalancerRequestsMinMax() throws Exception {
final Path home = Paths.get(TEST_HOME());
CoreContainer cc = null;
ShardHandlerFactory factory = null;
try {
cc = CoreContainer.createAndLoad(home, home.resolve("solr-shardhandler-loadBalancerRequests.xml"));
factory = cc.getShardHandlerFactory();
// test that factory is HttpShardHandlerFactory with expected url reserve fraction
assertTrue(factory instanceof HttpShardHandlerFactory);
final HttpShardHandlerFactory httpShardHandlerFactory = ((HttpShardHandlerFactory)factory);
assertEquals(expectedLoadBalancerRequestsMinimumAbsolute, httpShardHandlerFactory.permittedLoadBalancerRequestsMinimumAbsolute, 0.0);
assertEquals(expectedLoadBalancerRequestsMaximumFraction, httpShardHandlerFactory.permittedLoadBalancerRequestsMaximumFraction, 0.0);
// create a dummy request and dummy url list
final QueryRequest queryRequest = null;
final List<String> urls = new ArrayList<>();
for (int ii=0; ii<10; ++ii) {
urls.add(null);
}
// create LBHttpSolrClient request
final LBHttpSolrClient.Req req = httpShardHandlerFactory.newLBHttpSolrClientReq(queryRequest, urls);
// actual vs. expected test
final int actualNumServersToTry = req.getNumServersToTry().intValue();
int expectedNumServersToTry = (int)Math.floor(urls.size() * expectedLoadBalancerRequestsMaximumFraction);
if (expectedNumServersToTry < expectedLoadBalancerRequestsMinimumAbsolute) {
expectedNumServersToTry = expectedLoadBalancerRequestsMinimumAbsolute;
}
assertEquals("wrong numServersToTry for"
+ " urls.size="+urls.size()
+ " expectedLoadBalancerRequestsMinimumAbsolute="+expectedLoadBalancerRequestsMinimumAbsolute
+ " expectedLoadBalancerRequestsMaximumFraction="+expectedLoadBalancerRequestsMaximumFraction,
expectedNumServersToTry,
actualNumServersToTry);
} finally {
if (factory != null) factory.close();
if (cc != null) cc.shutdown();
}
}
}

View File

@ -185,11 +185,17 @@ public class LBHttpSolrClient extends SolrClient {
protected SolrRequest request;
protected List<String> servers;
protected int numDeadServersToTry;
private final Integer numServersToTry;
public Req(SolrRequest request, List<String> servers) {
this(request, servers, null);
}
public Req(SolrRequest request, List<String> servers, Integer numServersToTry) {
this.request = request;
this.servers = servers;
this.numDeadServersToTry = servers.size();
this.numServersToTry = numServersToTry;
}
public SolrRequest getRequest() {
@ -209,6 +215,10 @@ public class LBHttpSolrClient extends SolrClient {
public void setNumDeadServersToTry(int numDeadServersToTry) {
this.numDeadServersToTry = numDeadServersToTry;
}
public Integer getNumServersToTry() {
return numServersToTry;
}
}
public static class Rsp {
@ -360,6 +370,9 @@ public class LBHttpSolrClient extends SolrClient {
boolean isNonRetryable = req.request instanceof IsUpdateRequest || ADMIN_PATHS.contains(req.request.getPath());
List<ServerWrapper> skipped = null;
final Integer numServersToTry = req.getNumServersToTry();
int numServersTried = 0;
boolean timeAllowedExceeded = false;
long timeAllowedNano = getTimeAllowedInNanos(req.getRequest());
long timeOutTime = System.nanoTime() + timeAllowedNano;
@ -387,8 +400,14 @@ public class LBHttpSolrClient extends SolrClient {
}
try {
MDC.put("LBHttpSolrClient.url", serverStr);
if (numServersToTry != null && numServersTried > numServersToTry.intValue()) {
break;
}
HttpSolrClient client = makeSolrClient(serverStr);
++numServersTried;
ex = doRequest(client, req, rsp, isNonRetryable, false, null);
if (ex == null) {
return rsp; // SUCCESS
@ -405,8 +424,13 @@ public class LBHttpSolrClient extends SolrClient {
break;
}
if (numServersToTry != null && numServersTried > numServersToTry.intValue()) {
break;
}
try {
MDC.put("LBHttpSolrClient.url", wrapper.client.getBaseURL());
++numServersTried;
ex = doRequest(wrapper.client, req, rsp, isNonRetryable, true, wrapper.getKey());
if (ex == null) {
return rsp; // SUCCESS
@ -422,7 +446,13 @@ public class LBHttpSolrClient extends SolrClient {
if (timeAllowedExceeded) {
solrServerExceptionMessage = "Time allowed to handle this request exceeded";
} else {
solrServerExceptionMessage = "No live SolrServers available to handle this request";
if (numServersToTry != null && numServersTried > numServersToTry.intValue()) {
solrServerExceptionMessage = "No live SolrServers available to handle this request:"
+ " numServersTried="+numServersTried
+ " numServersToTry="+numServersToTry.intValue();
} else {
solrServerExceptionMessage = "No live SolrServers available to handle this request";
}
}
if (ex == null) {
throw new SolrServerException(solrServerExceptionMessage);
@ -594,10 +624,16 @@ public class LBHttpSolrClient extends SolrClient {
@Override
public NamedList<Object> request(final SolrRequest request, String collection)
throws SolrServerException, IOException {
return request(request, collection, null);
}
public NamedList<Object> request(final SolrRequest request, String collection,
final Integer numServersToTry) throws SolrServerException, IOException {
Exception ex = null;
ServerWrapper[] serverList = aliveServerList;
int maxTries = serverList.length;
final int maxTries = (numServersToTry == null ? serverList.length : numServersToTry.intValue());
int numServersTried = 0;
Map<String,ServerWrapper> justFailed = null;
boolean timeAllowedExceeded = false;
@ -612,6 +648,7 @@ public class LBHttpSolrClient extends SolrClient {
ServerWrapper wrapper = serverList[count % serverList.length];
try {
++numServersTried;
return wrapper.client.request(request, collection);
} catch (SolrException e) {
// Server is alive but the request was malformed or invalid
@ -638,6 +675,7 @@ public class LBHttpSolrClient extends SolrClient {
if (wrapper.standard==false || justFailed!=null && justFailed.containsKey(wrapper.getKey())) continue;
try {
++numServersTried;
NamedList<Object> rsp = wrapper.client.request(request, collection);
// remove from zombie list *before* adding to alive to avoid a race that could lose a server
zombieServers.remove(wrapper.getKey());
@ -663,7 +701,13 @@ public class LBHttpSolrClient extends SolrClient {
if (timeAllowedExceeded) {
solrServerExceptionMessage = "Time allowed to handle this request exceeded";
} else {
solrServerExceptionMessage = "No live SolrServers available to handle this request";
if (numServersToTry != null && numServersTried > numServersToTry.intValue()) {
solrServerExceptionMessage = "No live SolrServers available to handle this request:"
+ " numServersTried="+numServersTried
+ " numServersToTry="+numServersToTry.intValue();
} else {
solrServerExceptionMessage = "No live SolrServers available to handle this request";
}
}
if (ex == null) {
throw new SolrServerException(solrServerExceptionMessage);