Skip to content

Commit 5c399dd

Browse files
SOLR-17153: CloudSolrClient should not throw "Collection not found" with an out-dated ClusterState (#2363)
ZkClientClusterStateProvider needed to double-check a collection truly doesn't exist. HttpClusterStateProvider is already correct. HttpSolrCall on the server side was hardened similarly. This could happen for a highly burdened cluster / zookeeper.
1 parent 2eb9b21 commit 5c399dd

File tree

5 files changed

+65
-56
lines changed

5 files changed

+65
-56
lines changed

solr/CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ Optimizations
135135

136136
Bug Fixes
137137
---------------------
138+
* SOLR-17153: CloudSolrClient could fail a request immediately following a collection creation. Required double-checking the collection doesn’t exist. (Aparna Suresh via David Smiley)
139+
138140
* SOLR-17152: Better alignment of Admin UI graph (janhoy)
139141

140142
* SOLR-17148: Fixing Config API overlay property enabling or disabling the cache (Sanjay Dutt, hossman, Eric Pugh)

solr/core/src/java/org/apache/solr/api/V2HttpCall.java

Lines changed: 14 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,12 @@
3535
import java.util.Locale;
3636
import java.util.Map;
3737
import java.util.Set;
38-
import java.util.function.Supplier;
3938
import javax.servlet.http.HttpServletRequest;
4039
import javax.servlet.http.HttpServletResponse;
4140
import net.jcip.annotations.ThreadSafe;
4241
import org.apache.solr.client.solrj.SolrRequest;
4342
import org.apache.solr.common.SolrException;
4443
import org.apache.solr.common.cloud.DocCollection;
45-
import org.apache.solr.common.cloud.ZkStateReader;
4644
import org.apache.solr.common.params.CommonParams;
4745
import org.apache.solr.common.util.JsonSchemaValidator;
4846
import org.apache.solr.common.util.PathTrie;
@@ -140,8 +138,20 @@ public void call(SolrQueryRequest req, SolrQueryResponse rsp) {
140138
if (pathSegments.size() > 1 && ("c".equals(prefix) || "collections".equals(prefix))) {
141139
origCorename = pathSegments.get(1);
142140

143-
DocCollection collection =
144-
resolveDocCollection(queryParams.get(COLLECTION_PROP, origCorename));
141+
String collectionStr = queryParams.get(COLLECTION_PROP, origCorename);
142+
collectionsList =
143+
resolveCollectionListOrAlias(collectionStr); // &collection= takes precedence
144+
if (collectionsList.size() > 1) {
145+
throw new SolrException(
146+
SolrException.ErrorCode.BAD_REQUEST,
147+
"Request must be sent to a single collection "
148+
+ "or an alias that points to a single collection,"
149+
+ " but '"
150+
+ collectionStr
151+
+ "' resolves to "
152+
+ this.collectionsList);
153+
}
154+
DocCollection collection = resolveDocCollection(collectionsList);
145155
if (collection == null) {
146156
if (!path.endsWith(CommonParams.INTROSPECT)) {
147157
throw new SolrException(
@@ -218,54 +228,6 @@ protected void parseRequest() throws Exception {
218228
if (solrReq == null) solrReq = parser.parse(core, path, req);
219229
}
220230

221-
/**
222-
* Lookup the collection from the collection string (maybe comma delimited). Also sets {@link
223-
* #collectionsList} by side-effect. if {@code secondTry} is false then we'll potentially
224-
* recursively try this all one more time while ensuring the alias and collection info is sync'ed
225-
* from ZK.
226-
*/
227-
protected DocCollection resolveDocCollection(String collectionStr) {
228-
if (!cores.isZooKeeperAware()) {
229-
throw new SolrException(
230-
SolrException.ErrorCode.BAD_REQUEST, "Solr not running in cloud mode ");
231-
}
232-
ZkStateReader zkStateReader = cores.getZkController().getZkStateReader();
233-
234-
Supplier<DocCollection> logic =
235-
() -> {
236-
this.collectionsList = resolveCollectionListOrAlias(collectionStr); // side-effect
237-
if (collectionsList.size() > 1) {
238-
throw new SolrException(
239-
SolrException.ErrorCode.BAD_REQUEST,
240-
"Request must be sent to a single collection "
241-
+ "or an alias that points to a single collection,"
242-
+ " but '"
243-
+ collectionStr
244-
+ "' resolves to "
245-
+ this.collectionsList);
246-
}
247-
String collectionName = collectionsList.get(0); // first
248-
// TODO an option to choose another collection in the list if can't find a local replica
249-
// of the first?
250-
251-
return zkStateReader.getClusterState().getCollectionOrNull(collectionName);
252-
};
253-
254-
DocCollection docCollection = logic.get();
255-
if (docCollection != null) {
256-
return docCollection;
257-
}
258-
// ensure our view is up to date before trying again
259-
try {
260-
zkStateReader.aliasesManager.update();
261-
zkStateReader.forceUpdateCollection(collectionsList.get(0));
262-
} catch (Exception e) {
263-
log.error("Error trying to update state while resolving collection.", e);
264-
// don't propagate exception on purpose
265-
}
266-
return logic.get();
267-
}
268-
269231
public static Api getApiInfo(
270232
PluginBag<SolrRequestHandler> requestHandlers,
271233
String path,

solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.Random;
5454
import java.util.Set;
5555
import java.util.concurrent.TimeUnit;
56+
import java.util.function.Supplier;
5657
import javax.servlet.http.HttpServletRequest;
5758
import javax.servlet.http.HttpServletResponse;
5859
import net.jcip.annotations.ThreadSafe;
@@ -281,6 +282,8 @@ protected void init() throws Exception {
281282
queryParams.get(COLLECTION_PROP, def)); // &collection= takes precedence
282283

283284
if (core == null) {
285+
// force update collection only if local clusterstate is outdated
286+
resolveDocCollection(collectionsList);
284287
// lookup core from collection, or route away if need to
285288
// route to 1st
286289
String collectionName = collectionsList.isEmpty() ? null : collectionsList.get(0);
@@ -345,6 +348,37 @@ protected void init() throws Exception {
345348
action = PASSTHROUGH;
346349
}
347350

351+
/**
352+
* Lookup the collection from the collection string (maybe comma delimited). Also sets {@link
353+
* #collectionsList} by side-effect. if {@code secondTry} is false then we'll potentially
354+
* recursively try this all one more time while ensuring the alias and collection info is sync'ed
355+
* from ZK.
356+
*/
357+
protected DocCollection resolveDocCollection(List<String> collectionsList) {
358+
if (!cores.isZooKeeperAware()) {
359+
throw new SolrException(
360+
SolrException.ErrorCode.BAD_REQUEST, "Solr not running in cloud mode ");
361+
}
362+
ZkStateReader zkStateReader = cores.getZkController().getZkStateReader();
363+
String collectionName = collectionsList.get(0);
364+
Supplier<DocCollection> logic =
365+
() -> zkStateReader.getClusterState().getCollectionOrNull(collectionName);
366+
367+
DocCollection docCollection = logic.get();
368+
if (docCollection != null) {
369+
return docCollection;
370+
}
371+
// ensure our view is up to date before trying again
372+
try {
373+
zkStateReader.aliasesManager.update();
374+
zkStateReader.forceUpdateCollection(collectionName);
375+
} catch (Exception e) {
376+
log.error("Error trying to update state while resolving collection.", e);
377+
// don't propagate exception on purpose
378+
}
379+
return logic.get();
380+
}
381+
348382
protected void autoCreateSystemColl(String corename) throws Exception {
349383
if (core == null
350384
&& SYSTEM_COLL.equals(corename)

solr/solrj-zookeeper/src/java/org/apache/solr/client/solrj/impl/ZkClientClusterStateProvider.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,22 @@ public static ClusterState createFromJsonSupportingLegacyConfigName(
144144
@Override
145145
public ClusterState.CollectionRef getState(String collection) {
146146
ClusterState clusterState = getZkStateReader().getClusterState();
147-
if (clusterState != null) {
148-
return clusterState.getCollectionRef(collection);
149-
} else {
147+
if (clusterState == null) {
150148
return null;
151149
}
150+
151+
ClusterState.CollectionRef collectionRef = clusterState.getCollectionRef(collection);
152+
if (collectionRef == null) {
153+
// force update collection
154+
try {
155+
getZkStateReader().forceUpdateCollection(collection);
156+
return getZkStateReader().getClusterState().getCollectionRef(collection);
157+
} catch (KeeperException | InterruptedException e) {
158+
return null;
159+
}
160+
} else {
161+
return collectionRef;
162+
}
152163
}
153164

154165
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ static ClusterStateProvider newZkClusterStateProvider(
5353
/**
5454
* Obtain the state of the collection (cluster status).
5555
*
56-
* @return the collection state, or null is collection doesn't exist
56+
* @return the collection state, or null only if collection doesn't exist
5757
*/
5858
ClusterState.CollectionRef getState(String collection);
5959

0 commit comments

Comments
 (0)