This is an automated email from the ASF dual-hosted git repository. ishan pushed a commit to branch jira/SOLR15694 in repository https://gitbox.apache.org/repos/asf/solr.git
commit a14085aaa705ab058cd25212dff19f9881f17002 Author: Ishan Chattopadhyaya <[email protected]> AuthorDate: Sat Dec 18 15:12:13 2021 +0530 Adding Roles API to ref guide --- .../java/org/apache/solr/handler/ClusterAPI.java | 42 ++++++++-- .../test/org/apache/solr/cloud/NodeRolesTest.java | 18 ++-- solr/solr-ref-guide/src/node-roles.adoc | 97 +++++++++++++++++++++- 3 files changed, 140 insertions(+), 17 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java index 9f93c39..39f4750 100644 --- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java +++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java @@ -103,10 +103,8 @@ public class ClusterAPI { } for (String child : children) { Object c = readRecursive(path + "/" + child, zk, depth - 1); - result.put(child, c); } - } catch (Exception e) { throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e); } @@ -115,7 +113,6 @@ public class ClusterAPI { } else { return result; } - } @EndPoint(method = GET, @@ -123,16 +120,45 @@ public class ClusterAPI { permission = COLL_READ_PERM) public void nodesWithRole(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { String role = req.getPathTemplateValues().get("role"); - rsp.add(role, + rsp.add("node-roles", Map.of(role, readRecursive(ZkStateReader.NODE_ROLES + "/"+ role, - collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2)); + collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 2))); + } + + @EndPoint(method = GET, + path = "/cluster/node-roles/nodes/{node}", + permission = COLL_READ_PERM) + @SuppressWarnings("unchecked") + public void rolesForNode(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + String node = req.getPathTemplateValues().get("node"); + Map<String, String> ret = new HashMap<String, String>(); + Map<String, Map<String, Set<String>>> roles = (Map<String, Map<String, Set<String>>>) readRecursive(ZkStateReader.NODE_ROLES, + collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager(), 3); + for (String role: roles.keySet()) { + for (String mode : roles.get(role).keySet()) { + if (roles.get(role).get(mode) instanceof List && ((List) roles.get(role).get(mode)).size() == 0) { + continue; + } + Set<String> nodes = roles.get(role).get(mode); + if (nodes.contains(node)) ret.put(role, mode); + } + } + for (String role: ret.keySet()) { + rsp.add(role, ret.get(role)); + } } + @EndPoint(method = GET, path = "/cluster/node-roles/supported", permission = COLL_READ_PERM) public void supportedRoles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - rsp.add("supported-roles", - collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().listData(ZkStateReader.NODE_ROLES)); + //rsp.add("supported-roles", + // collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager().getDistribStateManager().listData(ZkStateReader.NODE_ROLES)); + Map<String, Object> roleModesSupportedMap = new HashMap<>(); + for (NodeRoles.Role role: NodeRoles.Role.values()) { + roleModesSupportedMap.put(role.toString(), Map.of("modes", role.supportedModes(), "defaultIfAbsent", role.defaultIfAbsent())); + } + rsp.add("supported-roles", roleModesSupportedMap); } @EndPoint(method = GET, @@ -143,7 +169,7 @@ public class ClusterAPI { String roleVal = req.getPathTemplateValues().get("role-val"); List<String> nodes = collectionsHandler.getCoreContainer().getZkController().getSolrCloudManager() .getDistribStateManager().listData(ZkStateReader.NODE_ROLES + "/"+ role+"/"+roleVal); - rsp.add( role, Collections.singletonMap(roleVal, nodes)); + rsp.add( "node-roles", Map.of(role, Collections.singletonMap(roleVal, nodes))); } public static List<String> getNodesByRole(NodeRoles.Role role, String val, DistribStateManager zk) diff --git a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java index cda83ad..1970f6c 100644 --- a/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/NodeRolesTest.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; + import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.V2Request; @@ -52,9 +54,9 @@ public class NodeRolesTest extends SolrCloudTestCase { JettySolrRunner j0 = cluster.getJettySolrRunner(0); JettySolrRunner j1 = null, j2 = null; V2Response rsp = new V2Request.Builder("/cluster/node-roles/supported").GET().build().process(cluster.getSolrClient()); - List<String> l = (List<String>) rsp._get("supported-roles", Collections.emptyList()); - assertTrue(l.contains("data")); - assertTrue(l.contains("overseer")); + Map<String, Object> l = (Map<String, Object>) rsp._get("supported-roles", Collections.emptyMap()); + assertTrue(l.containsKey("data")); + assertTrue(l.containsKey("overseer")); j1 = startNodeWithRoles("overseer:preferred,data:off"); @@ -72,12 +74,12 @@ public class NodeRolesTest extends SolrCloudTestCase { rsp = new V2Request.Builder("/cluster/node-roles/overseer").GET().build().process(cluster.getSolrClient()); - assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); - assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName())); + assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); + assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName())); rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient()); - assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); - assertTrue( ((Collection)rsp._get("overseer/preferred", Collections.emptyList())).contains(j1.getNodeName())); + assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j2.getNodeName())); + assertTrue( ((Collection)rsp._get("node-roles/overseer/preferred", Collections.emptyList())).contains(j1.getNodeName())); String COLLECTION_NAME = "TEST_ROLES"; CollectionAdminRequest @@ -93,7 +95,7 @@ public class NodeRolesTest extends SolrCloudTestCase { // Shutdown the dedicated overseer, make sure that node disappears from the roles output j1.stop(); rsp = new V2Request.Builder("/cluster/node-roles/overseer/preferred").GET().build().process(cluster.getSolrClient()); - assertFalse (((Collection) rsp._get("overseer/preferred" , null)). contains(j1.getNodeName())); + assertFalse (((Collection) rsp._get("node-roles/overseer/preferred" , null)). contains(j1.getNodeName())); } private JettySolrRunner startNodeWithRoles(String roles) throws Exception { diff --git a/solr/solr-ref-guide/src/node-roles.adoc b/solr/solr-ref-guide/src/node-roles.adoc index f3e5969..ed2c07a 100644 --- a/solr/solr-ref-guide/src/node-roles.adoc +++ b/solr/solr-ref-guide/src/node-roles.adoc @@ -62,4 +62,99 @@ A common situation arises when the nodes in a cluster is under heavy querying or * Most nodes, say 10 of them, in the cluster start with `-Dsolr.node.roles=data:on,overseer:allowed` (or with no parameter, since the default value for `solr.node.roles` is the same). * One or more nodes can start with `-Dsolr.node.roles=overseer:preferred` (or `-Dsolr.node.roles=overseer:preferred,data:off`) -In this arrangement, such dedicated nodes can have lesser resources than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up, it will assume the overseer leadership again. \ No newline at end of file +In this arrangement, such dedicated nodes can have lesser resources than other data nodes (since these are stateless nodes) and yet the cluster will behave optimally. In case the dedicated overseer nodes go down for some reason, the overseer leader will be elected from one of the data nodes (since they have overseer in "allowed" mode), and once one of the dedicated overseer nodes are back up, it will assume the overseer leadership again. + +== Roles API + +=== GET /api/cluster/node-roles/supported + +*Input* +[source,text] +---- +curl http://localhost:8983/api/cluster/node-roles/supported +---- + +*Output* +[source,text] +---- +{ + "supported-roles":{ + "data":{ + "defaultIfAbsent":"off", + "modes":["off", + "on"] + }, + "overseer":{ + "defaultIfAbsent":"disallowed", + "modes":["disallowed", + "allowed", + "preferred"] + } + } +} +---- + +=== GET /api/cluster/node-roles + +*Input* +[source,text] +---- +curl http://localhost:8983/api/cluster/node-roles +---- + +*Output* +[source,text] +---- +{ + "node-roles":{ + "data":{ + "off":["solr2:8983_solr"], + "on":["solr1:8983_solr"] + }, + "overseer":{ + "allowed":["solr1:8983_solr"], + "disallowed":[], + "preferred":["solr2:8983_solr"] + } + } +} +---- + +=== GET /api/cluster/node-roles/{role} + +*Input* +[source,text] +---- +http://localhost:8983/api/node-roles/data +---- + +*Output* +[source,text] +---- +{ + "node-roles":{ + "data":{ + "off":["solr2:8983_solr"], + "on":["solr1:8983_solr"] + } + } +} +---- + + +=== GET /api/cluster/node-roles/nodes/{node} + +*Input* +[source,text] +---- +curl http://localhost:8983/api/cluster/node-roles/nodes/solr1:8983_solr +---- + +*Output* +[source,text] +---- +{ + "data":"on", + "overseer":"allowed" +} +----
