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: (