Skip to content

Commit

Permalink
server, ui: use POST request method for HotRangesV2 service
Browse files Browse the repository at this point in the history
Initially, `HotRangesV2` service in status server was defined
to use GET method to handle HTTP request. It was convenient way
to display response data in Advanced debugging page since it allowed
to render response body right on to page.

But now, hot ranges will be used on user facing page and request
dispatching for hot ranges api should follow generic workflow:
initialize `HotRangesRequest` protobuf message and dispatch request
with `src/util/api` service. This restriction forces to use POST
method (since GET method doesn't allow to provide request body).

In addition, Hot Ranges debugging page is refactored to use `api`
service.

Release note: None

Release justification: bug fixes and low-risk updates
to new functionality
  • Loading branch information
koorosh committed Mar 2, 2022
1 parent af84b8a commit 35d2b78
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 30 deletions.
24 changes: 12 additions & 12 deletions docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -3209,7 +3209,7 @@ target node(s) selected in a HotRangesRequest.
| ----- | ---- | ----- | ----------- | -------------- |
| desc | [cockroach.roachpb.RangeDescriptor](#cockroach.server.serverpb.HotRangesResponse-cockroach.roachpb.RangeDescriptor) | | Desc is the descriptor of the range for which the report was produced.<br><br>TODO(knz): This field should be removed. See: https://github.com/cockroachdb/cockroach/issues/53212 | [reserved](#support-status) |
| queries_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | QueriesPerSecond is the recent number of queries per second on this range. | [alpha](#support-status) |
| leaseholder_node_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | LeaseholderNodeID indicates on Node ID that contains replica that is leaseholder | [reserved](#support-status) |
| leaseholder_node_id | [int32](#cockroach.server.serverpb.HotRangesResponse-int32) | | LeaseholderNodeID indicates the Node ID that is the current leaseholder for the given range. | [reserved](#support-status) |



Expand All @@ -3218,7 +3218,7 @@ target node(s) selected in a HotRangesRequest.

## HotRangesV2

`GET /_status/v2/hotranges`
`POST /_status/v2/hotranges`



Expand Down Expand Up @@ -3253,7 +3253,7 @@ HotRangesResponseV2 is a response payload returned by `HotRangesV2` service.

| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| ranges | [HotRangesResponseV2.HotRange](#cockroach.server.serverpb.HotRangesResponseV2-cockroach.server.serverpb.HotRangesResponseV2.HotRange) | repeated | ranges contain list of hot ranges info that has highest number of QPS | [reserved](#support-status) |
| ranges | [HotRangesResponseV2.HotRange](#cockroach.server.serverpb.HotRangesResponseV2-cockroach.server.serverpb.HotRangesResponseV2.HotRange) | repeated | ranges contain list of hot ranges info that has highest number of QPS. | [reserved](#support-status) |



Expand All @@ -3267,15 +3267,15 @@ HotRange message describes a single hot range, ie its QPS, node ID it belongs to

| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| range_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | range_id indicates Range ID that's identified as hot range | [reserved](#support-status) |
| node_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | node_id indicates on node that contains current hot range | [reserved](#support-status) |
| qps | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | qps (queries per second) shows the amount of queries that interact with current range | [reserved](#support-status) |
| table_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | table_name indicates table which data is stored in this hot range | [reserved](#support-status) |
| database_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | database_name indicates on database that has current hot range | [reserved](#support-status) |
| index_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | index_name indicates the index name for current range | [reserved](#support-status) |
| replica_node_ids | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | repeated | replica_node_ids specifies the list of node ids that contain replicas with current hot range | [reserved](#support-status) |
| leaseholder_node_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | leaseholder_node_id indicates on Node ID that contains replica that is a leaseholder | [reserved](#support-status) |
| schema_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | schema_name provides the name of schema (if exists) for table in current range | [reserved](#support-status) |
| range_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | range_id indicates Range ID that's identified as hot range. | [reserved](#support-status) |
| node_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | node_id indicates the node that contains the current hot range. | [reserved](#support-status) |
| qps | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | qps (queries per second) shows the amount of queries that interact with current range. | [reserved](#support-status) |
| table_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | table_name indicates the SQL table that the range belongs to. | [reserved](#support-status) |
| database_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | database_name indicates on database that has current hot range. | [reserved](#support-status) |
| index_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | index_name indicates the index name for current range. | [reserved](#support-status) |
| replica_node_ids | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | repeated | replica_node_ids specifies the list of node ids that contain replicas with current hot range. | [reserved](#support-status) |
| leaseholder_node_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | leaseholder_node_id indicates the Node ID that is the current leaseholder for the given range. | [reserved](#support-status) |
| schema_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | schema_name provides the name of schema (if exists) for table in current range. | [reserved](#support-status) |



Expand Down
2 changes: 1 addition & 1 deletion docs/generated/http/hotranges-other.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ Support status: [alpha](#support-status)
| ----- | ---- | ----- | ----------- | -------------- |
| desc | [cockroach.roachpb.RangeDescriptor](#cockroach.roachpb.RangeDescriptor) | | Desc is the descriptor of the range for which the report was produced.<br><br>TODO(knz): This field should be removed. See: https://github.com/cockroachdb/cockroach/issues/53212 | [reserved](#support-status) |
| queries_per_second | [double](#double) | | QueriesPerSecond is the recent number of queries per second on this range. | [alpha](#support-status) |
| leaseholder_node_id | [int32](#int32) | | LeaseholderNodeID indicates on Node ID that contains replica that is leaseholder | [reserved](#support-status) |
| leaseholder_node_id | [int32](#int32) | | LeaseholderNodeID indicates the Node ID that is the current leaseholder for the given range. | [reserved](#support-status) |


25 changes: 13 additions & 12 deletions pkg/server/serverpb/status.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1140,7 +1140,7 @@ message HotRangesResponse {
// on this range.
// API: PUBLIC ALPHA
double queries_per_second = 2;
// LeaseholderNodeID indicates on Node ID that contains replica that is leaseholder
// LeaseholderNodeID indicates the Node ID that is the current leaseholder for the given range.
int32 leaseholder_node_id = 3 [
(gogoproto.customname) = "LeaseholderNodeID",
(gogoproto.casttype) =
Expand Down Expand Up @@ -1198,42 +1198,42 @@ message HotRangesResponse {
message HotRangesResponseV2 {
// HotRange message describes a single hot range, ie its QPS, node ID it belongs to, etc.
message HotRange {
// range_id indicates Range ID that's identified as hot range
// range_id indicates Range ID that's identified as hot range.
int32 range_id = 1 [
(gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/roachpb.RangeID",
(gogoproto.customname) = "RangeID"
];
// node_id indicates on node that contains current hot range
// node_id indicates the node that contains the current hot range.
int32 node_id = 2 [
(gogoproto.customname) = "NodeID",
(gogoproto.casttype) =
"github.com/cockroachdb/cockroach/pkg/roachpb.NodeID"
];
// qps (queries per second) shows the amount of queries that interact with current range
// qps (queries per second) shows the amount of queries that interact with current range.
double qps = 3 [
(gogoproto.customname) = "QPS"
];
// table_name indicates table which data is stored in this hot range
// table_name indicates the SQL table that the range belongs to.
string table_name = 4;
// database_name indicates on database that has current hot range
// database_name indicates on database that has current hot range.
string database_name = 5;
// index_name indicates the index name for current range
// index_name indicates the index name for current range.
string index_name = 6;
// replica_node_ids specifies the list of node ids that contain replicas with current hot range
// replica_node_ids specifies the list of node ids that contain replicas with current hot range.
repeated int32 replica_node_ids = 7 [
(gogoproto.casttype) =
"github.com/cockroachdb/cockroach/pkg/roachpb.NodeID"
];
// leaseholder_node_id indicates on Node ID that contains replica that is a leaseholder
// leaseholder_node_id indicates the Node ID that is the current leaseholder for the given range.
int32 leaseholder_node_id = 8 [
(gogoproto.customname) = "LeaseholderNodeID",
(gogoproto.casttype) =
"github.com/cockroachdb/cockroach/pkg/roachpb.NodeID"
];
// schema_name provides the name of schema (if exists) for table in current range
// schema_name provides the name of schema (if exists) for table in current range.
string schema_name = 9;
}
// ranges contain list of hot ranges info that has highest number of QPS
// ranges contain list of hot ranges info that has highest number of QPS.
repeated HotRange ranges = 1;
}

Expand Down Expand Up @@ -1926,7 +1926,8 @@ service Status {

rpc HotRangesV2(HotRangesRequest) returns (HotRangesResponseV2) {
option (google.api.http) = {
get : "/_status/v2/hotranges"
post : "/_status/v2/hotranges"
body : "*"
};
}

Expand Down
1 change: 1 addition & 0 deletions pkg/server/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2172,6 +2172,7 @@ func (s *statusServer) HotRangesV2(
)
_, tableID, err := s.sqlServer.execCfg.Codec.DecodeTablePrefix(r.Desc.StartKey.AsRawKey())
if err != nil {
log.Warningf(ctx, "cannot decode tableID for range descriptor: %s. %s", r.Desc.String(), err.Error())
continue
}
parent := rangeReportMetas[tableID].parentID
Expand Down
2 changes: 1 addition & 1 deletion pkg/server/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ func TestHotRanges2Response(t *testing.T) {
defer ts.Stopper().Stop(context.Background())

var hotRangesResp serverpb.HotRangesResponseV2
if err := getStatusJSONProto(ts, "v2/hotranges", &hotRangesResp); err != nil {
if err := postStatusJSONProto(ts, "v2/hotranges", &serverpb.HotRangesRequest{}, &hotRangesResp); err != nil {
t.Fatal(err)
}
if len(hotRangesResp.Ranges) == 0 {
Expand Down
7 changes: 7 additions & 0 deletions pkg/ui/workspaces/db-console/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import Nodes from "src/views/reports/containers/nodes";
import ProblemRanges from "src/views/reports/containers/problemRanges";
import Range from "src/views/reports/containers/range";
import ReduxDebug from "src/views/reports/containers/redux";
import HotRanges from "src/views/reports/containers/hotranges";
import Settings from "src/views/reports/containers/settings";
import Stores from "src/views/reports/containers/stores";
import SQLActivityPage from "src/views/sqlActivity/sqlActivityPage";
Expand Down Expand Up @@ -274,6 +275,12 @@ export const App: React.FC<AppProps> = (props: AppProps) => {
path="/debug/enqueue_range"
component={EnqueueRange}
/>
<Route exact path="/debug/hotranges" component={HotRanges} />
<Route
exact
path="/debug/hotranges/:node_id"
component={HotRanges}
/>

<Route path="/raft">
<Raft>
Expand Down
15 changes: 15 additions & 0 deletions pkg/ui/workspaces/db-console/src/util/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ export type ResetIndexUsageStatsResponseMessage = protos.cockroach.server.server
export type UserSQLRolesRequestMessage = protos.cockroach.server.serverpb.UserSQLRolesRequest;
export type UserSQLRolesResponseMessage = protos.cockroach.server.serverpb.UserSQLRolesResponse;

export type HotRangesRequestMessage = protos.cockroach.server.serverpb.HotRangesRequest;
export type HotRangesV2ResponseMessage = protos.cockroach.server.serverpb.HotRangesResponseV2;

// API constants

export const API_PREFIX = "_admin/v1";
Expand Down Expand Up @@ -821,3 +824,15 @@ export function getUserSQLRoles(
timeout,
);
}

export function getHotRanges(
req: HotRangesRequestMessage,
timeout?: moment.Duration,
): Promise<HotRangesV2ResponseMessage> {
return timeoutFetch(
serverpb.HotRangesResponseV2,
`${STATUS_PREFIX}/v2/hotranges`,
req as any,
timeout,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -505,13 +505,25 @@ export default function Debug() {
<DebugTableRow title="Hot Ranges">
<DebugTableLink
name="All Nodes"
url="_status/v2/hotranges"
note="_status/v2/hotranges"
url="#/debug/hotranges"
note="#/debug/hotranges"
/>
<DebugTableLink
name="Single node's ranges"
url="_status/v2/hotranges?node_id=local"
note="_status/v2/hotranges?node_id=[node_id]"
url="#/debug/hotranges/local"
note="#/debug/hotranges/[node_id]"
/>
</DebugTableRow>
<DebugTableRow title="Hot Ranges (legacy)">
<DebugTableLink
name="All Nodes"
url="_status/hotranges"
note="_status/hotranges"
/>
<DebugTableLink
name="Single node's ranges"
url="_status/hotranges?node_id=local"
note="_status/hotranges?node_id=[node_id]"
/>
</DebugTableRow>
<DebugTableRow title="Single Node Specific">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2022 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

import React, { useCallback, useEffect, useState } from "react";
import { RouteComponentProps, withRouter } from "react-router-dom";
import moment from "moment";
import { Button } from "@cockroachlabs/ui-components";
import { cockroach } from "src/js/protos";
import { getHotRanges } from "src/util/api";

type HotRangesProps = RouteComponentProps<{ node_id: string }>;

const HotRanges = (props: HotRangesProps) => {
const nodeIdParam = props.match.params["node_id"];
const [nodeId, setNodeId] = useState(nodeIdParam);
const [time, setTime] = useState<moment.Moment>(moment());
const [hotRanges, setHotRanges] = useState<
cockroach.server.serverpb.HotRangesResponseV2["ranges"]
>([]);
const requestHotRanges = useCallback(() => {
const request = cockroach.server.serverpb.HotRangesRequest.create({
node_id: nodeId,
});
getHotRanges(request).then(response => {
setHotRanges(response.ranges);
setTime(moment());
});
}, [nodeId]);
useEffect(requestHotRanges, [requestHotRanges, nodeId]);
useEffect(() => {
setNodeId(nodeIdParam);
}, [nodeIdParam]);
return (
<div
style={{
display: "flex",
flexDirection: "column",
}}
>
<span>{`Node ID: ${nodeId ?? "All nodes"}`}</span>
<span>{`Time: ${time.toISOString()}`}</span>
<Button onClick={requestHotRanges} intent={"secondary"}>
Refresh
</Button>
<pre className="state-json-box">{JSON.stringify(hotRanges, null, 2)}</pre>
</div>
);
};

export default withRouter(HotRanges);

0 comments on commit 35d2b78

Please sign in to comment.