[Enhancement] Implement ProfileActionV2 and QueryDetailActionV2 to obtain query information across all FEs (#61345)
Signed-off-by: zhaohehuhu <luoyedeyi@163.com>
This commit is contained in:
parent
4bb51cbd9e
commit
a79d85173b
|
|
@ -132,6 +132,20 @@ public class BaseRequest {
|
|||
return params.get(key);
|
||||
}
|
||||
|
||||
public String getSingleParameter(String key, String defaultValue) {
|
||||
String uri = request.uri();
|
||||
if (decoder == null) {
|
||||
decoder = new QueryStringDecoder(uri);
|
||||
}
|
||||
|
||||
List<String> values = decoder.parameters().get(key);
|
||||
if (values != null && !values.isEmpty()) {
|
||||
return values.get(0);
|
||||
}
|
||||
|
||||
return params.get(key) != null ? params.get(key) : defaultValue;
|
||||
}
|
||||
|
||||
public String getContent() throws DdlException {
|
||||
if (request instanceof FullHttpRequest) {
|
||||
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,8 @@ import com.starrocks.http.rest.TableRowCountAction;
|
|||
import com.starrocks.http.rest.TableSchemaAction;
|
||||
import com.starrocks.http.rest.TransactionLoadAction;
|
||||
import com.starrocks.http.rest.TriggerAction;
|
||||
import com.starrocks.http.rest.v2.ProfileActionV2;
|
||||
import com.starrocks.http.rest.v2.QueryDetailActionV2;
|
||||
import com.starrocks.http.rest.v2.TablePartitionAction;
|
||||
import com.starrocks.metric.GaugeMetric;
|
||||
import com.starrocks.metric.GaugeMetricImpl;
|
||||
|
|
@ -223,8 +225,10 @@ public class HttpServer {
|
|||
ColocateMetaService.UpdateGroupAction.registerAction(controller);
|
||||
GlobalDictMetaService.ForbitTableAction.registerAction(controller);
|
||||
ProfileAction.registerAction(controller);
|
||||
ProfileActionV2.registerAction(controller);
|
||||
QueryProgressAction.registerAction(controller);
|
||||
QueryDetailAction.registerAction(controller);
|
||||
QueryDetailActionV2.registerAction(controller);
|
||||
ConnectionAction.registerAction(controller);
|
||||
ShowDataAction.registerAction(controller);
|
||||
QueryDumpAction.registerAction(controller);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.http;
|
||||
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.config.AuthSchemes;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||
import org.apache.http.entity.AbstractHttpEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpUtils.class);
|
||||
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final CloseableHttpClient INSTANCE = getHttpClient();
|
||||
}
|
||||
|
||||
public static CloseableHttpClient getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static final PoolingHttpClientConnectionManager CLIENT_CONNECTION_MANAGER;
|
||||
|
||||
private static CloseableHttpClient getHttpClient() {
|
||||
Objects.requireNonNull(CLIENT_CONNECTION_MANAGER, "clientConnectionManager is not initialized");
|
||||
|
||||
RequestConfig requestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES)
|
||||
.setExpectContinueEnabled(Boolean.TRUE)
|
||||
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST, AuthSchemes.SPNEGO))
|
||||
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC, AuthSchemes.SPNEGO))
|
||||
.setConnectTimeout(5000)
|
||||
.setSocketTimeout(5000)
|
||||
.setConnectionRequestTimeout(5000)
|
||||
.setRedirectsEnabled(true)
|
||||
.build();
|
||||
|
||||
return HttpClients.custom()
|
||||
.setConnectionManager(CLIENT_CONNECTION_MANAGER)
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setRetryHandler(new DefaultHttpRequestRetryHandler(5, false))
|
||||
.build();
|
||||
}
|
||||
|
||||
static {
|
||||
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = null;
|
||||
try {
|
||||
SSLContextBuilder builder = new SSLContextBuilder();
|
||||
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
|
||||
SSLContext sslContext = builder.build();
|
||||
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||
Registry<ConnectionSocketFactory> socketFactoryRegistry =
|
||||
RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("http", PlainConnectionSocketFactory.getSocketFactory())
|
||||
.register("https", socketFactory)
|
||||
.build();
|
||||
poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
|
||||
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(50);
|
||||
poolingHttpClientConnectionManager.setMaxTotal(100);
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOG.error("Got NoSuchAlgorithmException when SSLContext init", e);
|
||||
} catch (KeyManagementException e) {
|
||||
LOG.error("Got KeyManagementException when SSLContext init", e);
|
||||
} catch (KeyStoreException e) {
|
||||
LOG.error("Got KeyStoreException when SSLContext init", e);
|
||||
}
|
||||
CLIENT_CONNECTION_MANAGER = poolingHttpClientConnectionManager;
|
||||
LOG.info(" initial http client successfully");
|
||||
}
|
||||
|
||||
public static String get(String uri, Map<String, String> header) throws Exception {
|
||||
HttpGet httpGet = new HttpGet(uri);
|
||||
addHeaders(httpGet, header);
|
||||
return executeRequest(uri, httpGet, null);
|
||||
}
|
||||
|
||||
public static String post(String uri, AbstractHttpEntity entity, Map<String, String> header) throws Exception {
|
||||
HttpPost httpPost = new HttpPost(uri);
|
||||
httpPost.setEntity(entity);
|
||||
addHeaders(httpPost, header);
|
||||
return executeRequest(uri, httpPost, entity);
|
||||
}
|
||||
|
||||
private static void addHeaders(HttpRequestBase request, Map<String, String> headers) {
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
headers.forEach(request::addHeader);
|
||||
}
|
||||
}
|
||||
|
||||
private static String executeRequest(String uri,
|
||||
HttpUriRequest request,
|
||||
AbstractHttpEntity entity) throws HttpRequestException {
|
||||
CloseableHttpClient httpClient = getInstance();
|
||||
if (Objects.isNull(httpClient)) {
|
||||
LOG.error("HttpClient is null for uri: {}", uri);
|
||||
throw new HttpRequestException("HttpClient is null for uri: " + uri);
|
||||
}
|
||||
try (CloseableHttpResponse response = httpClient.execute(request)) {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
String result = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
if (code == HttpStatus.SC_OK) {
|
||||
return result;
|
||||
} else {
|
||||
if (entity != null) {
|
||||
String msg = String.format("Http request failed, url=%s, code=%d, body=%s, response=%s",
|
||||
uri, code, entity, result);
|
||||
LOG.error(msg);
|
||||
throw new HttpRequestException(msg);
|
||||
} else {
|
||||
String msg = String.format("Http request failed, url=%s, code=%d, response=%s",
|
||||
uri, code, result);
|
||||
LOG.error(msg);
|
||||
throw new HttpRequestException(msg);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
String msg = "Http request exception, uri=" + uri;
|
||||
LOG.error(msg, e);
|
||||
throw new HttpRequestException(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -37,9 +37,12 @@ package com.starrocks.http.rest;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.starrocks.authorization.AccessDeniedException;
|
||||
import com.starrocks.authorization.AuthorizationMgr;
|
||||
import com.starrocks.catalog.UserIdentity;
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.DdlException;
|
||||
import com.starrocks.common.ErrorCode;
|
||||
import com.starrocks.common.Pair;
|
||||
|
|
@ -50,13 +53,18 @@ import com.starrocks.http.BaseAction;
|
|||
import com.starrocks.http.BaseRequest;
|
||||
import com.starrocks.http.BaseResponse;
|
||||
import com.starrocks.http.HttpConnectContext;
|
||||
import com.starrocks.http.HttpUtils;
|
||||
import com.starrocks.http.WebUtils;
|
||||
import com.starrocks.qe.ConnectContext;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.system.Frontend;
|
||||
import com.starrocks.thrift.TNetworkAddress;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
|
@ -65,6 +73,7 @@ import java.net.URISyntaxException;
|
|||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RestBaseAction extends BaseAction {
|
||||
|
||||
|
|
@ -292,4 +301,88 @@ public class RestBaseAction extends BaseAction {
|
|||
});
|
||||
}
|
||||
|
||||
public static List<String> fetchResultFromOtherFrontendNodes(String queryPath,
|
||||
String authorization,
|
||||
HttpMethod method,
|
||||
Boolean returnMultipleResults) {
|
||||
List<Pair<String, Integer>> frontends = getOtherAliveFe();
|
||||
if (frontends.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
ImmutableMap<String, String> header = ImmutableMap.<String, String>builder()
|
||||
.put(HttpHeaders.AUTHORIZATION, authorization).build();
|
||||
List<String> result = Lists.newArrayList();
|
||||
for (Pair<String, Integer> front : frontends) {
|
||||
String url = String.format("http://%s:%d%s", front.first, front.second, queryPath);
|
||||
try {
|
||||
String data = null;
|
||||
if (method == HttpMethod.GET) {
|
||||
data = HttpUtils.get(url, header);
|
||||
} else if (method == HttpMethod.POST) {
|
||||
data = HttpUtils.post(url, null, header);
|
||||
}
|
||||
if (StringUtils.isNotBlank(data)) {
|
||||
result.add(data);
|
||||
}
|
||||
if (!returnMultipleResults && !result.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("request url {} error", url, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<Pair<String, Integer>> getAllAliveFe() {
|
||||
|
||||
if (GlobalStateMgr.getCurrentState() == null) {
|
||||
return List.of();
|
||||
} else {
|
||||
return GlobalStateMgr.getCurrentState()
|
||||
.getNodeMgr()
|
||||
.getAllFrontends()
|
||||
.stream()
|
||||
.filter(Frontend::isAlive)
|
||||
.map(fe -> new Pair<>(fe.getHost(), Config.http_port))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public static Pair<String, Integer> getCurrentFe() {
|
||||
if (GlobalStateMgr.getCurrentState() == null) {
|
||||
return null;
|
||||
} else {
|
||||
return GlobalStateMgr.getCurrentState()
|
||||
.getNodeMgr()
|
||||
.getSelfNode();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Pair<String, Integer>> getOtherAliveFe() {
|
||||
List<Pair<String, Integer>> allAliveFe = getAllAliveFe();
|
||||
if (allAliveFe.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
Pair<String, Integer> currentFe = getCurrentFe();
|
||||
if (currentFe == null) {
|
||||
return List.of();
|
||||
}
|
||||
String currentFeAddress = currentFe.first;
|
||||
return allAliveFe.stream()
|
||||
.filter(fe -> !fe.first.equals(currentFeAddress))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
protected void sendSuccessResponse(BaseResponse response, String content, BaseRequest request) {
|
||||
response.getContent().append(content);
|
||||
sendResult(request, response);
|
||||
}
|
||||
|
||||
protected void sendErrorResponse(BaseResponse response, String message, HttpResponseStatus status, BaseRequest request) {
|
||||
response.getContent().append(message);
|
||||
sendResult(request, response, status);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.http.rest.v2;
|
||||
|
||||
import com.starrocks.common.util.ProfileManager;
|
||||
import com.starrocks.http.ActionController;
|
||||
import com.starrocks.http.BaseRequest;
|
||||
import com.starrocks.http.BaseResponse;
|
||||
import com.starrocks.http.IllegalArgException;
|
||||
import com.starrocks.http.rest.RestBaseAction;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// This class is a RESTFUL interface to get query profile from all frontend nodes.
|
||||
// Usage:
|
||||
// wget http://fe_host:fe_http_port/api/v2/profile?query_id=123456&is_request_all_frontend=true;
|
||||
public class ProfileActionV2 extends RestBaseAction {
|
||||
|
||||
private static final String QUERY_PLAN_URI = "/api/v2/profile?query_id=%s";
|
||||
|
||||
public ProfileActionV2(ActionController controller) {
|
||||
super(controller);
|
||||
}
|
||||
|
||||
public static void registerAction(ActionController controller) throws IllegalArgException {
|
||||
controller.registerHandler(HttpMethod.GET, "/api/v2/profile", new ProfileActionV2(controller));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeWithoutPassword(BaseRequest request, BaseResponse response) {
|
||||
String authorization = request.getAuthorizationHeader();
|
||||
String queryId = request.getSingleParameter("query_id");
|
||||
String isRequestAllStr = request.getSingleParameter("is_request_all_frontend", "false");
|
||||
boolean isRequestAll = Boolean.parseBoolean(isRequestAllStr);
|
||||
|
||||
if (queryId == null || queryId.isEmpty()) {
|
||||
sendErrorResponse(response,
|
||||
"Invalid parameter: query_id",
|
||||
HttpResponseStatus.BAD_REQUEST,
|
||||
request);
|
||||
return;
|
||||
}
|
||||
|
||||
String queryProfileStr = ProfileManager.getInstance().getProfile(queryId);
|
||||
|
||||
if (queryProfileStr != null) {
|
||||
sendSuccessResponse(response, queryProfileStr, request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRequestAll) {
|
||||
// If the query profile is not found in the local fe's ProfileManager,
|
||||
// we will query other frontend nodes to get the query profile.
|
||||
String queryPath = String.format(QUERY_PLAN_URI, queryId);
|
||||
List<String> profileList = fetchResultFromOtherFrontendNodes(queryPath, authorization, HttpMethod.GET, false);
|
||||
for (String profile : profileList) {
|
||||
if (profile != null) {
|
||||
sendSuccessResponse(response, profile, request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendErrorResponse(response,
|
||||
String.format("Query id %s not found.", queryId),
|
||||
HttpResponseStatus.NOT_FOUND,
|
||||
request);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.http.rest.v2;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.starrocks.http.ActionController;
|
||||
import com.starrocks.http.BaseRequest;
|
||||
import com.starrocks.http.BaseResponse;
|
||||
import com.starrocks.http.IllegalArgException;
|
||||
import com.starrocks.http.rest.RestBaseAction;
|
||||
import com.starrocks.qe.QueryDetail;
|
||||
import com.starrocks.qe.QueryDetailQueue;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
// This class is a RESTFUL interface to get query profile from all frontend nodes.
|
||||
// Usage:
|
||||
// wget http://fe_host:fe_http_port/api/v2/query_detail?user=root&event_time=1753671088&is_request_all_frontend=true;
|
||||
public class QueryDetailActionV2 extends RestBaseAction {
|
||||
|
||||
private static final String QUERY_PLAN_URI = "/api/v2/query_detail";
|
||||
|
||||
public QueryDetailActionV2(ActionController controller) {
|
||||
super(controller);
|
||||
}
|
||||
|
||||
public static void registerAction(ActionController controller) throws IllegalArgException {
|
||||
controller.registerHandler(HttpMethod.GET, QUERY_PLAN_URI, new QueryDetailActionV2(controller));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeWithoutPassword(BaseRequest request, BaseResponse response) {
|
||||
String authorization = request.getAuthorizationHeader();
|
||||
String eventTimeStr = request.getSingleParameter("event_time");
|
||||
String user = request.getSingleParameter("user");
|
||||
String isRequestAllStr = request.getSingleParameter("is_request_all_frontend", "false");
|
||||
boolean isRequestAll = Boolean.parseBoolean(isRequestAllStr);
|
||||
|
||||
if (eventTimeStr == null || eventTimeStr.isEmpty()) {
|
||||
sendErrorResponse(response,
|
||||
"not valid parameter",
|
||||
HttpResponseStatus.BAD_REQUEST,
|
||||
request);
|
||||
return;
|
||||
}
|
||||
|
||||
long eventTime = Long.parseLong(eventTimeStr.trim());
|
||||
|
||||
List<QueryDetail> queryDetails;
|
||||
if (user != null && !user.isEmpty()) {
|
||||
// If user is specified, we will filter the query details by user.
|
||||
queryDetails = QueryDetailQueue.getQueryDetailsAfterTime(eventTime, user);
|
||||
} else {
|
||||
// If user is not specified, we will get all query details after the specified event time
|
||||
queryDetails = QueryDetailQueue.getQueryDetailsAfterTime(eventTime);
|
||||
}
|
||||
|
||||
if (isRequestAll) {
|
||||
// If the query profile is not found in the local fe's ProfileManager,
|
||||
// we will query other frontend nodes to get the query profile.
|
||||
String queryPath = QUERY_PLAN_URI + "?event_time=" + eventTimeStr;
|
||||
if (user != null && !user.isEmpty()) {
|
||||
queryPath += "&user=" + user;
|
||||
}
|
||||
List<String> dataList = fetchResultFromOtherFrontendNodes(queryPath, authorization, HttpMethod.GET, true);
|
||||
if (dataList != null) {
|
||||
for (String data : dataList) {
|
||||
if (data != null) {
|
||||
Gson gson = new Gson();
|
||||
QueryDetail[] queryDetailArray = gson.fromJson(data, QueryDetail[].class);
|
||||
queryDetails.addAll(Arrays.asList(queryDetailArray));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
String jsonString = gson.toJson(queryDetails);
|
||||
response.getContent().append(jsonString);
|
||||
sendResult(request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -82,6 +82,16 @@ public class QueryDetailQueue {
|
|||
return results;
|
||||
}
|
||||
|
||||
public static List<QueryDetail> getQueryDetailsAfterTime(long eventTime, String user) {
|
||||
List<QueryDetail> results = Lists.newArrayList();
|
||||
for (QueryDetail queryDetail : TOTAL_QUERIES) {
|
||||
if (queryDetail.getEventTime() > eventTime && queryDetail.getUser().equalsIgnoreCase(user)) {
|
||||
results.add(queryDetail);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static long getTotalQueriesCount() {
|
||||
return TOTAL_QUERIES.size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.http;
|
||||
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpUtilsTest extends StarRocksHttpTestCase {
|
||||
|
||||
private static final String QUERY_PLAN_URI = "/system";
|
||||
|
||||
@Test
|
||||
public void testGetHttpClient() {
|
||||
CloseableHttpClient httpClient1 = HttpUtils.getInstance();
|
||||
CloseableHttpClient httpClient2 = HttpUtils.getInstance();
|
||||
Assertions.assertEquals(httpClient1, httpClient2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpGet() throws Exception {
|
||||
Map<String, String> header = Map.of(HttpHeaders.AUTHORIZATION, rootAuth);
|
||||
String url = "http://localhost:" + HTTP_PORT + QUERY_PLAN_URI + "?path=/backends";
|
||||
String result = HttpUtils.get(url, header);
|
||||
Assertions.assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpPostFails() {
|
||||
Map<String, String> header = Map.of(HttpHeaders.AUTHORIZATION, rootAuth);
|
||||
String url = URI + "/_query_plan";
|
||||
StringEntity entity = new StringEntity(
|
||||
"{ \"sql\" : \" select k1 as alias_1,k2 from " + DB_NAME + "." + TABLE_NAME + " \" }",
|
||||
StandardCharsets.UTF_8);
|
||||
Assertions.assertThrows(HttpRequestException.class, () -> {
|
||||
HttpUtils.post(url, entity, header);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpPostFailsWithoutEntity() {
|
||||
|
||||
Map<String, String> header = Map.of(HttpHeaders.AUTHORIZATION, rootAuth);
|
||||
String url = URI + "/_query_plan";
|
||||
StringEntity entity = null;
|
||||
Assertions.assertThrows(HttpRequestException.class, () -> {
|
||||
HttpUtils.post(url, entity, header);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpPostFailsWithIOException() {
|
||||
|
||||
new MockUp<CloseableHttpClient>() {
|
||||
@Mock
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpUriRequest request) throws IOException, ClientProtocolException {
|
||||
throw new IOException("http execute failed");
|
||||
}
|
||||
};
|
||||
|
||||
Map<String, String> header = Map.of(HttpHeaders.AUTHORIZATION, rootAuth);
|
||||
String url = URI + "/_query_plan";
|
||||
StringEntity entity = null;
|
||||
Assertions.assertThrows(HttpRequestException.class, () -> {
|
||||
HttpUtils.post(url, entity, header);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpPostFailsWhenHttpClientUnavailable() {
|
||||
|
||||
new MockUp<HttpUtils>() {
|
||||
@Mock
|
||||
public static CloseableHttpClient getInstance() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Map<String, String> header = Map.of(HttpHeaders.AUTHORIZATION, rootAuth);
|
||||
String url = URI + "/_query_plan";
|
||||
StringEntity entity = new StringEntity(
|
||||
"{ \"sql\" : \" select k1 as alias_1,k2 from " + DB_NAME + "." + TABLE_NAME + " \" }",
|
||||
StandardCharsets.UTF_8);
|
||||
Assertions.assertThrows(HttpRequestException.class, () -> {
|
||||
HttpUtils.post(url, entity, header);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInstance() {
|
||||
CloseableHttpClient client1 = HttpUtils.getInstance();
|
||||
CloseableHttpClient client2 = HttpUtils.getInstance();
|
||||
Assertions.assertSame(client1, client2);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,13 @@
|
|||
|
||||
package com.starrocks.http;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.Pair;
|
||||
import com.starrocks.ha.FrontendNodeType;
|
||||
import com.starrocks.http.rest.RestBaseAction;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.system.Frontend;
|
||||
import com.starrocks.thrift.TNetworkAddress;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
|
|
@ -23,10 +29,15 @@ import io.netty.handler.codec.http.HttpHeaders;
|
|||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import mockit.Expectations;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
|
@ -85,4 +96,81 @@ public class RestBaseActionTest {
|
|||
restBaseAction.redirectTo(mockRequest, mockResponse, mockAddr);
|
||||
verify(mockResponse).updateHeader(HttpHeaderNames.LOCATION.toString(), asciiUri);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetOtherAliveFronts() {
|
||||
new MockUp<RestBaseAction>() {
|
||||
@Mock
|
||||
public static List<Pair<String, Integer>> getAllAliveFe() {
|
||||
return List.of(
|
||||
new Pair<>("fe1", 8030),
|
||||
new Pair<>("fe2", 8031)
|
||||
);
|
||||
}
|
||||
|
||||
@Mock
|
||||
public static Pair<String, Integer> getCurrentFe() {
|
||||
return new Pair<>("fe1", 8030);
|
||||
}
|
||||
};
|
||||
|
||||
List<Pair<String, Integer>> fronts = restBaseAction.getOtherAliveFe();
|
||||
|
||||
Assertions.assertEquals(fronts.size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyGetEmptyOtherAliveFronts() {
|
||||
|
||||
new Expectations(GlobalStateMgr.getCurrentState().getNodeMgr()) {
|
||||
{
|
||||
|
||||
GlobalStateMgr.getCurrentState();
|
||||
minTimes = 1;
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
List<Pair<String, Integer>> fronts = restBaseAction.getOtherAliveFe();
|
||||
|
||||
Assertions.assertEquals(fronts.size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyGetNullCurrentFe() {
|
||||
|
||||
new Expectations(GlobalStateMgr.getCurrentState()) {
|
||||
{
|
||||
|
||||
GlobalStateMgr.getCurrentState();
|
||||
minTimes = 1;
|
||||
result = null;
|
||||
}
|
||||
};
|
||||
|
||||
Pair<String, Integer> currentFe = restBaseAction.getCurrentFe();
|
||||
|
||||
Assertions.assertNull(currentFe);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllAliveFe() {
|
||||
Frontend frontend = new Frontend(0, FrontendNodeType.LEADER, "", "localhost", 0);
|
||||
frontend.setAlive(true);
|
||||
new Expectations(GlobalStateMgr.getCurrentState().getNodeMgr()) {
|
||||
{
|
||||
GlobalStateMgr.getCurrentState().getNodeMgr().getAllFrontends();
|
||||
minTimes = 1;
|
||||
result = Lists.newArrayList(frontend);
|
||||
}
|
||||
};
|
||||
|
||||
List<Pair<String, Integer>> result = restBaseAction.getAllAliveFe();
|
||||
|
||||
Assertions.assertEquals(1, result.size());
|
||||
Assertions.assertEquals(frontend.getHost(), result.get(0).first);
|
||||
Assertions.assertEquals(Config.http_port, result.get(0).second);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -545,7 +545,8 @@ public abstract class StarRocksHttpTestCase {
|
|||
}
|
||||
};
|
||||
|
||||
Frontend frontend = new Frontend(0, FrontendNodeType.LEADER, "", "", 0);
|
||||
Frontend frontend = new Frontend(0, FrontendNodeType.LEADER, "", "localhost", 0);
|
||||
frontend.setAlive(true);
|
||||
new Expectations(nodeMgr) {
|
||||
{
|
||||
nodeMgr.getClusterInfo();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.http.rest.v2;
|
||||
|
||||
import com.starrocks.common.Pair;
|
||||
import com.starrocks.common.util.ProfileManager;
|
||||
import com.starrocks.ha.FrontendNodeType;
|
||||
import com.starrocks.http.StarRocksHttpTestCase;
|
||||
import com.starrocks.http.rest.RestBaseAction;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.system.Frontend;
|
||||
import mockit.Expectations;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ProfileActionV2Test extends StarRocksHttpTestCase {
|
||||
|
||||
private static final String QUERY_PLAN_URI = "/api/v2/profile";
|
||||
|
||||
@Test
|
||||
public void testQueryProfile() throws IOException {
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.addHeader("Authorization", rootAuth)
|
||||
.url("http://localhost:" + HTTP_PORT + QUERY_PLAN_URI + "?query_id=eaff21d2-3734-11ee-909f-8e20563011de")
|
||||
.build();
|
||||
Response response = networkClient.newCall(request).execute();
|
||||
String respStr = response.body().string();
|
||||
Assertions.assertTrue(respStr.contains("Query id eaff21d2-3734-11ee-909f-8e20563011de not found."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryProfileFromLeaderFront() throws Exception {
|
||||
|
||||
new MockUp<ProfileManager>() {
|
||||
@Mock
|
||||
public String getProfile(String queryId) {
|
||||
if (queryId.equalsIgnoreCase("eaff21d2-3734-11ee-909f-8e20563011de")) {
|
||||
String queryProfileStr = "Query:\n" +
|
||||
" Summary:\n" +
|
||||
" - Query ID: eaff21d2-3734-11ee-909f-8e20563011de\n" +
|
||||
" - Start Time: 2023-08-10 12:18:11\n" +
|
||||
" - End Time: 2023-08-10 12:18:11\n" +
|
||||
" - Total: 150ms\n" +
|
||||
" - Query Type: Query\n" +
|
||||
" - Query State: Finished\n" +
|
||||
" - StarRocks Version: bugfix2-0da335ff34\n" +
|
||||
" - User: root\n" +
|
||||
" - Test: a<b<c\n" +
|
||||
" - Default Db: ssb\n" +
|
||||
" - Sql Statement: select count(s_suppkey), count(s_name), count(s_address), count(s_city), " +
|
||||
"count(s_nation), count(s_region), count(s_phone), count(lo_revenue), count(lo_shipmode), " +
|
||||
"count(lo_quantity), count(lo_partkey), count(lo_discount) from lineorder join supplier on " +
|
||||
"lo_suppkey=s_suppkey and lo_partkey<s_suppkey and lo_quantity>100\n" +
|
||||
" - Variables: parallel_fragment_exec_instance_num=1,max_parallel_scan_instance_num=-1," +
|
||||
"pipeline_dop=0,enable_adaptive_sink_dop=true,enable_runtime_adaptive_dop=false," +
|
||||
"runtime_profile_report_interval=10\n" +
|
||||
" - Collect Profile Time: 41ms";
|
||||
return queryProfileStr;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.addHeader("Authorization", rootAuth)
|
||||
.url("http://localhost:" + HTTP_PORT + QUERY_PLAN_URI + "?query_id=eaff21d2-3734-11ee-909f-8e20563011de"
|
||||
+ "&is_request_all_frontend=true")
|
||||
.build();
|
||||
Response response = networkClient.newCall(request).execute();
|
||||
String respStr = response.body().string();
|
||||
Assertions.assertTrue(respStr.contains("Query ID: eaff21d2-3734-11ee-909f-8e20563011de"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryProfileFromFronts() throws Exception {
|
||||
|
||||
Frontend frontend = new Frontend(0, FrontendNodeType.LEADER, "", "localhost", 0);
|
||||
|
||||
new Expectations(GlobalStateMgr.getCurrentState().getNodeMgr()) {
|
||||
{
|
||||
|
||||
GlobalStateMgr.getCurrentState().getNodeMgr().getSelfNode();
|
||||
minTimes = 1;
|
||||
result = new Pair<>(frontend.getHost(), HTTP_PORT);
|
||||
}
|
||||
};
|
||||
|
||||
new MockUp<RestBaseAction>() {
|
||||
@Mock
|
||||
public static List<Pair<String, Integer>> getOtherAliveFe() {
|
||||
Pair<String, Integer> frontNode = GlobalStateMgr.getCurrentState()
|
||||
.getNodeMgr()
|
||||
.getSelfNode();
|
||||
List<Pair<String, Integer>> frontNodes = new ArrayList<>();
|
||||
frontNodes.add(frontNode);
|
||||
return frontNodes;
|
||||
}
|
||||
};
|
||||
|
||||
new MockUp<ProfileManager>() {
|
||||
int callCount = 0;
|
||||
@Mock
|
||||
public String getProfile(String queryId) {
|
||||
|
||||
if (callCount <= 0) {
|
||||
callCount++;
|
||||
// Simulate that the profile is not found in the local ProfileManager
|
||||
return null;
|
||||
}
|
||||
|
||||
String queryProfileStr = "Query:\n" +
|
||||
" Summary:\n" +
|
||||
" - Query ID: eaff21d2-3734-11ee-909f-8e20563011de\n" +
|
||||
" - Start Time: 2023-08-10 12:18:11\n" +
|
||||
" - End Time: 2023-08-10 12:18:11\n" +
|
||||
" - Total: 150ms\n" +
|
||||
" - Query Type: Query\n" +
|
||||
" - Query State: Finished\n" +
|
||||
" - StarRocks Version: bugfix2-0da335ff34\n" +
|
||||
" - User: root\n" +
|
||||
" - Test: a<b<c\n" +
|
||||
" - Default Db: ssb\n" +
|
||||
" - Sql Statement: select count(s_suppkey), count(s_name), count(s_address), count(s_city), " +
|
||||
"count(s_nation), count(s_region), count(s_phone), count(lo_revenue), count(lo_shipmode), " +
|
||||
"count(lo_quantity), count(lo_partkey), count(lo_discount) from lineorder join supplier on " +
|
||||
"lo_suppkey=s_suppkey and lo_partkey<s_suppkey and lo_quantity>100\n" +
|
||||
" - Variables: parallel_fragment_exec_instance_num=1,max_parallel_scan_instance_num=-1," +
|
||||
"pipeline_dop=0,enable_adaptive_sink_dop=true,enable_runtime_adaptive_dop=false," +
|
||||
"runtime_profile_report_interval=10\n" +
|
||||
" - Collect Profile Time: 41ms";
|
||||
return queryProfileStr;
|
||||
}
|
||||
};
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.addHeader("Authorization", rootAuth)
|
||||
.url("http://localhost:" + HTTP_PORT + QUERY_PLAN_URI + "?query_id=eaff21d2-3734-11ee-909f-8e20563011de"
|
||||
+ "&is_request_all_frontend=true")
|
||||
.build();
|
||||
Response response = networkClient.newCall(request).execute();
|
||||
String respStr = response.body().string();
|
||||
Assertions.assertTrue(respStr.contains("Query ID: eaff21d2-3734-11ee-909f-8e20563011de"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2021-present StarRocks, Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.starrocks.http.rest.v2;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.starrocks.common.Pair;
|
||||
import com.starrocks.ha.FrontendNodeType;
|
||||
import com.starrocks.http.StarRocksHttpTestCase;
|
||||
import com.starrocks.http.rest.RestBaseAction;
|
||||
import com.starrocks.qe.QueryDetail;
|
||||
import com.starrocks.qe.QueryDetailQueue;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.system.Frontend;
|
||||
import mockit.Expectations;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class QueryDetailV2Test extends StarRocksHttpTestCase {
|
||||
|
||||
private static final String QUERY_PLAN_URI = "/api/v2/query_detail";
|
||||
|
||||
@Test
|
||||
public void testQueryDetail() throws IOException {
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.addHeader("Authorization", rootAuth)
|
||||
.url("http://localhost:" + HTTP_PORT + QUERY_PLAN_URI + "?event_time=1753671088")
|
||||
.build();
|
||||
Response response = networkClient.newCall(request).execute();
|
||||
String respStr = response.body().string();
|
||||
Assertions.assertNotNull(respStr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryDetailFromLeaderFront() throws IOException {
|
||||
|
||||
QueryDetail startQueryDetail = new QueryDetail("219a2d5443c542d4-8fc938db37c892e3", false, 1, "127.0.0.1",
|
||||
System.currentTimeMillis(), -1, -1, QueryDetail.QueryMemState.RUNNING,
|
||||
"testDb", "select * from table1 limit 1",
|
||||
"root", "", "default_catalog");
|
||||
startQueryDetail.setScanRows(100);
|
||||
startQueryDetail.setScanBytes(10001);
|
||||
startQueryDetail.setReturnRows(1);
|
||||
startQueryDetail.setCpuCostNs(1002);
|
||||
startQueryDetail.setMemCostBytes(100003);
|
||||
QueryDetailQueue.addQueryDetail(startQueryDetail);
|
||||
|
||||
long eventTime = startQueryDetail.getEventTime() - 1;
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.addHeader("Authorization", rootAuth)
|
||||
.url("http://localhost:" + HTTP_PORT + QUERY_PLAN_URI + "?event_time=" + eventTime)
|
||||
.build();
|
||||
Response response = networkClient.newCall(request).execute();
|
||||
String respStr = response.body().string();
|
||||
Assertions.assertTrue(respStr.contains("219a2d5443c542d4-8fc938db37c892e3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryDetailFromFronts() throws IOException {
|
||||
|
||||
Frontend frontend = new Frontend(0, FrontendNodeType.LEADER, "", "localhost", 0);
|
||||
|
||||
new Expectations(GlobalStateMgr.getCurrentState().getNodeMgr()) {
|
||||
{
|
||||
|
||||
GlobalStateMgr.getCurrentState().getNodeMgr().getSelfNode();
|
||||
minTimes = 1;
|
||||
result = new Pair<>(frontend.getHost(), HTTP_PORT);
|
||||
}
|
||||
};
|
||||
|
||||
QueryDetail startQueryDetail = new QueryDetail("219a2d5443c542d4-8fc938db37c892e3", false, 1, "127.0.0.1",
|
||||
System.currentTimeMillis(), -1, -1, QueryDetail.QueryMemState.RUNNING,
|
||||
"testDb", "select * from table1 limit 1",
|
||||
"root", "", "default_catalog");
|
||||
startQueryDetail.setScanRows(100);
|
||||
startQueryDetail.setScanBytes(10001);
|
||||
startQueryDetail.setReturnRows(1);
|
||||
startQueryDetail.setCpuCostNs(1002);
|
||||
startQueryDetail.setMemCostBytes(100003);
|
||||
QueryDetailQueue.addQueryDetail(startQueryDetail);
|
||||
|
||||
|
||||
new MockUp<RestBaseAction>() {
|
||||
@Mock
|
||||
public static List<Pair<String, Integer>> getOtherAliveFe() {
|
||||
|
||||
QueryDetail startQueryDetail = new QueryDetail("219a2d5443c542d4-8fc938db37c892e5", false, 1, "127.0.0.1",
|
||||
System.currentTimeMillis(), -1, -1, QueryDetail.QueryMemState.RUNNING,
|
||||
"testDb", "select * from table2 limit 1",
|
||||
"root", "", "default_catalog");
|
||||
startQueryDetail.setScanRows(150);
|
||||
startQueryDetail.setScanBytes(10005);
|
||||
startQueryDetail.setReturnRows(1);
|
||||
startQueryDetail.setCpuCostNs(1005);
|
||||
startQueryDetail.setMemCostBytes(100005);
|
||||
QueryDetailQueue.addQueryDetail(startQueryDetail);
|
||||
|
||||
Pair<String, Integer> frontNode = GlobalStateMgr.getCurrentState()
|
||||
.getNodeMgr()
|
||||
.getSelfNode();
|
||||
List<Pair<String, Integer>> frontNodes = new ArrayList<>();
|
||||
frontNodes.add(frontNode);
|
||||
return frontNodes;
|
||||
}
|
||||
};
|
||||
|
||||
long eventTime = startQueryDetail.getEventTime() - 1;
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.addHeader("Authorization", rootAuth)
|
||||
.url("http://localhost:" + HTTP_PORT + QUERY_PLAN_URI +
|
||||
"?event_time=" + eventTime +
|
||||
"&user=root" +
|
||||
"&is_request_all_frontend=true")
|
||||
.build();
|
||||
Response response = networkClient.newCall(request).execute();
|
||||
String respStr = Objects.requireNonNull(response.body()).string();
|
||||
Gson gson = new Gson();
|
||||
QueryDetail[] details = gson.fromJson(respStr, QueryDetail[].class);
|
||||
Assertions.assertEquals(3, details.length);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue