mirror of https://github.com/apache/lucene.git
SOLR-10340: New set-listener and remove-listener API
This commit is contained in:
parent
e4b3df41a9
commit
0fb9d1bd1b
|
@ -63,6 +63,8 @@ New Features
|
||||||
|
|
||||||
* SOLR-10339: New set-trigger and remove-trigger APIs for autoscaling. (shalin)
|
* SOLR-10339: New set-trigger and remove-trigger APIs for autoscaling. (shalin)
|
||||||
|
|
||||||
|
* SOLR-10340: New set-listener and remove-listener API for autoscaling. (shalin)
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
----------------------
|
----------------------
|
||||||
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.
|
* SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
|
|
||||||
package org.apache.solr.cloud.autoscaling;
|
package org.apache.solr.cloud.autoscaling;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class AutoScaling {
|
public class AutoScaling {
|
||||||
|
|
||||||
public enum EventType {
|
public enum EventType {
|
||||||
|
@ -29,4 +34,42 @@ public class AutoScaling {
|
||||||
INDEXRATE
|
INDEXRATE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TriggerStage {
|
||||||
|
STARTED,
|
||||||
|
ABORTED,
|
||||||
|
SUCCEEDED,
|
||||||
|
FAILED,
|
||||||
|
BEFORE_ACTION,
|
||||||
|
AFTER_ACTION
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface TriggerListener {
|
||||||
|
public void triggerFired(Trigger trigger, Event event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HttpCallbackListener implements TriggerListener {
|
||||||
|
@Override
|
||||||
|
public void triggerFired(Trigger trigger, Event event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface Trigger {
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
public EventType getEventType();
|
||||||
|
|
||||||
|
public boolean isEnabled();
|
||||||
|
|
||||||
|
public Map<String, Object> getProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static interface Event {
|
||||||
|
public String getSource();
|
||||||
|
|
||||||
|
public Date getTime();
|
||||||
|
|
||||||
|
public EventType getType();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,13 @@ package org.apache.solr.cloud.autoscaling;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.solr.api.Api;
|
import org.apache.solr.api.Api;
|
||||||
|
@ -88,10 +91,114 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
|
||||||
break;
|
break;
|
||||||
case "remove-trigger":
|
case "remove-trigger":
|
||||||
handleRemoveTrigger(req, rsp, op);
|
handleRemoveTrigger(req, rsp, op);
|
||||||
|
break;
|
||||||
|
case "set-listener":
|
||||||
|
handleSetListener(req, rsp, op);
|
||||||
|
break;
|
||||||
|
case "remove-listener":
|
||||||
|
handleRemoveListener(req, rsp, op);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleRemoveListener(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op) throws KeeperException, InterruptedException {
|
||||||
|
String listenerName = op.getStr("name");
|
||||||
|
|
||||||
|
if (listenerName == null || listenerName.trim().length() == 0) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "The listener name cannot be null or empty");
|
||||||
|
}
|
||||||
|
Map<String, Object> autoScalingConf = zkReadAutoScalingConf(container.getZkController().getZkStateReader());
|
||||||
|
Map<String, Object> listeners = (Map<String, Object>) autoScalingConf.get("listeners");
|
||||||
|
if (listeners == null || !listeners.containsKey(listenerName)) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No listener exists with name: " + listenerName);
|
||||||
|
}
|
||||||
|
zkSetListener(container.getZkController().getZkStateReader(), listenerName, null);
|
||||||
|
rsp.getValues().add("result", "success");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSetListener(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op) throws KeeperException, InterruptedException {
|
||||||
|
String listenerName = op.getStr("name");
|
||||||
|
String triggerName = op.getStr("trigger");
|
||||||
|
List<String> stageNames = op.getStrs("stage", Collections.emptyList());
|
||||||
|
String listenerClass = op.getStr("class");
|
||||||
|
List<String> beforeActions = op.getStrs("beforeAction", Collections.emptyList());
|
||||||
|
List<String> afterActions = op.getStrs("afterAction", Collections.emptyList());
|
||||||
|
|
||||||
|
if (listenerName == null || listenerName.trim().length() == 0) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "The listener name cannot be null or empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> autoScalingConf = zkReadAutoScalingConf(container.getZkController().getZkStateReader());
|
||||||
|
Map<String, Object> triggers = (Map<String, Object>) autoScalingConf.get("triggers");
|
||||||
|
if (triggers == null || !triggers.containsKey(triggerName)) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "A trigger with the name " + triggerName + " does not exist");
|
||||||
|
}
|
||||||
|
Map<String, Object> triggerProps = (Map<String, Object>) triggers.get(triggerName);
|
||||||
|
|
||||||
|
if (stageNames.isEmpty() && beforeActions.isEmpty() && afterActions.isEmpty()) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Either 'stage' or 'beforeAction' or 'afterAction' must be specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String stage : stageNames) {
|
||||||
|
try {
|
||||||
|
AutoScaling.TriggerStage.valueOf(stage);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid stage name: " + stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listenerClass == null || listenerClass.trim().length() == 0) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "The 'class' of the listener cannot be null or empty");
|
||||||
|
}
|
||||||
|
// validate that we can load the listener class
|
||||||
|
// todo nocommit -- what about MemClassLoader?
|
||||||
|
try {
|
||||||
|
container.getResourceLoader().findClass(listenerClass, AutoScaling.TriggerListener.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Listener not found: " + listenerClass, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, String>> actions = (List<Map<String, String>>) triggerProps.get("actions");
|
||||||
|
Set<String> actionNames = new HashSet<>();
|
||||||
|
actionNames.addAll(beforeActions);
|
||||||
|
actionNames.addAll(afterActions);
|
||||||
|
for (Map<String, String> action : actions) {
|
||||||
|
actionNames.remove(action.get("name"));
|
||||||
|
}
|
||||||
|
if (!actionNames.isEmpty()) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "The trigger '" + triggerName + "' does not have actions named: " + actionNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - handle races between competing set-trigger and set-listener invocations
|
||||||
|
zkSetListener(container.getZkController().getZkStateReader(), listenerName, op.getValuesExcluding("name"));
|
||||||
|
rsp.getValues().add("result", "success");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void zkSetListener(ZkStateReader reader, String listenerName, Map<String, Object> listenerProperties) throws KeeperException, InterruptedException {
|
||||||
|
while (true) {
|
||||||
|
Stat stat = new Stat();
|
||||||
|
ZkNodeProps loaded = null;
|
||||||
|
byte[] data = reader.getZkClient().getData(SOLR_AUTOSCALING_CONF_PATH, null, stat, true);
|
||||||
|
loaded = ZkNodeProps.load(data);
|
||||||
|
Map<String, Object> listeners = (Map<String, Object>) loaded.get("listeners");
|
||||||
|
if (listeners == null) listeners = new HashMap<>(1);
|
||||||
|
if (listenerProperties != null) {
|
||||||
|
listeners.put(listenerName, listenerProperties);
|
||||||
|
} else {
|
||||||
|
listeners.remove(listenerName);
|
||||||
|
}
|
||||||
|
loaded = loaded.plus("listeners", listeners);
|
||||||
|
try {
|
||||||
|
reader.getZkClient().setData(SOLR_AUTOSCALING_CONF_PATH, Utils.toJSON(loaded), stat.getVersion(), true);
|
||||||
|
} catch (KeeperException.BadVersionException bve) {
|
||||||
|
// somebody else has changed the configuration so we must retry
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleSetTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op) throws KeeperException, InterruptedException {
|
private void handleSetTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op) throws KeeperException, InterruptedException {
|
||||||
String triggerName = op.getStr("name");
|
String triggerName = op.getStr("name");
|
||||||
|
|
||||||
|
@ -144,7 +251,7 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
|
||||||
try {
|
try {
|
||||||
container.getResourceLoader().findClass(klass, TriggerAction.class);
|
container.getResourceLoader().findClass(klass, TriggerAction.class);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error trying to find Action: " + klass, e);
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Action not found: " + klass, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +261,7 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
|
||||||
|
|
||||||
private void handleRemoveTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op) throws KeeperException, InterruptedException {
|
private void handleRemoveTrigger(SolrQueryRequest req, SolrQueryResponse rsp, CommandOperation op) throws KeeperException, InterruptedException {
|
||||||
String triggerName = op.getStr("name");
|
String triggerName = op.getStr("name");
|
||||||
|
boolean removeListeners = op.getBoolean("removeListeners", false);
|
||||||
|
|
||||||
if (triggerName == null || triggerName.trim().length() == 0) {
|
if (triggerName == null || triggerName.trim().length() == 0) {
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "The trigger name cannot be null or empty");
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "The trigger name cannot be null or empty");
|
||||||
|
@ -163,6 +271,26 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
|
||||||
if (triggers == null || !triggers.containsKey(triggerName)) {
|
if (triggers == null || !triggers.containsKey(triggerName)) {
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No trigger exists with name: " + triggerName);
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No trigger exists with name: " + triggerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, Map<String, Object>> listeners = (Map<String, Map<String, Object>>) autoScalingConf.get("listeners");
|
||||||
|
Set<String> activeListeners = new HashSet<>();
|
||||||
|
if (listeners != null) {
|
||||||
|
for (Map.Entry<String, Map<String, Object>> entry : listeners.entrySet()) {
|
||||||
|
Map<String, Object> listenerProps = entry.getValue();
|
||||||
|
if (triggerName.equals(listenerProps.get("trigger")) && !removeListeners) {
|
||||||
|
activeListeners.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removeListeners) {
|
||||||
|
for (String activeListener : activeListeners) {
|
||||||
|
zkSetListener(container.getZkController().getZkStateReader(), activeListener, null);
|
||||||
|
}
|
||||||
|
} else if (!activeListeners.isEmpty()) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||||
|
"No listeners should exist for trigger: " + triggerName + ". Found listeners: " + activeListeners);
|
||||||
|
}
|
||||||
|
|
||||||
zkSetTrigger(container.getZkController().getZkStateReader(), triggerName, null);
|
zkSetTrigger(container.getZkController().getZkStateReader(), triggerName, null);
|
||||||
rsp.getValues().add("result", "success");
|
rsp.getValues().add("result", "success");
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,8 +58,73 @@
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The name of the trigger to be removed"
|
"description": "The name of the trigger to be removed"
|
||||||
}
|
},
|
||||||
}
|
"removeListeners": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If true, all listeners of this triggers are deleted together with the trigger"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"set-listener": {
|
||||||
|
"description": "The set-listener command lets you add a listener to a trigger",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the listener"
|
||||||
|
},
|
||||||
|
"trigger": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the trigger to listen to"
|
||||||
|
},
|
||||||
|
"stage": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The stage of the trigger for which to listen"
|
||||||
|
"items": {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"beforeAction": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The name of the action before which the listener should be notified"
|
||||||
|
"items": {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterAction": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The name of the action after which the listener should be notified"
|
||||||
|
"items": {
|
||||||
|
"type" : "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"class": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The listener class to be notified once the given stage of the given trigger is activated"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"trigger",
|
||||||
|
"class"
|
||||||
|
],
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
|
"remove-listener": {
|
||||||
|
"description": "Remove a listener",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the listener to be removed"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.solr.client.solrj.SolrClient;
|
||||||
import org.apache.solr.client.solrj.SolrRequest;
|
import org.apache.solr.client.solrj.SolrRequest;
|
||||||
import org.apache.solr.client.solrj.SolrResponse;
|
import org.apache.solr.client.solrj.SolrResponse;
|
||||||
import org.apache.solr.client.solrj.impl.CloudSolrClient;
|
import org.apache.solr.client.solrj.impl.CloudSolrClient;
|
||||||
|
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
||||||
import org.apache.solr.cloud.SolrCloudTestCase;
|
import org.apache.solr.cloud.SolrCloudTestCase;
|
||||||
import org.apache.solr.common.cloud.ZkNodeProps;
|
import org.apache.solr.common.cloud.ZkNodeProps;
|
||||||
import org.apache.solr.common.cloud.ZkStateReader;
|
import org.apache.solr.common.cloud.ZkStateReader;
|
||||||
|
@ -53,7 +54,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
|
||||||
// todo nocommit -- add testing for the v2 path
|
// todo nocommit -- add testing for the v2 path
|
||||||
// String path = random().nextBoolean() ? "/admin/autoscaling" : "/v2/cluster/autoscaling";
|
// String path = random().nextBoolean() ? "/admin/autoscaling" : "/v2/cluster/autoscaling";
|
||||||
String path = "/admin/autoscaling";
|
String path = "/admin/autoscaling";
|
||||||
String addTriggerCommand = "{\n" +
|
String setTriggerCommand = "{\n" +
|
||||||
"\t\"set-trigger\" : {\n" +
|
"\t\"set-trigger\" : {\n" +
|
||||||
"\t\t\"name\" : \"node_lost_trigger\",\n" +
|
"\t\t\"name\" : \"node_lost_trigger\",\n" +
|
||||||
"\t\t\"event\" : \"nodeLost\",\n" +
|
"\t\t\"event\" : \"nodeLost\",\n" +
|
||||||
|
@ -76,7 +77,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
|
||||||
"\t\t]\n" +
|
"\t\t]\n" +
|
||||||
"\t}\n" +
|
"\t}\n" +
|
||||||
"}";
|
"}";
|
||||||
SolrRequest req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, addTriggerCommand);
|
SolrRequest req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, setTriggerCommand);
|
||||||
|
|
||||||
NamedList<Object> response = solrClient.request(req);
|
NamedList<Object> response = solrClient.request(req);
|
||||||
assertEquals(response.get("result").toString(), "success");
|
assertEquals(response.get("result").toString(), "success");
|
||||||
|
@ -91,12 +92,100 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
|
||||||
assertEquals(4, nodeLostTrigger.size());
|
assertEquals(4, nodeLostTrigger.size());
|
||||||
assertEquals("600", nodeLostTrigger.get("waitFor").toString());
|
assertEquals("600", nodeLostTrigger.get("waitFor").toString());
|
||||||
|
|
||||||
|
setTriggerCommand = "{\n" +
|
||||||
|
"\t\"set-trigger\" : {\n" +
|
||||||
|
"\t\t\"name\" : \"node_lost_trigger\",\n" +
|
||||||
|
"\t\t\"event\" : \"nodeLost\",\n" +
|
||||||
|
"\t\t\"waitFor\" : \"20m\",\n" +
|
||||||
|
"\t\t\"enabled\" : \"false\",\n" +
|
||||||
|
"\t\t\"actions\" : [\n" +
|
||||||
|
"\t\t\t{\n" +
|
||||||
|
"\t\t\t\t\"name\" : \"compute_plan\",\n" +
|
||||||
|
"\t\t\t\t\"class\" : \"solr.ComputePlanAction\"\n" +
|
||||||
|
"\t\t\t},\n" +
|
||||||
|
"\t\t\t{\n" +
|
||||||
|
"\t\t\t\t\"name\" : \"execute_plan\",\n" +
|
||||||
|
"\t\t\t\t\"class\" : \"solr.ExecutePlanAction\"\n" +
|
||||||
|
"\t\t\t},\n" +
|
||||||
|
"\t\t\t{\n" +
|
||||||
|
"\t\t\t\t\"name\" : \"log_plan\",\n" +
|
||||||
|
"\t\t\t\t\"class\" : \"solr.LogPlanAction\",\n" +
|
||||||
|
"\t\t\t\t\"collection\" : \".system\"\n" +
|
||||||
|
"\t\t\t}\n" +
|
||||||
|
"\t\t]\n" +
|
||||||
|
"\t}\n" +
|
||||||
|
"}";
|
||||||
|
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, setTriggerCommand);
|
||||||
|
response = solrClient.request(req);
|
||||||
|
assertEquals(response.get("result").toString(), "success");
|
||||||
|
data = zkClient().getData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, null, null, true);
|
||||||
|
loaded = ZkNodeProps.load(data);
|
||||||
|
triggers = (Map<String, Object>) loaded.get("triggers");
|
||||||
|
assertNotNull(triggers);
|
||||||
|
assertEquals(1, triggers.size());
|
||||||
|
assertTrue(triggers.containsKey("node_lost_trigger"));
|
||||||
|
nodeLostTrigger = (Map<String, Object>) triggers.get("node_lost_trigger");
|
||||||
|
assertEquals(4, nodeLostTrigger.size());
|
||||||
|
assertEquals("1200", nodeLostTrigger.get("waitFor").toString());
|
||||||
|
assertEquals("false", nodeLostTrigger.get("enabled").toString());
|
||||||
|
|
||||||
|
String setListenerCommand = "{\n" +
|
||||||
|
"\t\"set-listener\" : \n" +
|
||||||
|
"\t\t{\n" +
|
||||||
|
"\t\t\t\"name\" : \"xyz\",\n" +
|
||||||
|
"\t\t\t\"trigger\" : \"node_lost_trigger\",\n" +
|
||||||
|
"\t\t\t\"stage\" : [\"STARTED\",\"ABORTED\",\"SUCCEEDED\"],\n" +
|
||||||
|
"\t\t\t\"beforeAction\" : \"execute_plan\",\n" +
|
||||||
|
"\t\t\t\"class\" : \"org.apache.solr.cloud.autoscaling.AutoScaling$HttpCallbackListener\",\n" +
|
||||||
|
"\t\t\t\"url\" : \"http://xyz.com/on_node_lost?node={$LOST_NODE_NAME}\"\n" +
|
||||||
|
"\t\t}\n" +
|
||||||
|
"}";
|
||||||
|
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, setListenerCommand);
|
||||||
|
response = solrClient.request(req);
|
||||||
|
assertEquals(response.get("result").toString(), "success");
|
||||||
|
data = zkClient().getData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, null, null, true);
|
||||||
|
loaded = ZkNodeProps.load(data);
|
||||||
|
Map<String, Object> listeners = (Map<String, Object>) loaded.get("listeners");
|
||||||
|
assertNotNull(listeners);
|
||||||
|
assertEquals(1, listeners.size());
|
||||||
|
assertTrue(listeners.containsKey("xyz"));
|
||||||
|
Map<String, Object> xyzListener = (Map<String, Object>) listeners.get("xyz");
|
||||||
|
assertEquals(5, xyzListener.size());
|
||||||
|
assertEquals("org.apache.solr.cloud.autoscaling.AutoScaling$HttpCallbackListener", xyzListener.get("class").toString());
|
||||||
|
|
||||||
String removeTriggerCommand = "{\n" +
|
String removeTriggerCommand = "{\n" +
|
||||||
"\t\"remove-trigger\" : {\n" +
|
"\t\"remove-trigger\" : {\n" +
|
||||||
"\t\t\"name\" : \"node_lost_trigger\"\n" +
|
"\t\t\"name\" : \"node_lost_trigger\"\n" +
|
||||||
"\t}\n" +
|
"\t}\n" +
|
||||||
"}";
|
"}";
|
||||||
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, removeTriggerCommand);
|
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, removeTriggerCommand);
|
||||||
|
try {
|
||||||
|
response = solrClient.request(req);
|
||||||
|
fail("Trying to remove trigger which has listeners registered should have failed");
|
||||||
|
} catch (HttpSolrClient.RemoteSolrException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
String removeListenerCommand = "{\n" +
|
||||||
|
"\t\"remove-listener\" : {\n" +
|
||||||
|
"\t\t\"name\" : \"xyz\"\n" +
|
||||||
|
"\t}\n" +
|
||||||
|
"}";
|
||||||
|
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, removeListenerCommand);
|
||||||
|
response = solrClient.request(req);
|
||||||
|
assertEquals(response.get("result").toString(), "success");
|
||||||
|
data = zkClient().getData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, null, null, true);
|
||||||
|
loaded = ZkNodeProps.load(data);
|
||||||
|
listeners = (Map<String, Object>) loaded.get("listeners");
|
||||||
|
assertNotNull(listeners);
|
||||||
|
assertEquals(0, listeners.size());
|
||||||
|
|
||||||
|
removeTriggerCommand = "{\n" +
|
||||||
|
"\t\"remove-trigger\" : {\n" +
|
||||||
|
"\t\t\"name\" : \"node_lost_trigger\"\n" +
|
||||||
|
"\t}\n" +
|
||||||
|
"}";
|
||||||
|
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, removeTriggerCommand);
|
||||||
response = solrClient.request(req);
|
response = solrClient.request(req);
|
||||||
assertEquals(response.get("result").toString(), "success");
|
assertEquals(response.get("result").toString(), "success");
|
||||||
data = zkClient().getData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, null, null, true);
|
data = zkClient().getData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, null, null, true);
|
||||||
|
@ -104,6 +193,25 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
|
||||||
triggers = (Map<String, Object>) loaded.get("triggers");
|
triggers = (Map<String, Object>) loaded.get("triggers");
|
||||||
assertNotNull(triggers);
|
assertNotNull(triggers);
|
||||||
assertEquals(0, triggers.size());
|
assertEquals(0, triggers.size());
|
||||||
|
|
||||||
|
setListenerCommand = "{\n" +
|
||||||
|
"\t\"set-listener\" : \n" +
|
||||||
|
"\t\t{\n" +
|
||||||
|
"\t\t\t\"name\" : \"xyz\",\n" +
|
||||||
|
"\t\t\t\"trigger\" : \"node_lost_trigger\",\n" +
|
||||||
|
"\t\t\t\"stage\" : [\"STARTED\",\"ABORTED\",\"SUCCEEDED\"],\n" +
|
||||||
|
"\t\t\t\"beforeAction\" : \"execute_plan\",\n" +
|
||||||
|
"\t\t\t\"class\" : \"org.apache.solr.cloud.autoscaling.AutoScaling$HttpCallbackListener\",\n" +
|
||||||
|
"\t\t\t\"url\" : \"http://xyz.com/on_node_lost?node={$LOST_NODE_NAME}\"\n" +
|
||||||
|
"\t\t}\n" +
|
||||||
|
"}";
|
||||||
|
req = new AutoScalingRequest(SolrRequest.METHOD.POST, path, removeListenerCommand);
|
||||||
|
try {
|
||||||
|
response = solrClient.request(req);
|
||||||
|
fail("Adding a listener on a non-existent trigger should have failed");
|
||||||
|
} catch (HttpSolrClient.RemoteSolrException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class AutoScalingRequest extends SolrRequest {
|
static class AutoScalingRequest extends SolrRequest {
|
||||||
|
|
Loading…
Reference in New Issue