mirror of https://github.com/apache/lucene.git
SOLR-10479: Adds support for HttpShardHandlerFactory.loadBalancerRequests(MinimumAbsolute|MaximumFraction) configuration. (Ramsey Haddad, Daniel Collins, Christine Poerschke)
This commit is contained in:
parent
0b47126e5c
commit
2bb6e2caca
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
@ -421,9 +445,15 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
final String solrServerExceptionMessage;
|
||||
if (timeAllowedExceeded) {
|
||||
solrServerExceptionMessage = "Time allowed to handle this request exceeded";
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
|
@ -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());
|
||||
|
@ -662,9 +700,15 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
final String solrServerExceptionMessage;
|
||||
if (timeAllowedExceeded) {
|
||||
solrServerExceptionMessage = "Time allowed to handle this request exceeded";
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue