You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2017/10/04 08:25:04 UTC
[12/50] [abbrv] lucene-solr:master: SOLR-11076 New
/autoscaling/history API to return past cluster events and actions.
SOLR-11076 New /autoscaling/history API to return past cluster events and actions.
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/98c371f8
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/98c371f8
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/98c371f8
Branch: refs/heads/master
Commit: 98c371f85eca6bcf683286aab4a757947fd86324
Parents: f10993d
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Mon Aug 14 16:28:43 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Wed Aug 23 14:00:16 2017 +0200
----------------------------------------------------------------------
.../cloud/autoscaling/SystemLogListener.java | 35 ++-
.../org/apache/solr/core/CoreContainer.java | 5 +
.../admin/AutoscalingHistoryHandler.java | 132 +++++++++
.../solr/security/PermissionNameProvider.java | 1 +
.../admin/AutoscalingHistoryHandlerTest.java | 275 +++++++++++++++++++
.../apache/solr/common/params/CommonParams.java | 2 +
6 files changed, 441 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98c371f8/solr/core/src/java/org/apache/solr/cloud/autoscaling/SystemLogListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/SystemLogListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/SystemLogListener.java
index c42803f..a72b174 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/SystemLogListener.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/SystemLogListener.java
@@ -22,9 +22,11 @@ import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Date;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.StringJoiner;
import org.apache.solr.client.solrj.SolrRequest;
@@ -56,6 +58,14 @@ public class SystemLogListener extends TriggerListenerBase {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String SOURCE_FIELD = "source_s";
+ public static final String EVENT_SOURCE_FIELD = "event.source_s";
+ public static final String EVENT_TYPE_FIELD = "event.type_s";
+ public static final String STAGE_FIELD = "stage_s";
+ public static final String ACTION_FIELD = "action_s";
+ public static final String MESSAGE_FIELD = "message_t";
+ public static final String BEFORE_ACTIONS_FIELD = "before.actions_ss";
+ public static final String AFTER_ACTIONS_FIELD = "after.actions_ss";
+ public static final String COLLECTIONS_FIELD = "collections_ss";
public static final String SOURCE = SystemLogListener.class.getSimpleName();
public static final String DOC_TYPE = "autoscaling_event";
@@ -84,17 +94,17 @@ public class SystemLogListener extends TriggerListenerBase {
doc.addField(SOURCE_FIELD, SOURCE);
doc.addField("id", IdUtils.timeRandomId());
doc.addField("event.id_s", event.getId());
- doc.addField("event.type_s", event.getEventType().toString());
- doc.addField("event.source_s", event.getSource());
+ doc.addField(EVENT_TYPE_FIELD, event.getEventType().toString());
+ doc.addField(EVENT_SOURCE_FIELD, event.getSource());
doc.addField("event.time_l", event.getEventTime());
doc.addField("timestamp", new Date());
addMap("event.property.", doc, event.getProperties());
- doc.addField("stage_s", stage.toString());
+ doc.addField(STAGE_FIELD, stage.toString());
if (actionName != null) {
- doc.addField("action_s", actionName);
+ doc.addField(ACTION_FIELD, actionName);
}
if (message != null) {
- doc.addField("message_t", message);
+ doc.addField(MESSAGE_FIELD, message);
}
addError(doc, error);
// add JSON versions of event and context
@@ -105,8 +115,8 @@ public class SystemLogListener extends TriggerListenerBase {
addOperations(doc, (List<SolrRequest>)context.getProperties().get("operations"));
// capture specifics of responses after execute_plan action
addResponses(doc, (List<NamedList<Object>>)context.getProperties().get("responses"));
- addActions("before", doc, (List<String>)context.getProperties().get(TriggerEventProcessorStage.BEFORE_ACTION.toString()));
- addActions("after", doc, (List<String>)context.getProperties().get(TriggerEventProcessorStage.AFTER_ACTION.toString()));
+ addActions(BEFORE_ACTIONS_FIELD, doc, (List<String>)context.getProperties().get(TriggerEventProcessorStage.BEFORE_ACTION.toString()));
+ addActions(AFTER_ACTIONS_FIELD, doc, (List<String>)context.getProperties().get(TriggerEventProcessorStage.AFTER_ACTION.toString()));
String contextJson = Utils.toJSONString(context);
doc.addField("context_str", contextJson);
}
@@ -124,11 +134,11 @@ public class SystemLogListener extends TriggerListenerBase {
}
}
- private void addActions(String prefix, SolrInputDocument doc, List<String> actions) {
+ private void addActions(String field, SolrInputDocument doc, List<String> actions) {
if (actions == null) {
return;
}
- actions.forEach(a -> doc.addField(prefix + ".actions_ss", a));
+ actions.forEach(a -> doc.addField(field, a));
}
private void addMap(String prefix, SolrInputDocument doc, Map<String, Object> map) {
@@ -147,11 +157,15 @@ public class SystemLogListener extends TriggerListenerBase {
if (operations == null || operations.isEmpty()) {
return;
}
+ Set<String> collections = new HashSet<>();
for (SolrRequest req : operations) {
SolrParams params = req.getParams();
if (params == null) {
continue;
}
+ if (params.get(CollectionAdminParams.COLLECTION) != null) {
+ collections.add(params.get(CollectionAdminParams.COLLECTION));
+ }
// build a whitespace-separated param string
StringJoiner paramJoiner = new StringJoiner(" ");
paramJoiner.setEmptyValue("");
@@ -167,6 +181,9 @@ public class SystemLogListener extends TriggerListenerBase {
doc.addField("operations.params_ts", paramString);
}
}
+ if (!collections.isEmpty()) {
+ doc.addField(COLLECTIONS_FIELD, collections);
+ }
}
private void addResponses(SolrInputDocument doc, List<NamedList<Object>> responses) {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98c371f8/solr/core/src/java/org/apache/solr/core/CoreContainer.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index b5eb3a5..0198f9d 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -19,6 +19,7 @@ package org.apache.solr.core;
import static java.util.Objects.requireNonNull;
import static org.apache.solr.common.params.CommonParams.AUTHC_PATH;
import static org.apache.solr.common.params.CommonParams.AUTHZ_PATH;
+import static org.apache.solr.common.params.CommonParams.AUTOSCALING_HISTORY_PATH;
import static org.apache.solr.common.params.CommonParams.COLLECTIONS_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.CONFIGSETS_HANDLER_PATH;
import static org.apache.solr.common.params.CommonParams.CORES_HANDLER_PATH;
@@ -78,6 +79,7 @@ import org.apache.solr.core.backup.repository.BackupRepository;
import org.apache.solr.core.backup.repository.BackupRepositoryFactory;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.handler.SnapShooter;
+import org.apache.solr.handler.admin.AutoscalingHistoryHandler;
import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.handler.admin.CoreAdminHandler;
@@ -189,6 +191,8 @@ public class CoreContainer {
protected MetricsCollectorHandler metricsCollectorHandler;
+ protected AutoscalingHistoryHandler autoscalingHistoryHandler;
+
// Bits for the state variable.
public final static long LOAD_COMPLETE = 0x1L;
@@ -532,6 +536,7 @@ public class CoreContainer {
coreAdminHandler = createHandler(CORES_HANDLER_PATH, cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class);
configSetsHandler = createHandler(CONFIGSETS_HANDLER_PATH, cfg.getConfigSetsHandlerClass(), ConfigSetsHandler.class);
metricsHandler = createHandler(METRICS_PATH, MetricsHandler.class.getName(), MetricsHandler.class);
+ autoscalingHistoryHandler = createHandler(AUTOSCALING_HISTORY_PATH, AutoscalingHistoryHandler.class.getName(), AutoscalingHistoryHandler.class);
metricsCollectorHandler = createHandler(MetricsCollectorHandler.HANDLER_PATH, MetricsCollectorHandler.class.getName(), MetricsCollectorHandler.class);
// may want to add some configuration here in the future
metricsCollectorHandler.init(null);
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98c371f8/solr/core/src/java/org/apache/solr/handler/admin/AutoscalingHistoryHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/AutoscalingHistoryHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/AutoscalingHistoryHandler.java
new file mode 100644
index 0000000..7d2c43a
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/handler/admin/AutoscalingHistoryHandler.java
@@ -0,0 +1,132 @@
+package org.apache.solr.handler.admin;
+
+import java.lang.invoke.MethodHandles;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.cloud.autoscaling.SystemLogListener;
+import org.apache.solr.cloud.autoscaling.TriggerEvent;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.AutoScalingParams;
+import org.apache.solr.common.params.CollectionAdminParams;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.RequestHandlerBase;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This handler makes it easier to retrieve a history of autoscaling events from the .system
+ * collection.
+ */
+public class AutoscalingHistoryHandler extends RequestHandlerBase implements PermissionNameProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static final String SYSTEM_COLLECTION_PARAM = "systemCollection";
+
+ public static final String ACTION_PARAM = "action";
+ public static final String MESSAGE_PARAM = "message";
+ public static final String TRIGGER_PARAM = AutoScalingParams.TRIGGER;
+ public static final String TYPE_PARAM = "eventType";
+ public static final String NODE_PARAM = "node";
+ public static final String COLLECTION_PARAM = CollectionAdminParams.COLLECTION;
+ public static final String STAGE_PARAM = AutoScalingParams.STAGE;
+ public static final String BEFORE_ACTION_PARAM = AutoScalingParams.BEFORE_ACTION;
+ public static final String AFTER_ACTION_PARAM = AutoScalingParams.AFTER_ACTION;
+
+ private static final String EVENTS_FQ = "{!term f=" + CommonParams.TYPE + "}" + SystemLogListener.DOC_TYPE;
+
+ private static final String ACTION_FQ_FORMAT = "{!term f=" + SystemLogListener.ACTION_FIELD + "}%s";
+ private static final String MESSAGE_FQ_FORMAT = "{!lucene}" + SystemLogListener.MESSAGE_FIELD + ":%s";
+ private static final String TRIGGER_FQ_FORMAT = "{!term f=" + SystemLogListener.EVENT_SOURCE_FIELD + "}%s";
+ private static final String STAGE_FQ_FORMAT = "{!term f=" + SystemLogListener.STAGE_FIELD + "}%s";
+ private static final String COLLECTION_FQ_FORMAT = "{!term f=" + SystemLogListener.COLLECTIONS_FIELD + "}%s";
+ private static final String TYPE_FQ_FORMAT = "{!term f=" + SystemLogListener.EVENT_TYPE_FIELD + "}%s";
+ private static final String NODE_FQ_FORMAT = "{!term f=event.property." + TriggerEvent.NODE_NAME + "_s}%s";
+ private static final String BEFORE_ACTION_FQ_FORMAT = "{!term f=" + SystemLogListener.BEFORE_ACTIONS_FIELD + "}%s";
+ private static final String AFTER_ACTION_FQ_FORMAT = "{!term f=" + SystemLogListener.AFTER_ACTIONS_FIELD + "}%s";
+
+ private static final Map<String, String> formats = new HashMap<String, String>() {{
+ put(ACTION_PARAM, ACTION_FQ_FORMAT);
+ put(MESSAGE_PARAM, MESSAGE_FQ_FORMAT);
+ put(TRIGGER_PARAM, TRIGGER_FQ_FORMAT);
+ put(TYPE_PARAM, TYPE_FQ_FORMAT);
+ put(STAGE_PARAM, STAGE_FQ_FORMAT);
+ put(NODE_PARAM, NODE_FQ_FORMAT);
+ put(COLLECTION_PARAM, COLLECTION_FQ_FORMAT);
+ put(BEFORE_ACTION_PARAM, BEFORE_ACTION_FQ_FORMAT);
+ put(AFTER_ACTION_PARAM, AFTER_ACTION_FQ_FORMAT);
+ }};
+
+ private final CoreContainer coreContainer;
+
+
+ public AutoscalingHistoryHandler(CoreContainer coreContainer) {
+ this.coreContainer = coreContainer;
+ }
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.AUTOSCALING_HISTORY_READ_PERM;
+ }
+
+ @Override
+ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ ModifiableSolrParams params = new ModifiableSolrParams(req.getParams());
+ String collection = params.get(SYSTEM_COLLECTION_PARAM, CollectionAdminParams.SYSTEM_COLL);
+ params.remove(SYSTEM_COLLECTION_PARAM);
+ params.remove(CommonParams.QT);
+ // check that we have the main query, if not then use *:*
+ if (params.get(CommonParams.Q) == null) {
+ params.add(CommonParams.Q, "*:*");
+ }
+ // sort by doc id, which are time-based, unless specified otherwise
+ if (params.get(CommonParams.SORT) == null) {
+ params.add(CommonParams.SORT, "id asc");
+ }
+ // filter query to pick only autoscaling events
+ params.remove(CommonParams.FQ, EVENTS_FQ);
+ params.add(CommonParams.FQ, EVENTS_FQ);
+ // add filters translated from simplified parameters
+ for (Map.Entry<String, String> e : formats.entrySet()) {
+ String[] values = params.remove(e.getKey());
+ if (values != null) {
+ for (String value : values) {
+ params.add(CommonParams.FQ, String.format(e.getValue(), value));
+ }
+ }
+ }
+ try (CloudSolrClient cloudSolrClient = new CloudSolrClient.Builder()
+ .withZkHost(coreContainer.getZkController().getZkServerAddress())
+ .withHttpClient(coreContainer.getUpdateShardHandler().getHttpClient())
+ .build()) {
+ QueryResponse qr = cloudSolrClient.query(collection, params);
+ rsp.getValues().add("response", qr.getResults());
+ } catch (Exception e) {
+ if ((e instanceof SolrException) && e.getMessage().contains("Collection not found")) {
+ // relatively benign
+ LOG.info("Collection " + collection + " does not exist.");
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "A handler to return autoscaling event history";
+ }
+
+ @Override
+ public Category getCategory() {
+ return Category.ADMIN;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98c371f8/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
index dc98d26..4073947 100644
--- a/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
+++ b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
@@ -49,6 +49,7 @@ public interface PermissionNameProvider {
METRICS_READ_PERM("metrics-read", null),
AUTOSCALING_READ_PERM("autoscaling-read", null),
AUTOSCALING_WRITE_PERM("autoscaling-write", null),
+ AUTOSCALING_HISTORY_READ_PERM("autoscaling-history-read", null),
ALL("all", unmodifiableSet(new HashSet<>(asList("*", null))))
;
final String name;
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98c371f8/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java
new file mode 100644
index 0000000..e26d9d7
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/admin/AutoscalingHistoryHandlerTest.java
@@ -0,0 +1,275 @@
+package org.apache.solr.handler.admin;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.cloud.autoscaling.ActionContext;
+import org.apache.solr.cloud.autoscaling.SystemLogListener;
+import org.apache.solr.cloud.autoscaling.TriggerActionBase;
+import org.apache.solr.cloud.autoscaling.TriggerEvent;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.params.CollectionAdminParams;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.solr.cloud.autoscaling.AutoScalingHandlerTest.createAutoScalingRequest;
+
+/**
+ *
+ */
+public class AutoscalingHistoryHandlerTest extends SolrCloudTestCase {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static CountDownLatch actionFiredLatch;
+ private static CloudSolrClient solrClient;
+ private static String PREFIX = AutoscalingHistoryHandlerTest.class.getSimpleName();
+
+ private static CountDownLatch getActionFiredLatch() {
+ return actionFiredLatch;
+ }
+
+ @BeforeClass
+ public static void setupCluster() throws Exception {
+ configureCluster(3)
+ .addConfig("conf", configset("cloud-minimal"))
+ .configure();
+ solrClient = cluster.getSolrClient();
+ CollectionAdminRequest.createCollection(PREFIX + "_collection", null, 1, 3)
+ .process(solrClient);
+ CollectionAdminRequest.createCollection(CollectionAdminParams.SYSTEM_COLL, null, 1, 3)
+ .process(solrClient);
+ }
+
+ public static class TesterAction extends TriggerActionBase {
+
+ @Override
+ public void process(TriggerEvent event, ActionContext context) {
+ getActionFiredLatch().countDown();
+ }
+ }
+
+ @Before
+ public void setupTest() throws Exception {
+ actionFiredLatch = new CountDownLatch(1);
+
+ // first trigger
+ String setTriggerCommand = "{" +
+ "'set-trigger' : {" +
+ "'name' : '" + PREFIX + "_node_added_trigger'," +
+ "'event' : 'nodeAdded'," +
+ "'waitFor' : '0s'," +
+ "'enabled' : true," +
+ "'actions' : [" +
+ "{'name':'compute_plan','class':'solr.ComputePlanAction'}," +
+ "{'name':'execute_plan','class':'solr.ExecutePlanAction'}," +
+ "{'name':'test','class':'" + TesterAction.class.getName() + "'}" +
+ "]" +
+ "}}";
+ SolrRequest req = createAutoScalingRequest(SolrRequest.METHOD.POST, setTriggerCommand);
+ NamedList<Object> response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+
+ // second trigger
+ setTriggerCommand = "{" +
+ "'set-trigger' : {" +
+ "'name' : '" + PREFIX + "_node_lost_trigger'," +
+ "'event' : 'nodeLost'," +
+ "'waitFor' : '0s'," +
+ "'enabled' : true," +
+ "'actions' : [" +
+ "{'name':'compute_plan','class':'solr.ComputePlanAction'}," +
+ "{'name':'execute_plan','class':'solr.ExecutePlanAction'}," +
+ "{'name':'test','class':'" + TesterAction.class.getName() + "'}" +
+ "]" +
+ "}}";
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, setTriggerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+
+ // remove default listeners
+ String removeListenerCommand = "{\n" +
+ "\t\"remove-listener\" : {\n" +
+ "\t\t\"name\" : \"" + PREFIX + "_node_lost_trigger.system\"\n" +
+ "\t}\n" +
+ "}";
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, removeListenerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+ removeListenerCommand = "{\n" +
+ "\t\"remove-listener\" : {\n" +
+ "\t\t\"name\" : \"" + PREFIX + "_node_added_trigger.system\"\n" +
+ "\t}\n" +
+ "}";
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, removeListenerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+ // set up our own listeners
+ String setListenerCommand = "{" +
+ "'set-listener' : " +
+ "{" +
+ "'name' : 'node_added'," +
+ "'trigger' : '" + PREFIX + "_node_added_trigger'," +
+ "'stage' : ['STARTED','ABORTED','SUCCEEDED', 'FAILED']," +
+ "'beforeAction' : ['compute_plan','execute_plan','test']," +
+ "'afterAction' : ['compute_plan','execute_plan','test']," +
+ "'class' : '" + SystemLogListener.class.getName() + "'" +
+ "}" +
+ "}";
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, setListenerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+ setListenerCommand = "{" +
+ "'set-listener' : " +
+ "{" +
+ "'name' : 'node_lost'," +
+ "'trigger' : '" + PREFIX + "_node_lost_trigger'," +
+ "'stage' : ['STARTED','ABORTED','SUCCEEDED', 'FAILED']," +
+ "'beforeAction' : ['compute_plan','execute_plan','test']," +
+ "'afterAction' : ['compute_plan','execute_plan','test']," +
+ "'class' : '" + SystemLogListener.class.getName() + "'" +
+ "}" +
+ "}";
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, setListenerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+
+ }
+
+ private void resetLatch() {
+ actionFiredLatch = new CountDownLatch(1);
+ }
+
+ @Test
+ public void testHistory() throws Exception {
+ JettySolrRunner jetty = cluster.startJettySolrRunner();
+ String nodeAddedName = jetty.getNodeName();
+ boolean await = actionFiredLatch.await(60, TimeUnit.SECONDS);
+ assertTrue("action did not execute", await);
+ // commit on the history collection
+ solrClient.commit(CollectionAdminParams.SYSTEM_COLL);
+ // verify that new docs exist
+ ModifiableSolrParams query = params(CommonParams.Q, "type:" + SystemLogListener.DOC_TYPE,
+ CommonParams.FQ, "event.source_s:" + PREFIX + "_node_added_trigger");
+ QueryResponse resp = solrClient.query(CollectionAdminParams.SYSTEM_COLL, query);
+ SolrDocumentList docs = resp.getResults();
+ assertNotNull(docs);
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.TRIGGER_PARAM, PREFIX + "_node_added_trigger");
+ docs = solrClient.query(query).getResults();
+ assertEquals(8, docs.size());
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.STAGE_PARAM, "STARTED");
+ docs = solrClient.query(query).getResults();
+ assertEquals(1, docs.size());
+ assertEquals("NODEADDED", docs.get(0).getFieldValue("event.type_s"));
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.NODE_PARAM, nodeAddedName);
+ docs = solrClient.query(query).getResults();
+ assertEquals(8, docs.size());
+ for (SolrDocument doc : docs) {
+ assertEquals(nodeAddedName, doc.getFieldValue("event.property.nodeName_s"));
+ }
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.ACTION_PARAM, "test");
+ docs = solrClient.query(query).getResults();
+ assertEquals(2, docs.size());
+ assertEquals("BEFORE_ACTION", docs.get(0).getFieldValue("stage_s"));
+ assertEquals("AFTER_ACTION", docs.get(1).getFieldValue("stage_s"));
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.ACTION_PARAM, "test");
+ docs = solrClient.query(query).getResults();
+ assertEquals(2, docs.size());
+ assertEquals("BEFORE_ACTION", docs.get(0).getFieldValue("stage_s"));
+ assertEquals("AFTER_ACTION", docs.get(1).getFieldValue("stage_s"));
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.COLLECTION_PARAM, CollectionAdminParams.SYSTEM_COLL);
+ docs = solrClient.query(query).getResults();
+ assertEquals(5, docs.size());
+ assertEquals("AFTER_ACTION", docs.get(0).getFieldValue("stage_s"));
+ assertEquals("compute_plan", docs.get(0).getFieldValue("action_s"));
+
+ // reset latch
+ resetLatch();
+
+ // kill a node
+ cluster.stopJettySolrRunner(0);
+ await = actionFiredLatch.await(60, TimeUnit.SECONDS);
+ // wait for recovery
+ waitForRecovery(PREFIX + "_collection");
+ Thread.sleep(5000);
+ // commit on the history collection
+ solrClient.commit(CollectionAdminParams.SYSTEM_COLL);
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.TRIGGER_PARAM, PREFIX + "_node_lost_trigger");
+ docs = solrClient.query(query).getResults();
+ assertEquals(8, docs.size());
+
+ query = params(CommonParams.QT, CommonParams.AUTOSCALING_HISTORY_PATH,
+ AutoscalingHistoryHandler.TRIGGER_PARAM, PREFIX + "_node_lost_trigger",
+ AutoscalingHistoryHandler.COLLECTION_PARAM, PREFIX + "_collection");
+ docs = solrClient.query(query).getResults();
+ assertEquals(5, docs.size());
+ }
+
+ private void waitForRecovery(String collection) throws Exception {
+ boolean recovered = false;
+ for (int i = 0; i < 300; i++) {
+ ClusterState state = solrClient.getZkStateReader().getClusterState();
+ DocCollection collState = getCollectionState(collection);
+ log.debug("###### " + collState);
+ Collection<Replica> replicas = collState.getReplicas();
+ boolean allActive = true;
+ boolean hasLeaders = true;
+ if (replicas != null && !replicas.isEmpty()) {
+ for (Replica r : replicas) {
+ if (!r.isActive(state.getLiveNodes())) {
+ log.info("Not active: " + r);
+ allActive = false;
+ }
+ }
+ } else {
+ allActive = false;
+ }
+ for (Slice slice : collState.getSlices()) {
+ if (slice.getLeader() == null) {
+ hasLeaders = false;
+ }
+ }
+ if (allActive && hasLeaders) {
+ recovered = true;
+ break;
+ } else {
+ log.info("--- waiting, allActive=" + allActive + ", hasLeaders=" + hasLeaders);
+ Thread.sleep(1000);
+ }
+ }
+ assertTrue("replica never fully recovered", recovered);
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/98c371f8/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java b/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java
index 2d079fc..73b46a3 100644
--- a/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java
+++ b/solr/solrj/src/java/org/apache/solr/common/params/CommonParams.java
@@ -184,6 +184,7 @@ public interface CommonParams {
String ZK_PATH = "/admin/zookeeper";
String METRICS_PATH = "/admin/metrics";
String AUTOSCALING_PATH = "/admin/autoscaling";
+ String AUTOSCALING_HISTORY_PATH = "/admin/autoscaling/history";
String AUTOSCALING_DIAGNOSTICS_PATH = "/admin/autoscaling/diagnostics";
String STATUS = "status";
@@ -200,6 +201,7 @@ public interface CommonParams {
AUTHZ_PATH,
METRICS_PATH,
AUTOSCALING_PATH,
+ AUTOSCALING_HISTORY_PATH,
AUTOSCALING_DIAGNOSTICS_PATH)));
String APISPEC_LOCATION = "apispec/";
String INTROSPECT = "/_introspect";