diff --git a/docs/generated/http/full.md b/docs/generated/http/full.md
index 7909737711ef..4e4eafa7525b 100644
--- a/docs/generated/http/full.md
+++ b/docs/generated/http/full.md
@@ -1329,6 +1329,7 @@ only.
| reads_per_second | [double](#cockroach.server.serverpb.RaftDebugResponse-double) | | Reads per second served is the number of keys read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| write_bytes_per_second | [double](#cockroach.server.serverpb.RaftDebugResponse-double) | | Writes (bytes) per second is the number of bytes written to this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| read_bytes_per_second | [double](#cockroach.server.serverpb.RaftDebugResponse-double) | | Reads (bytes) per second is the number of bytes read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#cockroach.server.serverpb.RaftDebugResponse-double) | | CPU time (ns) per second is the cpu usage of this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
@@ -1575,6 +1576,7 @@ only.
| reads_per_second | [double](#cockroach.server.serverpb.RangesResponse-double) | | Reads per second served is the number of keys read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| write_bytes_per_second | [double](#cockroach.server.serverpb.RangesResponse-double) | | Writes (bytes) per second is the number of bytes written to this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| read_bytes_per_second | [double](#cockroach.server.serverpb.RangesResponse-double) | | Reads (bytes) per second is the number of bytes read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#cockroach.server.serverpb.RangesResponse-double) | | CPU time (ns) per second is the cpu usage of this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
@@ -1784,6 +1786,7 @@ only.
| reads_per_second | [double](#cockroach.server.serverpb.TenantRangesResponse-double) | | Reads per second served is the number of keys read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| write_bytes_per_second | [double](#cockroach.server.serverpb.TenantRangesResponse-double) | | Writes (bytes) per second is the number of bytes written to this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| read_bytes_per_second | [double](#cockroach.server.serverpb.TenantRangesResponse-double) | | Reads (bytes) per second is the number of bytes read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#cockroach.server.serverpb.TenantRangesResponse-double) | | CPU time (ns) per second is the cpu usage of this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
@@ -3495,6 +3498,7 @@ target node(s) selected in a HotRangesRequest.
| reads_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | Reads per second is the recent number of keys read per second on this range. | [reserved](#support-status) |
| write_bytes_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | Write bytes per second is the recent number of bytes written per second on this range. | [reserved](#support-status) |
| read_bytes_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | Read bytes per second is the recent number of bytes read per second on this range. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#cockroach.server.serverpb.HotRangesResponse-double) | | CPU time per second is the recent cpu usage in nanoseconds of this range. | [reserved](#support-status) |
@@ -3567,6 +3571,11 @@ HotRange message describes a single hot range, ie its QPS, node ID it belongs to
| 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) |
+| writes_per_second | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | writes_per_second is the recent number of keys written per second on this range. | [reserved](#support-status) |
+| reads_per_second | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | reads_per_second is the recent number of keys read per second on this range. | [reserved](#support-status) |
+| write_bytes_per_second | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | write_bytes_per_second is the recent number of bytes written per second on this range. | [reserved](#support-status) |
+| read_bytes_per_second | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | read_bytes_per_second is the recent number of bytes read per second on this range. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#cockroach.server.serverpb.HotRangesResponseV2-double) | | CPU time (ns) per second is the recent cpu usage per second on this range. | [reserved](#support-status) |
@@ -3881,6 +3890,7 @@ only.
| reads_per_second | [double](#cockroach.server.serverpb.RangeResponse-double) | | Reads per second served is the number of keys read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| write_bytes_per_second | [double](#cockroach.server.serverpb.RangeResponse-double) | | Writes (bytes) per second is the number of bytes written to this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
| read_bytes_per_second | [double](#cockroach.server.serverpb.RangeResponse-double) | | Reads (bytes) per second is the number of bytes read from this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#cockroach.server.serverpb.RangeResponse-double) | | CPU time (ns) per second is the cpu usage of this range per second, averaged over the last 30 minute period. | [reserved](#support-status) |
diff --git a/docs/generated/http/hotranges-other.md b/docs/generated/http/hotranges-other.md
index fc2098a04106..a899815903e8 100644
--- a/docs/generated/http/hotranges-other.md
+++ b/docs/generated/http/hotranges-other.md
@@ -68,5 +68,6 @@ Support status: [alpha](#support-status)
| reads_per_second | [double](#double) | | Reads per second is the recent number of keys read per second on this range. | [reserved](#support-status) |
| write_bytes_per_second | [double](#double) | | Write bytes per second is the recent number of bytes written per second on this range. | [reserved](#support-status) |
| read_bytes_per_second | [double](#double) | | Read bytes per second is the recent number of bytes read per second on this range. | [reserved](#support-status) |
+| cpu_time_per_second | [double](#double) | | CPU time per second is the recent cpu usage in nanoseconds of this range. | [reserved](#support-status) |
diff --git a/docs/generated/swagger/spec.json b/docs/generated/swagger/spec.json
index 9329229b3002..022df3ad3c29 100644
--- a/docs/generated/swagger/spec.json
+++ b/docs/generated/swagger/spec.json
@@ -2104,6 +2104,31 @@
"format": "double",
"x-go-name": "QPS"
},
+ "writes_per_second": {
+ "type": "number",
+ "format": "double",
+ "x-go-name": "WritesPerSecond"
+ },
+ "reads_per_second": {
+ "type": "number",
+ "format": "double",
+ "x-go-name": "ReadsPerSecond"
+ },
+ "write_bytes_per_second": {
+ "type": "number",
+ "format": "double",
+ "x-go-name": "WriteBytesPerSecond"
+ },
+ "read_bytes_per_second": {
+ "type": "number",
+ "format": "double",
+ "x-go-name": "ReadBytesPerSecond"
+ },
+ "cpu_time_per_second": {
+ "type": "number",
+ "format": "double",
+ "x-go-name": "CPUTimePerSecond"
+ },
"range_id": {
"$ref": "#/definitions/RangeID"
},
@@ -2499,4 +2524,4 @@
"in": "header"
}
}
-}
\ No newline at end of file
+}
diff --git a/pkg/kv/kvserver/store.go b/pkg/kv/kvserver/store.go
index af0b719b0141..7c5140eacbe9 100644
--- a/pkg/kv/kvserver/store.go
+++ b/pkg/kv/kvserver/store.go
@@ -3032,7 +3032,7 @@ type HotReplicaInfo struct {
WriteKeysPerSecond float64
WriteBytesPerSecond float64
ReadBytesPerSecond float64
- CPUNanosPerSecond float64
+ CPUTimePerSecond float64
}
// HottestReplicas returns the hottest replicas on a store, sorted by their
@@ -3064,6 +3064,7 @@ func mapToHotReplicasInfo(repls []CandidateReplica) []HotReplicaInfo {
hotRepls[i].ReadKeysPerSecond = loadStats.ReadKeysPerSecond
hotRepls[i].WriteBytesPerSecond = loadStats.WriteBytesPerSecond
hotRepls[i].ReadBytesPerSecond = loadStats.ReadBytesPerSecond
+ hotRepls[i].CPUTimePerSecond = loadStats.RaftCPUNanosPerSecond + loadStats.RequestCPUNanosPerSecond
}
return hotRepls
}
diff --git a/pkg/server/BUILD.bazel b/pkg/server/BUILD.bazel
index 5d2bb8d0f5aa..5923b7ed2cc6 100644
--- a/pkg/server/BUILD.bazel
+++ b/pkg/server/BUILD.bazel
@@ -487,6 +487,7 @@ go_test(
"//pkg/util/encoding",
"//pkg/util/envutil",
"//pkg/util/grpcutil",
+ "//pkg/util/grunning",
"//pkg/util/hlc",
"//pkg/util/httputil",
"//pkg/util/humanizeutil",
diff --git a/pkg/server/api_v2_ranges.go b/pkg/server/api_v2_ranges.go
index 19af99fc060f..0a20c6699392 100644
--- a/pkg/server/api_v2_ranges.go
+++ b/pkg/server/api_v2_ranges.go
@@ -440,16 +440,21 @@ type hotRangesResponse struct {
//
// 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"`
+ RangeID roachpb.RangeID `json:"range_id"`
+ NodeID roachpb.NodeID `json:"node_id"`
+ QPS float64 `json:"qps"`
+ WritesPerSecond float64 `json:"writes_per_second"`
+ ReadsPerSecond float64 `json:"reads_per_second"`
+ WriteBytesPerSecond float64 `json:"write_bytes_per_second"`
+ ReadBytesPerSecond float64 `json:"read_bytes_per_second"`
+ CPUTimePerSecond float64 `json:"cpu_time_per_second"`
+ 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
@@ -522,16 +527,21 @@ func (a *apiV2Server) listHotRanges(w http.ResponseWriter, r *http.Request) {
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,
+ RangeID: r.RangeID,
+ NodeID: r.NodeID,
+ QPS: r.QPS,
+ WritesPerSecond: r.WritesPerSecond,
+ ReadsPerSecond: r.ReadsPerSecond,
+ WriteBytesPerSecond: r.WriteBytesPerSecond,
+ ReadBytesPerSecond: r.ReadBytesPerSecond,
+ CPUTimePerSecond: r.CPUTimePerSecond,
+ LeaseholderNodeID: r.LeaseholderNodeID,
+ TableName: r.TableName,
+ DatabaseName: r.DatabaseName,
+ IndexName: r.IndexName,
+ ReplicaNodeIDs: r.ReplicaNodeIds,
+ SchemaName: r.SchemaName,
+ StoreID: r.StoreID,
}
}
return hotRangeInfos, nil
diff --git a/pkg/server/serverpb/status.proto b/pkg/server/serverpb/status.proto
index df3be7091313..7d26c0e4f6e9 100644
--- a/pkg/server/serverpb/status.proto
+++ b/pkg/server/serverpb/status.proto
@@ -414,6 +414,9 @@ message RangeStatistics {
// Reads (bytes) per second is the number of bytes read from this range per
// second, averaged over the last 30 minute period.
double read_bytes_per_second = 6;
+ // CPU time (ns) per second is the cpu usage of this range per second,
+ // averaged over the last 30 minute period.
+ double cpu_time_per_second = 7 [(gogoproto.customname) = "CPUTimePerSecond"];
}
message PrettySpan {
@@ -1340,6 +1343,8 @@ message HotRangesResponse {
// Read bytes per second is the recent number of bytes read per second on
// this range.
double read_bytes_per_second = 8;
+ // CPU time per second is the recent cpu usage in nanoseconds of this range.
+ double cpu_time_per_second = 9 [(gogoproto.customname) = "CPUTimePerSecond"];
}
// StoreResponse contains the part of a hot ranges report that
@@ -1432,6 +1437,21 @@ message HotRangesResponseV2 {
(gogoproto.casttype) =
"github.com/cockroachdb/cockroach/pkg/roachpb.StoreID"
];
+ // writes_per_second is the recent number of keys written per second on
+ // this range.
+ double writes_per_second = 11;
+ // reads_per_second is the recent number of keys read per second on
+ // this range.
+ double reads_per_second = 12;
+ // write_bytes_per_second is the recent number of bytes written per second
+ // on this range.
+ double write_bytes_per_second = 13;
+ // read_bytes_per_second is the recent number of bytes read per second on
+ // this range.
+ double read_bytes_per_second = 14;
+ // CPU time (ns) per second is the recent cpu usage per second on this
+ // range.
+ double cpu_time_per_second = 15 [(gogoproto.customname) = "CPUTimePerSecond"];
}
// Ranges contain list of hot ranges info that has highest number of QPS.
repeated HotRange ranges = 1;
diff --git a/pkg/server/status.go b/pkg/server/status.go
index b1e214f6edd0..60fe7639b103 100644
--- a/pkg/server/status.go
+++ b/pkg/server/status.go
@@ -2096,6 +2096,7 @@ func (s *systemStatusServer) rangesHelper(
ReadsPerSecond: loadStats.ReadKeysPerSecond,
WriteBytesPerSecond: loadStats.WriteKeysPerSecond,
ReadBytesPerSecond: loadStats.ReadBytesPerSecond,
+ CPUTimePerSecond: loadStats.RaftCPUNanosPerSecond + loadStats.RequestCPUNanosPerSecond,
},
Problems: serverpb.RangeProblems{
Unavailable: metrics.Unavailable,
@@ -2538,16 +2539,21 @@ func (s *systemStatusServer) HotRangesV2(
}
ranges = append(ranges, &serverpb.HotRangesResponseV2_HotRange{
- RangeID: r.Desc.RangeID,
- NodeID: requestedNodeID,
- QPS: r.QueriesPerSecond,
- TableName: tableName,
- SchemaName: schemaName,
- DatabaseName: dbName,
- IndexName: indexName,
- ReplicaNodeIds: replicaNodeIDs,
- LeaseholderNodeID: r.LeaseholderNodeID,
- StoreID: store.StoreID,
+ RangeID: r.Desc.RangeID,
+ NodeID: requestedNodeID,
+ QPS: r.QueriesPerSecond,
+ WritesPerSecond: r.WritesPerSecond,
+ ReadsPerSecond: r.ReadsPerSecond,
+ WriteBytesPerSecond: r.WriteBytesPerSecond,
+ ReadBytesPerSecond: r.ReadBytesPerSecond,
+ CPUTimePerSecond: r.CPUTimePerSecond,
+ TableName: tableName,
+ SchemaName: schemaName,
+ DatabaseName: dbName,
+ IndexName: indexName,
+ ReplicaNodeIds: replicaNodeIDs,
+ LeaseholderNodeID: r.LeaseholderNodeID,
+ StoreID: store.StoreID,
})
}
}
@@ -2641,6 +2647,7 @@ func (s *systemStatusServer) localHotRanges(
storeResp.HotRanges[i].ReadsPerSecond = r.ReadKeysPerSecond
storeResp.HotRanges[i].WriteBytesPerSecond = r.WriteBytesPerSecond
storeResp.HotRanges[i].ReadBytesPerSecond = r.ReadBytesPerSecond
+ storeResp.HotRanges[i].CPUTimePerSecond = r.CPUTimePerSecond
}
resp.Stores = append(resp.Stores, storeResp)
return nil
diff --git a/pkg/server/status_test.go b/pkg/server/status_test.go
index 28695fbc8016..7e16497e4278 100644
--- a/pkg/server/status_test.go
+++ b/pkg/server/status_test.go
@@ -59,6 +59,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
"github.com/cockroachdb/cockroach/pkg/ts"
"github.com/cockroachdb/cockroach/pkg/util"
+ "github.com/cockroachdb/cockroach/pkg/util/grunning"
"github.com/cockroachdb/cockroach/pkg/util/httputil"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/cockroachdb/cockroach/pkg/util/log"
@@ -1054,6 +1055,18 @@ func TestHotRangesResponse(t *testing.T) {
if r.Desc.RangeID == 0 || (len(r.Desc.StartKey) == 0 && len(r.Desc.EndKey) == 0) {
t.Errorf("unexpected empty/unpopulated range descriptor: %+v", r.Desc)
}
+ if r.QueriesPerSecond > 0 {
+ if r.ReadsPerSecond == 0 && r.WritesPerSecond == 0 {
+ t.Errorf("qps %.2f > 0, expected either reads=%.2f or writes=%.2f to be non-zero",
+ r.QueriesPerSecond, r.ReadsPerSecond, r.WritesPerSecond)
+ }
+ // If the architecture doesn't support sampling CPU, it
+ // will also be zero.
+ if grunning.Supported() && r.CPUTimePerSecond == 0 {
+ t.Errorf("qps %.2f > 0, expected cpu=%.2f to be non-zero",
+ r.QueriesPerSecond, r.CPUTimePerSecond)
+ }
+ }
if r.QueriesPerSecond > lastQPS {
t.Errorf("unexpected increase in qps between ranges; prev=%.2f, current=%.2f, desc=%v",
lastQPS, r.QueriesPerSecond, r.Desc)
@@ -1083,6 +1096,17 @@ func TestHotRanges2Response(t *testing.T) {
if r.RangeID == 0 {
t.Errorf("unexpected empty range id: %d", r.RangeID)
}
+ if r.QPS > 0 {
+ if r.ReadsPerSecond == 0 && r.WritesPerSecond == 0 {
+ t.Errorf("qps %.2f > 0, expected either reads=%.2f or writes=%.2f to be non-zero",
+ r.QPS, r.ReadsPerSecond, r.WritesPerSecond)
+ }
+ // If the architecture doesn't support sampling CPU, it
+ // will also be zero.
+ if grunning.Supported() && r.CPUTimePerSecond == 0 {
+ t.Errorf("qps %.2f > 0, expected cpu=%.2f to be non-zero", r.QPS, r.CPUTimePerSecond)
+ }
+ }
if r.QPS > lastQPS {
t.Errorf("unexpected increase in qps between ranges; prev=%.2f, current=%.2f", lastQPS, r.QPS)
}
diff --git a/pkg/ui/workspaces/db-console/src/views/hotRanges/hotRangesTable.tsx b/pkg/ui/workspaces/db-console/src/views/hotRanges/hotRangesTable.tsx
index 851a41947894..7bcc7cfdd2f7 100644
--- a/pkg/ui/workspaces/db-console/src/views/hotRanges/hotRangesTable.tsx
+++ b/pkg/ui/workspaces/db-console/src/views/hotRanges/hotRangesTable.tsx
@@ -25,6 +25,7 @@ import classNames from "classnames/bind";
import { round } from "lodash";
import styles from "./hotRanges.module.styl";
import { cockroach } from "src/js/protos";
+import { util } from "@cockroachlabs/cluster-ui";
import {
performanceBestPracticesHotSpots,
readsAndWritesOverviewPage,
@@ -96,6 +97,71 @@ const HotRangesTable = ({
cell: val => <>{round(val.qps, 2)}>,
sort: val => val.qps,
},
+ {
+ name: "cpuPerSecond",
+ title: (
+
+ CPU
+
+ ),
+ cell: val => <>{util.Duration(val.cpu_time_per_second)}>,
+ sort: val => val.cpu_time_per_second,
+ },
+ {
+ name: "writesPerSecond",
+ title: (
+
+ Write (keys)
+
+ ),
+ cell: val => <>{round(val.writes_per_second, 2)}>,
+ sort: val => val.writes_per_second,
+ },
+ {
+ name: "writeBytesPerSecond",
+ title: (
+
+ Write (bytes)
+
+ ),
+ cell: val => <>{util.Bytes(val.write_bytes_per_second)}>,
+ sort: val => val.write_bytes_per_second,
+ },
+ {
+ name: "readsPerSecond",
+ title: (
+
+ Read (keys)
+
+ ),
+ cell: val => <>{round(val.reads_per_second, 2)}>,
+ sort: val => val.reads_per_second,
+ },
+ {
+ name: "readsBytesPerSecond",
+ title: (
+
+ Read (bytes)
+
+ ),
+ cell: val => <>{util.Bytes(val.read_bytes_per_second)}>,
+ sort: val => val.read_bytes_per_second,
+ },
{
name: "nodes",
title: (