Skip to content

Commit b04c920

Browse files
mariematMathieu Marie
andauthored
SOLR-14892: shards.info with shards.tolerant can yield an empty key (#286)
Improvement / work-around for a bug. Co-authored-by: Mathieu Marie <[email protected]>
1 parent 946c23d commit b04c920

File tree

3 files changed

+102
-4
lines changed

3 files changed

+102
-4
lines changed

solr/CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ Bug Fixes
145145
* SOLR-17198: AffinityPlacementFactory can fail if Shard leadership changes occur while it is collecting metrics.
146146
(Paul McArthur)
147147

148+
* SOLR-14892: Queries with shards.info and shards.tolerant can yield multiple null keys in place of shard names
149+
(Mathieu Marie, David Smiley)
150+
148151
Dependency Upgrades
149152
---------------------
150153

solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,7 @@ protected void mergeIds(ResponseBuilder rb, ShardRequest sreq) {
946946
Float maxScore = null;
947947
boolean thereArePartialResults = false;
948948
Boolean segmentTerminatedEarly = null;
949+
int failedShardCount = 0;
949950
for (ShardResponse srsp : sreq.responses) {
950951
SolrDocumentList docs = null;
951952
NamedList<?> responseHeader = null;
@@ -956,15 +957,15 @@ protected void mergeIds(ResponseBuilder rb, ShardRequest sreq) {
956957
if (srsp.getException() != null) {
957958
Throwable t = srsp.getException();
958959
if (t instanceof SolrServerException) {
959-
t = ((SolrServerException) t).getCause();
960+
t = t.getCause();
960961
}
961962
nl.add("error", t.toString());
962963
if (!rb.req.getCore().getCoreContainer().hideStackTrace()) {
963964
StringWriter trace = new StringWriter();
964965
t.printStackTrace(new PrintWriter(trace));
965966
nl.add("trace", trace.toString());
966967
}
967-
if (srsp.getShardAddress() != null) {
968+
if (!StrUtils.isNullOrEmpty(srsp.getShardAddress())) {
968969
nl.add("shardAddress", srsp.getShardAddress());
969970
}
970971
} else {
@@ -994,8 +995,13 @@ protected void mergeIds(ResponseBuilder rb, ShardRequest sreq) {
994995
if (srsp.getSolrResponse() != null) {
995996
nl.add("time", srsp.getSolrResponse().getElapsedTime());
996997
}
997-
998-
shardInfo.add(srsp.getShard(), nl);
998+
// This ought to be better, but at least this ensures no duplicate keys in JSON result
999+
String shard = srsp.getShard();
1000+
if (StrUtils.isNullOrEmpty(shard)) {
1001+
failedShardCount += 1;
1002+
shard = "unknown_shard_" + failedShardCount;
1003+
}
1004+
shardInfo.add(shard, nl);
9991005
}
10001006
// now that we've added the shard info, let's only proceed if we have no error.
10011007
if (srsp.getException() != null) {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.solr.cloud;
18+
19+
import static org.hamcrest.core.IsIterableContaining.hasItems;
20+
21+
import java.util.ArrayList;
22+
import java.util.Collection;
23+
import org.apache.solr.client.solrj.SolrQuery;
24+
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
25+
import org.apache.solr.client.solrj.request.UpdateRequest;
26+
import org.apache.solr.client.solrj.response.QueryResponse;
27+
import org.apache.solr.common.params.ShardParams;
28+
import org.apache.solr.common.util.SimpleOrderedMap;
29+
import org.hamcrest.MatcherAssert;
30+
import org.junit.BeforeClass;
31+
import org.junit.Test;
32+
33+
/**
34+
* Test which asserts that shards.info=true works even if several shards are down It must return one
35+
* unique key per shard. See SOLR-14892
36+
*/
37+
public class TestShardsInfoResponse extends SolrCloudTestCase {
38+
39+
@BeforeClass
40+
public static void setupCluster() throws Exception {
41+
configureCluster(3).addConfig("cloud-minimal", configset("cloud-minimal")).configure();
42+
}
43+
44+
@Test
45+
@SuppressWarnings("unchecked")
46+
public void searchingWithShardsInfoMustNotReturnEmptyOrDuplicateKeys() throws Exception {
47+
48+
CollectionAdminRequest.createCollection("collection", "cloud-minimal", 3, 1)
49+
.process(cluster.getSolrClient());
50+
51+
UpdateRequest update = new UpdateRequest();
52+
for (int i = 0; i < 100; i++) {
53+
update.add("id", Integer.toString(i));
54+
}
55+
update.commit(cluster.getSolrClient(), "collection");
56+
57+
// Stops 2 shards
58+
for (int i = 0; i < 2; i++) {
59+
cluster.waitForJettyToStop(cluster.stopJettySolrRunner(i));
60+
}
61+
62+
QueryResponse response =
63+
cluster
64+
.getSolrClient()
65+
.query(
66+
"collection",
67+
new SolrQuery("*:*")
68+
.setRows(1)
69+
.setParam(ShardParams.SHARDS_TOLERANT, true)
70+
.setParam(ShardParams.SHARDS_INFO, true));
71+
assertEquals(0, response.getStatus());
72+
assertTrue(response.getResults().getNumFound() > 0);
73+
74+
SimpleOrderedMap<Object> shardsInfo =
75+
(SimpleOrderedMap<Object>) response.getResponse().get("shards.info");
76+
// We verify that there are no duplicate keys in case of 2 shards in error
77+
assertEquals(3, shardsInfo.size());
78+
79+
Collection<String> keys = new ArrayList<>();
80+
keys.add(shardsInfo.getName(0));
81+
keys.add(shardsInfo.getName(1));
82+
keys.add(shardsInfo.getName(2));
83+
84+
// The names of the shards in error are generated as unknown_shard_1 and unknown_shard_2 because
85+
// we could not get the real shard names.
86+
MatcherAssert.assertThat(
87+
(Iterable<String>) keys, hasItems("unknown_shard_1", "unknown_shard_2"));
88+
}
89+
}

0 commit comments

Comments
 (0)