You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ds...@apache.org on 2024/03/21 03:00:46 UTC

(solr) branch main updated: SOLR-14892: shards.info with shards.tolerant can yield an empty key (#286)

This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new b04c9202bf1 SOLR-14892: shards.info with shards.tolerant can yield an empty key (#286)
b04c9202bf1 is described below

commit b04c9202bf19e477482c11170e4ecf70fc93608e
Author: mariemat <ma...@gmail.com>
AuthorDate: Thu Mar 21 04:00:40 2024 +0100

    SOLR-14892: shards.info with shards.tolerant can yield an empty key (#286)
    
    Improvement / work-around for a bug.
    
    Co-authored-by: Mathieu Marie <mm...@salesforce.com>
---
 solr/CHANGES.txt                                   |  3 +
 .../solr/handler/component/QueryComponent.java     | 14 +++-
 .../apache/solr/cloud/TestShardsInfoResponse.java  | 89 ++++++++++++++++++++++
 3 files changed, 102 insertions(+), 4 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 76330b4473f..0cd8f12ae70 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -145,6 +145,9 @@ Bug Fixes
 * SOLR-17198: AffinityPlacementFactory can fail if Shard leadership changes occur while it is collecting metrics.
   (Paul McArthur)
 
+* SOLR-14892: Queries with shards.info and shards.tolerant can yield multiple null keys in place of shard names
+  (Mathieu Marie, David Smiley)
+
 Dependency Upgrades
 ---------------------
 
diff --git a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
index d30d62e1c04..8b34422ddfe 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
@@ -946,6 +946,7 @@ public class QueryComponent extends SearchComponent {
     Float maxScore = null;
     boolean thereArePartialResults = false;
     Boolean segmentTerminatedEarly = null;
+    int failedShardCount = 0;
     for (ShardResponse srsp : sreq.responses) {
       SolrDocumentList docs = null;
       NamedList<?> responseHeader = null;
@@ -956,7 +957,7 @@ public class QueryComponent extends SearchComponent {
         if (srsp.getException() != null) {
           Throwable t = srsp.getException();
           if (t instanceof SolrServerException) {
-            t = ((SolrServerException) t).getCause();
+            t = t.getCause();
           }
           nl.add("error", t.toString());
           if (!rb.req.getCore().getCoreContainer().hideStackTrace()) {
@@ -964,7 +965,7 @@ public class QueryComponent extends SearchComponent {
             t.printStackTrace(new PrintWriter(trace));
             nl.add("trace", trace.toString());
           }
-          if (srsp.getShardAddress() != null) {
+          if (!StrUtils.isNullOrEmpty(srsp.getShardAddress())) {
             nl.add("shardAddress", srsp.getShardAddress());
           }
         } else {
@@ -994,8 +995,13 @@ public class QueryComponent extends SearchComponent {
         if (srsp.getSolrResponse() != null) {
           nl.add("time", srsp.getSolrResponse().getElapsedTime());
         }
-
-        shardInfo.add(srsp.getShard(), nl);
+        // This ought to be better, but at least this ensures no duplicate keys in JSON result
+        String shard = srsp.getShard();
+        if (StrUtils.isNullOrEmpty(shard)) {
+          failedShardCount += 1;
+          shard = "unknown_shard_" + failedShardCount;
+        }
+        shardInfo.add(shard, nl);
       }
       // now that we've added the shard info, let's only proceed if we have no error.
       if (srsp.getException() != null) {
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestShardsInfoResponse.java b/solr/core/src/test/org/apache/solr/cloud/TestShardsInfoResponse.java
new file mode 100644
index 00000000000..b318d23d21a
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/TestShardsInfoResponse.java
@@ -0,0 +1,89 @@
+/*
+ * 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.cloud;
+
+import static org.hamcrest.core.IsIterableContaining.hasItems;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.common.util.SimpleOrderedMap;
+import org.hamcrest.MatcherAssert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test which asserts that shards.info=true works even if several shards are down It must return one
+ * unique key per shard. See SOLR-14892
+ */
+public class TestShardsInfoResponse extends SolrCloudTestCase {
+
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    configureCluster(3).addConfig("cloud-minimal", configset("cloud-minimal")).configure();
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void searchingWithShardsInfoMustNotReturnEmptyOrDuplicateKeys() throws Exception {
+
+    CollectionAdminRequest.createCollection("collection", "cloud-minimal", 3, 1)
+        .process(cluster.getSolrClient());
+
+    UpdateRequest update = new UpdateRequest();
+    for (int i = 0; i < 100; i++) {
+      update.add("id", Integer.toString(i));
+    }
+    update.commit(cluster.getSolrClient(), "collection");
+
+    // Stops 2 shards
+    for (int i = 0; i < 2; i++) {
+      cluster.waitForJettyToStop(cluster.stopJettySolrRunner(i));
+    }
+
+    QueryResponse response =
+        cluster
+            .getSolrClient()
+            .query(
+                "collection",
+                new SolrQuery("*:*")
+                    .setRows(1)
+                    .setParam(ShardParams.SHARDS_TOLERANT, true)
+                    .setParam(ShardParams.SHARDS_INFO, true));
+    assertEquals(0, response.getStatus());
+    assertTrue(response.getResults().getNumFound() > 0);
+
+    SimpleOrderedMap<Object> shardsInfo =
+        (SimpleOrderedMap<Object>) response.getResponse().get("shards.info");
+    // We verify that there are no duplicate keys in case of 2 shards in error
+    assertEquals(3, shardsInfo.size());
+
+    Collection<String> keys = new ArrayList<>();
+    keys.add(shardsInfo.getName(0));
+    keys.add(shardsInfo.getName(1));
+    keys.add(shardsInfo.getName(2));
+
+    // The names of the shards in error are generated as unknown_shard_1 and unknown_shard_2 because
+    // we could not get the real shard names.
+    MatcherAssert.assertThat(
+        (Iterable<String>) keys, hasItems("unknown_shard_1", "unknown_shard_2"));
+  }
+}