Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record more detailed HTTP stats #99852

Merged
merged 25 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c22fe4a
WIP
ywangd Sep 25, 2023
56d857a
Update docs/changelog/99852.yaml
ywangd Sep 25, 2023
2a4f24a
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Sep 26, 2023
32a9e3a
fix test
ywangd Sep 26, 2023
e669041
add response time
ywangd Sep 26, 2023
2873701
add merge and info stats support
ywangd Sep 26, 2023
4504e52
tests tweak
ywangd Sep 26, 2023
5c1fdb7
wrap chunked body and report size on close
ywangd Sep 27, 2023
4a4cd4a
use nanoTime
ywangd Sep 27, 2023
0d207b0
add tests for httpRouteStats
ywangd Sep 27, 2023
860341e
add tests for httpRouteStats
ywangd Sep 27, 2023
971f047
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Sep 27, 2023
5849e3d
address feedback
ywangd Sep 27, 2023
4b2e084
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Sep 27, 2023
49b95f8
upgrade encoded response length to long
ywangd Sep 27, 2023
9f84029
fix test
ywangd Sep 27, 2023
589b35d
assertion
ywangd Sep 27, 2023
159ff77
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Oct 3, 2023
f04e5af
Merge branch 'main' into es-95739
elasticmachine Oct 3, 2023
3a62b0d
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Oct 4, 2023
55fc8d3
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Oct 11, 2023
345623a
Update qa/smoke-test-http/src/javaRestTest/java/org/elasticsearch/htt…
ywangd Oct 12, 2023
a4355c4
fix import
ywangd Oct 12, 2023
bfb00c8
Merge remote-tracking branch 'origin/main' into es-95739
ywangd Oct 12, 2023
bd4ef2c
tweak
ywangd Oct 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/99852.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 99852
summary: Record more detailed HTTP stats
area: Network
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import static org.elasticsearch.cluster.metadata.IndexGraveyard.SETTING_MAX_TOMBSTONES;
import static org.elasticsearch.indices.IndicesService.WRITE_DANGLING_INDICES_INFO_SETTING;
import static org.elasticsearch.rest.RestStatus.ACCEPTED;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.test.XContentTestUtils.createJsonMapView;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
Expand Down Expand Up @@ -184,10 +183,6 @@ private List<String> listDanglingIndexIds() throws IOException {
return danglingIndexIds;
}

private void assertOK(Response response) {
assertThat(response.getStatusLine().getStatusCode(), equalTo(OK.getStatus()));
}

/**
* Given a node name, finds the corresponding node ID.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
package org.elasticsearch.http;

import org.elasticsearch.client.Response;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.plugins.Plugin;
Expand All @@ -17,6 +18,8 @@
import java.util.Collection;
import java.util.List;

import static org.hamcrest.Matchers.oneOf;

public abstract class HttpSmokeTestCase extends ESIntegTestCase {

@Override
Expand All @@ -42,4 +45,8 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
protected boolean ignoreExternalCluster() {
return true;
}

public static void assertOK(Response response) {
assertThat(response.getStatusLine().getStatusCode(), oneOf(200, 201));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.http;

import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.XContentTestUtils;
import org.elasticsearch.xcontent.json.JsonXContent;

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

import static org.hamcrest.Matchers.aMapWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;

@ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 0, numClientNodes = 0)
public class HttpStatsIT extends HttpSmokeTestCase {

@SuppressWarnings("unchecked")
public void testNodeHttpStats() throws IOException {
internalCluster().startNode();
performHttpRequests();

final Response response = getRestClient().performRequest(new Request("GET", "/_nodes/stats/http"));
assertOK(response);

final Map<String, Object> responseMap = XContentHelper.convertToMap(
JsonXContent.jsonXContent,
response.getEntity().getContent(),
false
);
final Map<String, Object> nodesMap = (Map<String, Object>) responseMap.get("nodes");

assertThat(nodesMap, aMapWithSize(1));
final String nodeId = nodesMap.keySet().iterator().next();

assertHttpStats(new XContentTestUtils.JsonMapView((Map<String, Object>) nodesMap.get(nodeId)));
}

@SuppressWarnings("unchecked")
public void testClusterInfoHttpStats() throws IOException {
internalCluster().ensureAtLeastNumDataNodes(3);
performHttpRequests();

final Response response = getRestClient().performRequest(new Request("GET", "/_info/http"));
assertOK(response);

final Map<String, Object> responseMap = XContentHelper.convertToMap(
JsonXContent.jsonXContent,
response.getEntity().getContent(),
false
);
assertHttpStats(new XContentTestUtils.JsonMapView(responseMap));
}

private void performHttpRequests() throws IOException {
// basic request
final RestClient restClient = getRestClient();
assertOK(restClient.performRequest(new Request("GET", "/")));
// request with body and URL placeholder
final Request searchRequest = new Request("GET", "*/_search");
searchRequest.setJsonEntity("""
{"query":{"match_all":{}}}""");
assertOK(restClient.performRequest(searchRequest));
// chunked response
assertOK(restClient.performRequest(new Request("GET", "/_cluster/state")));
// chunked text response
assertOK(restClient.performRequest(new Request("GET", "/_cat/nodes")));
}

private void assertHttpStats(XContentTestUtils.JsonMapView jsonMapView) {
final List<String> routes = List.of("/", "/_cat/nodes", "/{index}/_search", "/_cluster/state");

for (var route : routes) {
assertThat(route, jsonMapView.get("http.routes." + route), notNullValue());
assertThat(route, jsonMapView.get("http.routes." + route + ".requests.count"), equalTo(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".requests.total_size_in_bytes"), greaterThanOrEqualTo(0));
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.count"), equalTo(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.total_size_in_bytes"), greaterThan(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".requests.size_histogram"), hasSize(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".requests.size_histogram.0.count"), equalTo(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".requests.size_histogram.0.lt_bytes"), notNullValue());
if (route.equals("/{index}/_search")) {
assertThat(route, jsonMapView.get("http.routes." + route + ".requests.size_histogram.0.ge_bytes"), notNullValue());
}
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.size_histogram"), hasSize(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.size_histogram.0.count"), equalTo(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.size_histogram.0.lt_bytes"), notNullValue());
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.size_histogram.0.ge_bytes"), notNullValue());
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.handling_time_histogram"), hasSize(1));
assertThat(route, jsonMapView.get("http.routes." + route + ".responses.handling_time_histogram.0.count"), equalTo(1));
final int ltMillis = jsonMapView.get("http.routes." + route + ".responses.handling_time_histogram.0.lt_millis");
assertThat(route, ltMillis, notNullValue());
assertThat(
route,
jsonMapView.get("http.routes." + route + ".responses.handling_time_histogram.0.ge_millis"),
ltMillis > 1 ? notNullValue() : nullValue()
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ static TransportVersion def(int id) {
public static final TransportVersion PLUGIN_DESCRIPTOR_OPTIONAL_CLASSNAME = def(8_513_00_0);
public static final TransportVersion UNIVERSAL_PROFILING_LICENSE_ADDED = def(8_514_00_0);
public static final TransportVersion ELSER_SERVICE_MODEL_VERSION_ADDED = def(8_515_00_0);
public static final TransportVersion NODE_STATS_HTTP_ROUTE_STATS_ADDED = def(8_516_00_0);
/*
* STOP! READ THIS FIRST! No, really,
* ____ _____ ___ ____ _ ____ _____ _ ____ _____ _ _ ___ ____ _____ ___ ____ ____ _____ _
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

package org.elasticsearch.common.path;

import org.elasticsearch.common.collect.Iterators;

import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
Expand Down Expand Up @@ -267,6 +269,15 @@ private void put(Map<String, String> params, TrieNode node, String value) {
}
}

Iterator<T> allNodeValues() {
final Iterator<T> childrenIterator = Iterators.flatMap(children.values().iterator(), TrieNode::allNodeValues);
if (value == null) {
return childrenIterator;
} else {
return Iterators.concat(Iterators.single(value), childrenIterator);
}
}

@Override
public String toString() {
return key;
Expand Down Expand Up @@ -366,4 +377,8 @@ public T next() {
}
};
}

public Iterator<T> allNodeValues() {
return Iterators.concat(Iterators.single(rootValue), root.allNodeValues());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,12 @@ public HttpInfo info() {

@Override
public HttpStats stats() {
return new HttpStats(httpChannels.size(), totalChannelsAccepted.get(), httpClientStatsTracker.getClientStats());
return new HttpStats(
httpChannels.size(),
totalChannelsAccepted.get(),
httpClientStatsTracker.getClientStats(),
dispatcher.getStats()
);
}

protected void bindServer() {
Expand Down
Loading