Skip to content

Commit 88fa620

Browse files
SOLR-14985: CloudSolrClient with Solr URLs wasn't caching state (#2571)
Solrj CloudSolrClient with Solr URLs had serious performance regressions (since the beginning?) in which its collection state cache was not being used, resulting in many extra requests to Solr for cluster information. (Aparna Suresh, shalin, David Smiley) Co-authored-by: Shalin Shekhar Mangar <[email protected]>
1 parent 70a7bd7 commit 88fa620

File tree

5 files changed

+88
-17
lines changed

5 files changed

+88
-17
lines changed

solr/CHANGES.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ Improvements
111111

112112
Optimizations
113113
---------------------
114-
(No changes)
114+
* SOLR-14985: Solrj CloudSolrClient with Solr URLs had serious performance regressions (since the
115+
beginning?) in which its collection state cache was not being used, resulting in many extra
116+
requests to Solr for cluster information. (Aparna Suresh, shalin, David Smiley)
115117

116118
Bug Fixes
117119
---------------------

solr/solrj/src/java/org/apache/solr/client/solrj/cloud/DelegatingClusterStateProvider.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ public String resolveSimpleAlias(String alias) throws IllegalArgumentException {
7878
}
7979
}
8080

81+
@Override
82+
public Object getClusterProperty(String propertyName) {
83+
if (delegate != null) {
84+
return delegate.getClusterProperty(propertyName);
85+
} else {
86+
return null;
87+
}
88+
}
89+
8190
@Override
8291
public ClusterState getClusterState() {
8392
if (delegate != null) {

solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,9 @@ private NamedList<Object> directUpdate(AbstractUpdateRequest request, String col
362362

363363
// Check to see if the collection is an alias. Updates to multi-collection aliases are ok as
364364
// long as they are routed aliases
365-
List<String> aliasedCollections = getClusterStateProvider().resolveAlias(collection);
366-
if (getClusterStateProvider().isRoutedAlias(collection) || aliasedCollections.size() == 1) {
365+
List<String> aliasedCollections =
366+
new ArrayList<>(resolveAliases(Collections.singletonList(collection)));
367+
if (aliasedCollections.size() == 1 || getClusterStateProvider().isRoutedAlias(collection)) {
367368
collection = aliasedCollections.get(0); // pick 1st (consistent with HttpSolrCall behavior)
368369
} else {
369370
throw new SolrException(
@@ -1149,7 +1150,7 @@ private Set<String> resolveAliases(List<String> inputCollections) {
11491150
}
11501151
LinkedHashSet<String> uniqueNames = new LinkedHashSet<>(); // consistent ordering
11511152
for (String collectionName : inputCollections) {
1152-
if (getClusterStateProvider().getState(collectionName) == null) {
1153+
if (getDocCollection(collectionName, -1) == null) {
11531154
// perhaps it's an alias
11541155
uniqueNames.addAll(getClusterStateProvider().resolveAlias(collectionName));
11551156
} else {
@@ -1208,15 +1209,6 @@ protected DocCollection getDocCollection(String collection, Integer expectedVers
12081209
if (expectedVersion <= col.getZNodeVersion() && !cacheEntry.shouldRetry()) return col;
12091210
}
12101211

1211-
ClusterState.CollectionRef ref = getCollectionRef(collection);
1212-
if (ref == null) {
1213-
// no such collection exists
1214-
return null;
1215-
}
1216-
if (!ref.isLazilyLoaded()) {
1217-
// it is readily available just return it
1218-
return ref.get();
1219-
}
12201212
Object[] locks = this.locks;
12211213
int lockId =
12221214
Math.abs(Hash.murmurhash3_x86_32(collection, 0, collection.length(), 0) % locks.length);
@@ -1228,6 +1220,11 @@ protected DocCollection getDocCollection(String collection, Integer expectedVers
12281220
if (col != null) {
12291221
if (expectedVersion <= col.getZNodeVersion() && !cacheEntry.shouldRetry()) return col;
12301222
}
1223+
ClusterState.CollectionRef ref = getCollectionRef(collection);
1224+
if (ref == null) {
1225+
// no such collection exists
1226+
return null;
1227+
}
12311228
// We are going to fetch a new version
12321229
// we MUST try to get a new version
12331230
DocCollection fetchedCol = ref.get(); // this is a call to ZK

solr/solrj/src/java/org/apache/solr/client/solrj/impl/ClusterStateProvider.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,13 @@ default DocCollection getCollection(String name) throws IOException {
108108
/** Obtain a cluster property, or the default value if it doesn't exist. */
109109
default <T> T getClusterProperty(String key, T defaultValue) {
110110
@SuppressWarnings({"unchecked"})
111-
T value = (T) getClusterProperties().get(key);
111+
T value = (T) getClusterProperty(key);
112112
if (value == null) return defaultValue;
113113
return value;
114114
}
115115

116116
/** Obtain a cluster property, or null if it doesn't exist. */
117-
default Object getClusterProperty(String propertyName) {
118-
return getClusterProperties().get(propertyName);
119-
}
117+
Object getClusterProperty(String propertyName);
120118

121119
/** Get the collection-specific policy */
122120
String getPolicyNameByCollection(String coll);

solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudHttp2SolrClientTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Set;
3636
import java.util.concurrent.TimeUnit;
3737
import java.util.concurrent.TimeoutException;
38+
import java.util.regex.Pattern;
3839
import org.apache.http.impl.client.CloseableHttpClient;
3940
import org.apache.lucene.tests.util.TestUtil;
4041
import org.apache.solr.client.solrj.SolrClient;
@@ -70,6 +71,9 @@
7071
import org.apache.solr.handler.admin.CollectionsHandler;
7172
import org.apache.solr.handler.admin.ConfigSetsHandler;
7273
import org.apache.solr.handler.admin.CoreAdminHandler;
74+
import org.apache.solr.servlet.HttpSolrCall;
75+
import org.apache.solr.util.LogLevel;
76+
import org.apache.solr.util.LogListener;
7377
import org.junit.AfterClass;
7478
import org.junit.BeforeClass;
7579
import org.junit.Test;
@@ -89,6 +93,15 @@ public class CloudHttp2SolrClientTest extends SolrCloudTestCase {
8993
private static CloudSolrClient httpBasedCloudSolrClient = null;
9094
private static CloudSolrClient zkBasedCloudSolrClient = null;
9195

96+
private static final Pattern PATTERN_WITH_COLLECTION =
97+
Pattern.compile(
98+
"path=/admin/collections.*params=\\{[^}]*action=CLUSTERSTATUS"
99+
+ "[^}]*collection=[^&}]+[^}]*\\}");
100+
private static final Pattern PATTERN_WITHOUT_COLLECTION =
101+
Pattern.compile(
102+
"path=/admin/collections.*params=\\{[^}]*action=CLUSTERSTATUS"
103+
+ "(?![^}]*collection=)[^}]*\\}");
104+
92105
@BeforeClass
93106
public static void setupCluster() throws Exception {
94107
System.setProperty("metricsEnabled", "true");
@@ -114,6 +127,7 @@ public static void setupCluster() throws Exception {
114127

115128
@AfterClass
116129
public static void tearDownAfterClass() throws Exception {
130+
117131
if (httpBasedCloudSolrClient != null) {
118132
try {
119133
httpBasedCloudSolrClient.close();
@@ -246,6 +260,57 @@ public void testAliasHandling() throws Exception {
246260
2, client.query(null, paramsWithMixedCollectionAndAlias).getResults().getNumFound());
247261
}
248262

263+
@Test
264+
@LogLevel("org.apache.solr.servlet.HttpSolrCall=DEBUG")
265+
public void testHttpCspPerf() throws Exception {
266+
267+
String collectionName = "HTTPCSPTEST";
268+
CollectionAdminRequest.createCollection(collectionName, "conf", 2, 1)
269+
.process(cluster.getSolrClient());
270+
cluster.waitForActiveCollection(collectionName, 2, 2);
271+
272+
try (LogListener entireClusterStateLogs =
273+
LogListener.info(HttpSolrCall.class).regex(PATTERN_WITHOUT_COLLECTION);
274+
LogListener collectionClusterStateLogs =
275+
LogListener.info(HttpSolrCall.class).regex(PATTERN_WITH_COLLECTION);
276+
LogListener adminRequestLogs = LogListener.info(HttpSolrCall.class).substring("[admin]");
277+
CloudSolrClient solrClient = createHttpCSPBasedCloudSolrClient(); ) {
278+
SolrInputDocument doc = new SolrInputDocument("id", "1", "title_s", "my doc");
279+
solrClient.add(collectionName, doc);
280+
solrClient.commit(collectionName);
281+
for (int i = 0; i < 3; i++) {
282+
assertEquals(
283+
1, solrClient.query(collectionName, params("q", "*:*")).getResults().getNumFound());
284+
}
285+
286+
// 1 call to fetch entire cluster state via BaseHttpCSP.fetchLiveNodes()
287+
// 1 call to fetch CLUSTERSTATUS for collection via getDocCollection() (first collection
288+
// lookup)
289+
assertLogCount(adminRequestLogs, 2);
290+
// 1 call to fetch CLUSTERSTATUS for collection via getDocCollection() (first collection
291+
// lookup)
292+
assertLogCount(collectionClusterStateLogs, 1);
293+
// 1 call to fetch entire cluster state from HttpCSP.fetchLiveNodes()
294+
assertLogCount(entireClusterStateLogs, 1);
295+
}
296+
}
297+
298+
private CloudSolrClient createHttpCSPBasedCloudSolrClient() {
299+
final List<String> solrUrls = new ArrayList<>();
300+
solrUrls.add(cluster.getJettySolrRunner(0).getBaseUrl().toString());
301+
return new CloudHttp2SolrClient.Builder(solrUrls).build();
302+
}
303+
304+
private void assertLogCount(LogListener logListener, int expectedCount) {
305+
int logCount = logListener.getCount();
306+
assertEquals(expectedCount, logCount);
307+
if (logCount > 0) {
308+
for (int i = 0; i < logCount; i++) {
309+
logListener.pollMessage();
310+
}
311+
}
312+
}
313+
249314
@Test
250315
public void testRouting() throws Exception {
251316
CollectionAdminRequest.createCollection("routing_collection", "conf", 2, 1)

0 commit comments

Comments
 (0)