diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java b/solr/core/src/java/org/apache/solr/cli/StatusTool.java index 2233614e593..fa6c2cbf2bd 100644 --- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java +++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java @@ -353,8 +353,7 @@ protected Map getCloudStatus(SolrClient solrClient, String zkHos cloudStatus.put("liveNodes", String.valueOf(liveNodes.size())); // TODO get this as a metric from the metrics API instead, or something else. - Map collections = - (Map) json.findRecursive("cluster", "collections"); + var collections = (NamedList) json.findRecursive("cluster", "collections"); cloudStatus.put("collections", String.valueOf(collections.size())); return cloudStatus; diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java index 294a4c77715..7a8ecf9c850 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java @@ -34,6 +34,7 @@ import org.apache.solr.common.cloud.PerReplicaStates; import org.apache.solr.common.cloud.Replica; import org.apache.solr.common.cloud.ZkStateReader; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.ShardParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -205,21 +206,35 @@ private void fetchClusterStatusForCollOrAlias( } } - MapWriter collectionPropsWriter = - ew -> { - collectionStream.forEach( - (collectionState) -> { - ew.putNoEx( - collectionState.getName(), - buildResponseForCollection( - collectionState, - collectionVsAliases, - routeKey, - liveNodes, - requestedShards)); - }); - }; - clusterStatus.add("collections", collectionPropsWriter); + // Because of back-compat for SolrJ, create the whole response into a NamedList + // Otherwise stream with MapWriter to save memory + if (CommonParams.JAVABIN.equals(solrParams.get(CommonParams.WT))) { + NamedList collectionProps = new SimpleOrderedMap<>(); + collectionStream.forEach( + collectionState -> { + collectionProps.add( + collectionState.getName(), + buildResponseForCollection( + collectionState, collectionVsAliases, routeKey, liveNodes, requestedShards)); + }); + clusterStatus.add("collections", collectionProps); + } else { + MapWriter collectionPropsWriter = + ew -> { + collectionStream.forEach( + (collectionState) -> { + ew.putNoEx( + collectionState.getName(), + buildResponseForCollection( + collectionState, + collectionVsAliases, + routeKey, + liveNodes, + requestedShards)); + }); + }; + clusterStatus.add("collections", collectionPropsWriter); + } } private void addAliasMap(Aliases aliases, NamedList clusterStatus) { diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java index 56dfeb2440d..314655e85ce 100644 --- a/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java +++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/TestCollectionAPI.java @@ -16,6 +16,7 @@ */ package org.apache.solr.cloud.api.collections; +import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; @@ -28,6 +29,7 @@ import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; +import org.apache.solr.client.solrj.impl.NoOpResponseParser; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.response.CollectionAdminResponse; @@ -81,7 +83,6 @@ public void test() throws Exception { client.request(req); createCollection(null, COLLECTION_NAME1, 1, 1, client, null, "conf1"); } - waitForCollection(ZkStateReader.from(cloudClient), COLLECTION_NAME, 2); waitForCollection(ZkStateReader.from(cloudClient), COLLECTION_NAME1, 1); waitForRecoveriesToFinish(COLLECTION_NAME, false); @@ -91,6 +92,7 @@ public void test() throws Exception { clusterStatusNoCollection(); clusterStatusWithCollection(); clusterStatusWithCollectionAndShard(); + clusterStatusWithCollectionAndShardJSON(); clusterStatusWithCollectionAndMultipleShards(); clusterStatusWithCollectionHealthState(); clusterStatusWithRouteKey(); @@ -129,12 +131,10 @@ private void testModifyCollection() throws Exception { .getResponse(); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertEquals(1, collections.size()); - Map collectionProperties = (Map) collections.get(COLLECTION_NAME); - collectionProperties.get("replicationFactor"); - assertEquals("25", collectionProperties.get("replicationFactor")); + assertEquals("25", collections._getStr(List.of(COLLECTION_NAME, "replicationFactor"), null)); params = new ModifiableSolrParams(); params.set("action", CollectionParams.CollectionAction.MODIFYCOLLECTION.toString()); @@ -153,12 +153,10 @@ private void testModifyCollection() throws Exception { System.out.println(rsp); cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - collections = (Map) cluster.get("collections"); + collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertEquals(1, collections.size()); - collectionProperties = (Map) collections.get(COLLECTION_NAME); - collectionProperties.get("replicationFactor"); - assertNull(collectionProperties.get("replicationFactor")); + assertNull(collections._getStr(List.of(COLLECTION_NAME, "replicationFactor"), null)); params = new ModifiableSolrParams(); params.set("action", CollectionParams.CollectionAction.MODIFYCOLLECTION.toString()); @@ -257,7 +255,7 @@ private void testNoConfigset() throws Exception { NamedList rsp = client.request(req); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull( "Testing to insure collections are returned", collections.get(COLLECTION_NAME1)); @@ -284,7 +282,7 @@ private void assertCountsForRepFactorAndNrtReplicas(CloudSolrClient client, Stri NamedList rsp = client.request(request); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertEquals(1, collections.size()); @SuppressWarnings({"unchecked"}) @@ -306,7 +304,7 @@ private void clusterStatusWithCollectionAndShard() throws IOException, SolrServe NamedList rsp = client.request(request); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull(collections.get(COLLECTION_NAME)); assertEquals(1, collections.size()); @@ -332,7 +330,7 @@ private void clusterStatusWithCollectionAndMultipleShards() NamedList rsp = request.process(client).getResponse(); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull(collections.get(COLLECTION_NAME)); assertEquals(1, collections.size()); @@ -467,7 +465,7 @@ private void clusterStatusNoCollection() throws Exception { NamedList rsp = client.request(request); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull(collections.get(COLLECTION_NAME1)); assertEquals(4, collections.size()); @@ -489,7 +487,7 @@ private void clusterStatusWithCollection() throws IOException, SolrServerExcepti NamedList rsp = client.request(request); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertEquals(1, collections.size()); @SuppressWarnings({"unchecked"}) @@ -519,7 +517,7 @@ private void clusterStatusZNodeVersion() throws Exception { NamedList rsp = client.request(request); NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertEquals(1, collections.size()); Map collection = (Map) collections.get(cname); @@ -535,7 +533,7 @@ private void clusterStatusZNodeVersion() throws Exception { rsp = client.request(request); cluster = (NamedList) rsp.get("cluster"); - collections = (Map) cluster.get("collections"); + collections = (NamedList) cluster.get("collections"); collection = (Map) collections.get(cname); Integer newVersion = (Integer) collection.get("znodeVersion"); assertNotNull(newVersion); @@ -562,7 +560,7 @@ private void clusterStatusWithRouteKey() throws IOException, SolrServerException NamedList cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); @SuppressWarnings({"unchecked"}) - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull(collections.get(DEFAULT_COLLECTION)); assertEquals(1, collections.size()); @@ -609,7 +607,7 @@ private void clusterStatusAliasTest() throws Exception { DEFAULT_COLLECTION + "," + COLLECTION_NAME, aliases.get("myalias")); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull(collections.get(DEFAULT_COLLECTION)); Map collection = (Map) collections.get(DEFAULT_COLLECTION); @@ -629,7 +627,7 @@ private void clusterStatusAliasTest() throws Exception { cluster = (NamedList) rsp.get("cluster"); assertNotNull("Cluster state should not be null", cluster); - collections = (Map) cluster.get("collections"); + collections = (NamedList) cluster.get("collections"); assertNotNull("Collections should not be null in cluster state", collections); assertNotNull(collections.get(DEFAULT_COLLECTION)); assertNotNull(collections.get(COLLECTION_NAME)); @@ -652,6 +650,39 @@ private void clusterStatusAliasTest() throws Exception { } } + @SuppressWarnings("unchecked") + private void clusterStatusWithCollectionAndShardJSON() throws IOException, SolrServerException { + + try (CloudSolrClient client = createCloudClient(null)) { + ObjectMapper mapper = new ObjectMapper(); + + ModifiableSolrParams params = new ModifiableSolrParams(); + params.set("action", CollectionParams.CollectionAction.CLUSTERSTATUS.toString()); + params.set("collection", COLLECTION_NAME); + params.set("shard", SHARD1); + params.set("wt", "json"); + QueryRequest request = new QueryRequest(params); + request.setResponseParser(new NoOpResponseParser("json")); + request.setPath("/admin/collections"); + NamedList rsp = client.request(request); + String actualResponse = (String) rsp.get("response"); + + Map result = mapper.readValue(actualResponse, Map.class); + + var cluster = (Map) result.get("cluster"); + assertNotNull("Cluster state should not be null", cluster); + var collections = (Map) cluster.get("collections"); + assertNotNull("Collections should not be null in cluster state", collections); + assertNotNull(collections.get(COLLECTION_NAME)); + assertEquals(1, collections.size()); + var collection = (Map) collections.get(COLLECTION_NAME); + var shardStatus = (Map) collection.get("shards"); + assertEquals(1, shardStatus.size()); + Map selectedShardStatus = (Map) shardStatus.get(SHARD1); + assertNotNull(selectedShardStatus); + } + } + private void clusterStatusRolesTest() throws Exception { try (CloudSolrClient client = createCloudClient(null)) { client.connect(); diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java index d9b3a94d8ec..bad6832c4bd 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/BaseHttpClusterStateProvider.java @@ -41,6 +41,7 @@ import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.CollectionUtil; import org.apache.solr.common.util.EnvUtils; +import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.Utils; import org.slf4j.Logger; @@ -138,16 +139,13 @@ private ClusterState fetchClusterState(SolrClient client) this.liveNodes = Set.copyOf(liveNodesList); liveNodesTimestamp = System.nanoTime(); } - - var collectionsMap = (Map>) cluster.get("collections"); - + var collectionsNl = (NamedList>) cluster.get("collections"); Map collStateByName = - CollectionUtil.newLinkedHashMap(collectionsMap.size()); - for (Entry> entry : collectionsMap.entrySet()) { + CollectionUtil.newLinkedHashMap(collectionsNl.size()); + for (Entry> entry : collectionsNl) { collStateByName.put( entry.getKey(), getDocCollectionFromObjects(entry.getKey(), entry.getValue())); } - return new ClusterState(this.liveNodes, collStateByName); } @@ -184,9 +182,9 @@ private DocCollection fetchCollectionState(SolrClient client, String collection) SimpleOrderedMap cluster = submitClusterStateRequest(client, collection, ClusterStateRequestType.FETCH_COLLECTION); - var collState = (Map) cluster.findRecursive("collections"); - var collStateMap = (Map) collState.get(collection); - + var collStateMap = + (Map) + cluster.findRecursive("collections", collection); // SOLRJ IS ALWAYS JAVABIN if (collStateMap == null) { throw new NotACollectionException(); // probably an alias } diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java index aa0f3b56a1c..ea8225307da 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/ClusterStateProviderTest.java @@ -145,7 +145,7 @@ private Instant getCreationTimeFromClusterStatus(String collectionName) NamedList response = clusterStatusResponse.getResponse(); NamedList cluster = (NamedList) response.get("cluster"); - Map collections = (Map) cluster.get("collections"); + NamedList collections = (NamedList) cluster.get("collections"); Map collection = (Map) collections.get(collectionName); return Instant.ofEpochMilli((long) collection.get("creationTimeMillis")); }