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

release-21.2: ui: Add Hot Ranges page #77594

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
90 changes: 89 additions & 1 deletion docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -3007,7 +3007,9 @@ of ranges currently considered “hot” by the node(s).

| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is posssible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.<br><br>If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is possible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.<br><br>If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) |
| page_size | [int32](#cockroach.server.serverpb.HotRangesRequest-int32) | | | [reserved](#support-status) |
| page_token | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | | [reserved](#support-status) |



Expand Down Expand Up @@ -3087,6 +3089,92 @@ 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 the Node ID that is the current leaseholder for the given range. | [reserved](#support-status) |






## HotRangesV2

`POST /_status/v2/hotranges`



Support status: [reserved](#support-status)

#### Request Parameters




HotRangesRequest queries one or more cluster nodes for a list
of ranges currently considered “hot” by the node(s).


| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| node_id | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | NodeID indicates which node to query for a hot range report. It is possible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.<br><br>If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) |
| page_size | [int32](#cockroach.server.serverpb.HotRangesRequest-int32) | | | [reserved](#support-status) |
| page_token | [string](#cockroach.server.serverpb.HotRangesRequest-string) | | | [reserved](#support-status) |







#### Response Parameters




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) |
| errors_by_node_id | [HotRangesResponseV2.ErrorsByNodeIdEntry](#cockroach.server.serverpb.HotRangesResponseV2-cockroach.server.serverpb.HotRangesResponseV2.ErrorsByNodeIdEntry) | repeated | errors contains any errors that occurred during fan-out calls to other nodes. | [reserved](#support-status) |
| next_page_token | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | NextPageToken represents next pagination token to request next slice of data. | [reserved](#support-status) |






<a name="cockroach.server.serverpb.HotRangesResponseV2-cockroach.server.serverpb.HotRangesResponseV2.HotRange"></a>
#### HotRangesResponseV2.HotRange

HotRange message describes a single hot range, ie its QPS, node ID it belongs to, etc.

| 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 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) |
| store_id | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | store_id indicates the Store ID where range is stored. | [reserved](#support-status) |





<a name="cockroach.server.serverpb.HotRangesResponseV2-cockroach.server.serverpb.HotRangesResponseV2.ErrorsByNodeIdEntry"></a>
#### HotRangesResponseV2.ErrorsByNodeIdEntry



| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| key | [int32](#cockroach.server.serverpb.HotRangesResponseV2-int32) | | | |
| value | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | | |



Expand Down
1 change: 1 addition & 0 deletions docs/generated/http/hotranges-other.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +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 the Node ID that is the current leaseholder for the given range. | [reserved](#support-status) |


4 changes: 3 additions & 1 deletion docs/generated/http/hotranges-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Support status: [alpha](#support-status)

| Field | Type | Label | Description | Support status |
| ----- | ---- | ----- | ----------- | -------------- |
| node_id | [string](#string) | | NodeID indicates which node to query for a hot range report. It is posssible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.<br><br>If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) |
| node_id | [string](#string) | | NodeID indicates which node to query for a hot range report. It is possible to populate any node ID; if the node receiving the request is not the target node, it will forward the request to the target node.<br><br>If left empty, the request is forwarded to every node in the cluster. | [alpha](#support-status) |
| page_size | [int32](#int32) | | | [reserved](#support-status) |
| page_token | [string](#string) | | | [reserved](#support-status) |


67 changes: 59 additions & 8 deletions docs/generated/swagger/spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@
},
"x-go-package": "github.com/cockroachdb/cockroach/pkg/server/serverpb"
},
"RangeID": {
"type": "integer",
"format": "int64",
"title": "A RangeID is a unique ID associated to a Raft consensus group.",
"x-go-package": "github.com/cockroachdb/cockroach/pkg/roachpb"
},
"RangeProblems": {
"type": "object",
"title": "RangeProblems describes issues reported by a range. For internal use only.",
Expand Down Expand Up @@ -1707,6 +1713,54 @@
},
"x-go-package": "github.com/cockroachdb/cockroach/pkg/server"
},
"hotRangeInfo": {
"description": "(ie its range ID, QPS, table name, etc.).",
"type": "object",
"title": "Hot range details struct describes common information about hot range,",
"properties": {
"database_name": {
"type": "string",
"x-go-name": "DatabaseName"
},
"index_name": {
"type": "string",
"x-go-name": "IndexName"
},
"leaseholder_node_id": {
"$ref": "#/definitions/NodeID"
},
"node_id": {
"$ref": "#/definitions/NodeID"
},
"qps": {
"type": "number",
"format": "double",
"x-go-name": "QPS"
},
"range_id": {
"$ref": "#/definitions/RangeID"
},
"replica_node_ids": {
"type": "array",
"items": {
"$ref": "#/definitions/NodeID"
},
"x-go-name": "ReplicaNodeIDs"
},
"schema_name": {
"type": "string",
"x-go-name": "SchemaName"
},
"store_id": {
"$ref": "#/definitions/StoreID"
},
"table_name": {
"type": "string",
"x-go-name": "TableName"
}
},
"x-go-package": "github.com/cockroachdb/cockroach/pkg/server"
},
"hotRangesResponse": {
"type": "object",
"title": "Response struct for listHotRanges.",
Expand All @@ -1716,15 +1770,12 @@
"type": "string",
"x-go-name": "Next"
},
"ranges_by_node_id": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/definitions/rangeDescriptorInfo"
}
"ranges": {
"type": "array",
"items": {
"$ref": "#/definitions/hotRangeInfo"
},
"x-go-name": "RangesByNodeID"
"x-go-name": "Ranges"
},
"response_error": {
"type": "array",
Expand Down
1 change: 1 addition & 0 deletions pkg/server/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ go_library(
"//pkg/spanconfig/spanconfigkvaccessor",
"//pkg/spanconfig/spanconfigmanager",
"//pkg/sql",
"//pkg/sql/catalog",
"//pkg/sql/catalog/bootstrap",
"//pkg/sql/catalog/catalogkeys",
"//pkg/sql/catalog/catconstants",
Expand Down
62 changes: 37 additions & 25 deletions pkg/server/api_v2_ranges.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"context"
"fmt"
"net/http"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -415,13 +414,30 @@ type responseError struct {
//
// swagger:model hotRangesResponse
type hotRangesResponse struct {
RangesByNodeID map[string][]rangeDescriptorInfo `json:"ranges_by_node_id"`
Errors []responseError `json:"response_error,omitempty"`
Ranges []hotRangeInfo `json:"ranges"`
Errors []responseError `json:"response_error,omitempty"`
// Continuation token for the next paginated call. Use as the `start`
// parameter.
Next string `json:"next,omitempty"`
}

// Hot range details struct describes common information about hot range,
// (ie its range ID, QPS, table name, etc.).
//
// swagger:model hotRangeInfo
type hotRangeInfo struct {
RangeID roachpb.RangeID `json:"range_id"`
NodeID roachpb.NodeID `json:"node_id"`
QPS float64 `json:"qps"`
LeaseholderNodeID roachpb.NodeID `json:"leaseholder_node_id"`
TableName string `json:"table_name"`
DatabaseName string `json:"database_name"`
IndexName string `json:"index_name"`
SchemaName string `json:"schema_name"`
ReplicaNodeIDs []roachpb.NodeID `json:"replica_node_ids"`
StoreID roachpb.StoreID `json:"store_id"`
}

// swagger:operation GET /ranges/hot/ listHotRanges
//
// List hot ranges
Expand Down Expand Up @@ -464,9 +480,7 @@ func (a *apiV2Server) listHotRanges(w http.ResponseWriter, r *http.Request) {
nodeIDStr := r.URL.Query().Get("node_id")
limit, start := getRPCPaginationValues(r)

response := &hotRangesResponse{
RangesByNodeID: make(map[string][]rangeDescriptorInfo),
}
response := &hotRangesResponse{}
var requestedNodes []roachpb.NodeID
if len(nodeIDStr) > 0 {
requestedNodeID, _, err := a.status.parseNodeID(nodeIDStr)
Expand All @@ -484,32 +498,30 @@ func (a *apiV2Server) listHotRanges(w http.ResponseWriter, r *http.Request) {
remoteRequest := serverpb.HotRangesRequest{NodeID: "local"}
nodeFn := func(ctx context.Context, client interface{}, nodeID roachpb.NodeID) (interface{}, error) {
status := client.(serverpb.StatusClient)
resp, err := status.HotRanges(ctx, &remoteRequest)
resp, err := status.HotRangesV2(ctx, &remoteRequest)
if err != nil || resp == nil {
return nil, err
}
rangeDescriptorInfos := make([]rangeDescriptorInfo, 0)
for _, store := range resp.HotRangesByNodeID[nodeID].Stores {
for _, hotRange := range store.HotRanges {
var r rangeDescriptorInfo
r.init(&hotRange.Desc)
r.StoreID = int32(store.StoreID)
r.QueriesPerSecond = hotRange.QueriesPerSecond
rangeDescriptorInfos = append(rangeDescriptorInfos, r)

var hotRangeInfos = make([]hotRangeInfo, len(resp.Ranges))
for i, r := range resp.Ranges {
hotRangeInfos[i] = hotRangeInfo{
RangeID: r.RangeID,
NodeID: r.NodeID,
QPS: r.QPS,
LeaseholderNodeID: r.LeaseholderNodeID,
TableName: r.TableName,
DatabaseName: r.DatabaseName,
IndexName: r.IndexName,
ReplicaNodeIDs: r.ReplicaNodeIds,
SchemaName: r.SchemaName,
StoreID: r.StoreID,
}
}
sort.Slice(rangeDescriptorInfos, func(i, j int) bool {
if rangeDescriptorInfos[i].StoreID == rangeDescriptorInfos[j].StoreID {
return rangeDescriptorInfos[i].RangeID < rangeDescriptorInfos[j].RangeID
}
return rangeDescriptorInfos[i].StoreID < rangeDescriptorInfos[j].StoreID
})
return rangeDescriptorInfos, nil
return hotRangeInfos, nil
}
responseFn := func(nodeID roachpb.NodeID, resp interface{}) {
if hotRangesResp, ok := resp.([]rangeDescriptorInfo); ok {
response.RangesByNodeID[nodeID.String()] = hotRangesResp
}
response.Ranges = append(response.Ranges, resp.([]hotRangeInfo)...)
}
errorFn := func(nodeID roachpb.NodeID, err error) {
response.Errors = append(response.Errors, responseError{
Expand Down
17 changes: 4 additions & 13 deletions pkg/server/api_v2_ranges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,16 @@ func TestHotRangesV2(t *testing.T) {
require.NoError(t, json.NewDecoder(resp.Body).Decode(&hotRangesResp))
require.NoError(t, resp.Body.Close())

if len(hotRangesResp.RangesByNodeID) == 0 {
if len(hotRangesResp.Ranges) == 0 {
t.Fatalf("didn't get hot range responses from any nodes")
}
if len(hotRangesResp.Errors) > 0 {
t.Errorf("got an error in hot range response from n%d: %v",
hotRangesResp.Errors[0].NodeID, hotRangesResp.Errors[0].ErrorMessage)
}

for nodeID, nodeResp := range hotRangesResp.RangesByNodeID {
if len(nodeResp) == 0 {
t.Fatalf("didn't get hot range response from node n%s", nodeID)
}
// We don't check for ranges being sorted by QPS, as this hot ranges
// report does not use that as its sort key (for stability across multiple
// pagination calls).
for _, r := range nodeResp {
if r.RangeID == 0 || (len(r.StartKey) == 0 && len(r.EndKey) == 0) {
t.Errorf("unexpected empty/unpopulated range descriptor: %+v", r)
}
for _, r := range hotRangesResp.Ranges {
if r.RangeID == 0 || r.NodeID == 0 {
t.Errorf("unexpected empty/unpopulated range descriptor: %+v", r)
}
}
}
Expand Down
Loading