Skip to content

Commit

Permalink
Create healthstats API for query insights
Browse files Browse the repository at this point in the history
Signed-off-by: Chenyang Ji <cyji@amazon.com>
  • Loading branch information
ansjcy committed Sep 23, 2024
1 parent d74adb3 commit ec73491
Show file tree
Hide file tree
Showing 18 changed files with 993 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@
import org.opensearch.env.NodeEnvironment;
import org.opensearch.plugin.insights.core.listener.QueryInsightsListener;
import org.opensearch.plugin.insights.core.service.QueryInsightsService;
import org.opensearch.plugin.insights.rules.action.health_stats.HealthStatsAction;
import org.opensearch.plugin.insights.rules.action.top_queries.TopQueriesAction;
import org.opensearch.plugin.insights.rules.resthandler.health_stats.RestHealthStatsAction;
import org.opensearch.plugin.insights.rules.resthandler.top_queries.RestTopQueriesAction;
import org.opensearch.plugin.insights.rules.transport.health_stats.TransportHealthStatsAction;
import org.opensearch.plugin.insights.rules.transport.top_queries.TransportTopQueriesAction;
import org.opensearch.plugin.insights.settings.QueryCategorizationSettings;
import org.opensearch.plugin.insights.settings.QueryInsightsSettings;
Expand Down Expand Up @@ -107,12 +110,18 @@ public List<RestHandler> getRestHandlers(
final IndexNameExpressionResolver indexNameExpressionResolver,
final Supplier<DiscoveryNodes> nodesInCluster
) {
return List.of(new RestTopQueriesAction());
return List.of(
new RestTopQueriesAction(),
new RestHealthStatsAction()
);
}

@Override
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
return List.of(new ActionPlugin.ActionHandler<>(TopQueriesAction.INSTANCE, TransportTopQueriesAction.class));
return List.of(
new ActionPlugin.ActionHandler<>(TopQueriesAction.INSTANCE, TransportTopQueriesAction.class),
new ActionPlugin.ActionHandler<>(HealthStatsAction.INSTANCE, TransportHealthStatsAction.class)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights.rules.action.health_stats;

import org.opensearch.action.ActionType;

/**
* Transport action for cluster/node level query insights plugin health stats.
*/
public class HealthStatsAction extends ActionType<HealthStatsResponse> {

/**
* The HealthStatsAction Instance.
*/
public static final HealthStatsAction INSTANCE = new HealthStatsAction();
/**
* The name of this Action
*/
public static final String NAME = "cluster:admin/opensearch/insights/health_stats";

private HealthStatsAction() {
super(NAME, HealthStatsResponse::new);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights.rules.action.health_stats;

import org.opensearch.action.support.nodes.BaseNodeResponse;
import org.opensearch.cluster.node.DiscoveryNode;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.plugin.insights.rules.model.healthStats.QueryInsightsHealthStats;

import java.io.IOException;

/**
* Holds the health stats retrieved from a node
*/
public class HealthStatsNodeResponse extends BaseNodeResponse implements ToXContentObject {
/** The health stats retrieved from one node */
private final QueryInsightsHealthStats healthStats;

/**
* Create the HealthStatsNodeResponse Object from StreamInput
* @param in A {@link StreamInput} object.
* @throws IOException IOException
*/
public HealthStatsNodeResponse(final StreamInput in) throws IOException {
super(in);
healthStats = new QueryInsightsHealthStats(in);
}

/**
* Create the HealthStatsNodeResponse Object
* @param node A node that is part of the cluster.
* @param healthStats A list of HealthStats from nodes.
*/
public HealthStatsNodeResponse(final DiscoveryNode node, final QueryInsightsHealthStats healthStats) {
super(node);
this.healthStats = healthStats;
}

@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject(this.getNode().getId());
healthStats.toXContent(builder, params);
return builder.endObject();
}

@Override
public void writeTo(final StreamOutput out) throws IOException {
super.writeTo(out);
healthStats.writeTo(out);

}

/**
* Get health stats
*
* @return the health stats records in this node response
*/
public QueryInsightsHealthStats getHealthStats() {
return healthStats;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights.rules.action.health_stats;

import org.opensearch.action.support.nodes.BaseNodesRequest;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;

import java.io.IOException;

/**
* A request to get cluster/node level health stats information.
*/
public class HealthStatsRequest extends BaseNodesRequest<HealthStatsRequest> {
/**
* Constructor for HealthStatsRequest
*
* @param in A {@link StreamInput} object.
* @throws IOException if the stream cannot be deserialized.
*/
public HealthStatsRequest(final StreamInput in) throws IOException {
super(in);
}

/**
* Get health stats from nodes based on the nodes ids specified.
* If none are passed, cluster level health stats will be returned.
*
* @param nodesIds the nodeIds specified in the request
*/
public HealthStatsRequest(final String... nodesIds) {
super(nodesIds);
}

@Override
public void writeTo(final StreamOutput out) throws IOException {
super.writeTo(out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights.rules.action.health_stats;

import org.opensearch.action.FailedNodeException;
import org.opensearch.action.support.nodes.BaseNodesResponse;
import org.opensearch.cluster.ClusterName;
import org.opensearch.common.xcontent.XContentFactory;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.xcontent.ToXContentFragment;
import org.opensearch.core.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.List;

/**
* Transport response for cluster/node level health stats
*/
public class HealthStatsResponse extends BaseNodesResponse<HealthStatsNodeResponse> implements ToXContentFragment {
/**
* Constructor for HealthStatsNodeResponseResponse.
*
* @param in A {@link StreamInput} object.
* @throws IOException if the stream cannot be deserialized.
*/
public HealthStatsResponse(final StreamInput in) throws IOException {
super(in);
}

/**
* Constructor for HealthStatsResponse
*
* @param clusterName The current cluster name
* @param nodes A list that contains health stats from all nodes
* @param failures A list that contains FailedNodeException
*/
public HealthStatsResponse(
final ClusterName clusterName,
final List<HealthStatsNodeResponse> nodes,
final List<FailedNodeException> failures
) {
super(clusterName, nodes, failures);
}

@Override
protected List<HealthStatsNodeResponse> readNodesFrom(final StreamInput in) throws IOException {
return in.readList(HealthStatsNodeResponse::new);
}

@Override
protected void writeNodesTo(final StreamOutput out, final List<HealthStatsNodeResponse> nodes) throws IOException {
out.writeList(nodes);
}

@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
final List<HealthStatsNodeResponse> results = getNodes();
builder.startObject();
for (HealthStatsNodeResponse nodeResponse : results) {
nodeResponse.toXContent(builder, params);
}
return builder.endObject();
}

@Override
public String toString() {
try {
final XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
builder.startObject();
this.toXContent(builder, EMPTY_PARAMS);
builder.endObject();
return builder.toString();
} catch (IOException e) {
return "{ \"error\" : \"" + e.getMessage() + "\"}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

/**
* Transport Actions, Requests and Responses for Query Insights Health Stats
*/
package org.opensearch.plugin.insights.rules.action.health_stats;
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.plugin.insights.rules.resthandler.health_stats;

import static org.opensearch.plugin.insights.settings.QueryInsightsSettings.QUERY_INSIGHTS_HEALTH_STATS_URI;
import static org.opensearch.rest.RestRequest.Method.GET;

import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.joda.time.DateTime;
import org.opensearch.client.node.NodeClient;
import org.opensearch.common.settings.Settings;
import org.opensearch.core.common.Strings;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.ToXContent;
import org.opensearch.plugin.insights.rules.action.health_stats.HealthStatsAction;
import org.opensearch.plugin.insights.rules.action.health_stats.HealthStatsRequest;
import org.opensearch.plugin.insights.rules.action.health_stats.HealthStatsResponse;
import org.opensearch.plugin.insights.rules.action.top_queries.TopQueriesAction;
import org.opensearch.plugin.insights.rules.action.top_queries.TopQueriesRequest;
import org.opensearch.plugin.insights.rules.action.top_queries.TopQueriesResponse;
import org.opensearch.plugin.insights.rules.model.MetricType;
import org.opensearch.rest.BaseRestHandler;
import org.opensearch.rest.BytesRestResponse;
import org.opensearch.rest.RestChannel;
import org.opensearch.rest.RestRequest;
import org.opensearch.rest.RestResponse;
import org.opensearch.rest.action.RestResponseListener;

/**
* Rest action to get operational health stats of the query insights plugin
*/
public class RestHealthStatsAction extends BaseRestHandler {
/**
* Constructor for RestHealthStatsAction
*/
public RestHealthStatsAction() {}

@Override
public List<Route> routes() {
return List.of(
new Route(GET, QUERY_INSIGHTS_HEALTH_STATS_URI)
);
}

@Override
public String getName() {
return "query_insights_health_stats_action";
}

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) {
final HealthStatsRequest healthStatsRequest = prepareRequest(request);
healthStatsRequest.timeout(request.param("timeout"));

return channel -> client.execute(HealthStatsAction.INSTANCE, healthStatsRequest, healthStatsResponse(channel));
}

static HealthStatsRequest prepareRequest(final RestRequest request) {
final String[] nodesIds = Strings.splitStringByCommaToArray(request.param("nodeId"));
return new HealthStatsRequest(nodesIds);
}

@Override
protected Set<String> responseParams() {
return Settings.FORMAT_PARAMS;
}

@Override
public boolean canTripCircuitBreaker() {
return false;
}

RestResponseListener<HealthStatsResponse> healthStatsResponse(final RestChannel channel) {
return new RestResponseListener<>(channel) {
@Override
public RestResponse buildResponse(final HealthStatsResponse response) throws Exception {
return new BytesRestResponse(RestStatus.OK, response.toXContent(channel.newBuilder(), ToXContent.EMPTY_PARAMS));
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

/**
* Rest Handlers for Query Insights Health Stats
*/
package org.opensearch.plugin.insights.rules.resthandler.health_stats;
Loading

0 comments on commit ec73491

Please sign in to comment.