From 6e13a11a08617019353db007c731a2c020e0720a Mon Sep 17 00:00:00 2001 From: Dmitry Saveliev Date: Fri, 12 Jan 2018 23:43:17 +0400 Subject: [PATCH] cli: add internal tables to expose status server info about the cluster Fixes #20713. Release note: None --- pkg/sql/crdb_internal.go | 254 +++++++++++++++++- .../testdata/logic_test/crdb_internal | 26 +- pkg/sql/logictest/testdata/logic_test/explain | 4 +- .../testdata/logic_test/information_schema | 4 + 4 files changed, 270 insertions(+), 18 deletions(-) diff --git a/pkg/sql/crdb_internal.go b/pkg/sql/crdb_internal.go index 8159e3b615ec..fba050fd7e87 100644 --- a/pkg/sql/crdb_internal.go +++ b/pkg/sql/crdb_internal.go @@ -61,10 +61,12 @@ var crdbInternal = virtualSchema{ crdbInternalClusterSettingsTable, crdbInternalCreateStmtsTable, crdbInternalForwardDependenciesTable, - crdbInternalGossipNodes, - crdbInternalGossipLiveness, + crdbInternalGossipNodesTable, + crdbInternalGossipLivenessTable, crdbInternalIndexColumnsTable, crdbInternalJobsTable, + crdbInternalKVNodeStatusTable, + crdbInternalKVStoreStatusTable, crdbInternalLeasesTable, crdbInternalLocalQueriesTable, crdbInternalLocalSessionsTable, @@ -1553,16 +1555,16 @@ CREATE TABLE crdb_internal.zones ( }, } -// crdbInternalGossipNodes exposes local information about the cluster nodes. -var crdbInternalGossipNodes = virtualSchemaTable{ +// crdbInternalGossipNodesTable exposes local information about the cluster nodes. +var crdbInternalGossipNodesTable = virtualSchemaTable{ schema: ` CREATE TABLE crdb_internal.gossip_nodes ( - node_id INT NOT NULL, - network STRING NOT NULL, - address STRING NOT NULL, - attrs JSON NOT NULL, - locality STRING NOT NULL, - version STRING NOT NULL + node_id INT NOT NULL, + network STRING NOT NULL, + address STRING NOT NULL, + attrs JSON NOT NULL, + locality JSON NOT NULL, + server_version STRING NOT NULL ) `, populate: func(ctx context.Context, p *planner, prefix string, addRow func(...tree.Datum) error) error { @@ -1598,12 +1600,18 @@ CREATE TABLE crdb_internal.gossip_nodes ( attrs = append(attrs, json.FromString(a)) } + b := json.NewBuilder() + for _, t := range d.Locality.Tiers { + b.Add(t.Key, json.FromString(t.Value)) + } + locality := b.Build() + if err := addRow( tree.NewDInt(tree.DInt(d.NodeID)), tree.NewDString(d.Address.NetworkField), tree.NewDString(d.Address.AddressField), tree.NewDJSON(json.FromArrayOfJSON(attrs)), - tree.NewDString(d.Locality.String()), + tree.NewDJSON(locality), tree.NewDString(d.ServerVersion.String()), ); err != nil { return err @@ -1613,8 +1621,8 @@ CREATE TABLE crdb_internal.gossip_nodes ( }, } -// crdbInternalGossipLiveness exposes local information about the nodes liveness. -var crdbInternalGossipLiveness = virtualSchemaTable{ +// crdbInternalGossipLivenessTable exposes local information about the nodes' liveness. +var crdbInternalGossipLivenessTable = virtualSchemaTable{ schema: ` CREATE TABLE crdb_internal.gossip_liveness ( node_id INT NOT NULL, @@ -1733,3 +1741,223 @@ CREATE TABLE crdb_internal.partitions ( }) }, } + +// crdbInternalKVNodeStatusTable exposes information from the status server about the cluster nodes. +var crdbInternalKVNodeStatusTable = virtualSchemaTable{ + schema: ` +CREATE TABLE crdb_internal.kv_node_status ( + node_id INT NOT NULL, + network STRING NOT NULL, + address STRING NOT NULL, + attrs JSON NOT NULL, + locality JSON NOT NULL, + server_version STRING NOT NULL, + go_version STRING NOT NULL, + tag STRING NOT NULL, + time STRING NOT NULL, + revision STRING NOT NULL, + cgo_compiler STRING NOT NULL, + platform STRING NOT NULL, + distribution STRING NOT NULL, + type STRING NOT NULL, + dependencies STRING NOT NULL, + started_at TIMESTAMP NOT NULL, + updated_at TIMESTAMP NOT NULL, + metrics JSON NOT NULL, + args JSON NOT NULL, + env JSON NOT NULL, + activity JSON NOT NULL +) + `, + populate: func(ctx context.Context, p *planner, prefix string, addRow func(...tree.Datum) error) error { + if err := p.RequireSuperUser("read crdb_internal.kv_node_status"); err != nil { + return err + } + + response, err := p.ExecCfg().StatusServer.Nodes(ctx, &serverpb.NodesRequest{}) + if err != nil { + return err + } + + for _, n := range response.Nodes { + var attrs []json.JSON + for _, a := range n.Desc.Attrs.Attrs { + attrs = append(attrs, json.FromString(a)) + } + + locality := json.NewBuilder() + for _, t := range n.Desc.Locality.Tiers { + locality.Add(t.Key, json.FromString(t.Value)) + } + + var dependencies string + if n.BuildInfo.Dependencies == nil { + dependencies = "" + } else { + dependencies = *(n.BuildInfo.Dependencies) + } + + metrics := json.NewBuilder() + for k, v := range n.Metrics { + metric, err := json.FromFloat64(v) + if err != nil { + return err + } + metrics.Add(k, metric) + } + + var args []json.JSON + for _, a := range n.Args { + args = append(attrs, json.FromString(a)) + } + + var env []json.JSON + for _, v := range n.Env { + env = append(attrs, json.FromString(v)) + } + + activity := json.NewBuilder() + for nodeID, values := range n.Activity { + b := json.NewBuilder() + b.Add("incoming", json.FromInt64(values.Incoming)) + b.Add("outgoing", json.FromInt64(values.Outgoing)) + b.Add("latency", json.FromInt64(values.Latency)) + activity.Add(nodeID.String(), b.Build()) + } + + if err := addRow( + tree.NewDInt(tree.DInt(n.Desc.NodeID)), + tree.NewDString(n.Desc.Address.NetworkField), + tree.NewDString(n.Desc.Address.AddressField), + tree.NewDJSON(json.FromArrayOfJSON(attrs)), + tree.NewDJSON(locality.Build()), + tree.NewDString(n.Desc.ServerVersion.String()), + tree.NewDString(n.BuildInfo.GoVersion), + tree.NewDString(n.BuildInfo.Tag), + tree.NewDString(n.BuildInfo.Time), + tree.NewDString(n.BuildInfo.Revision), + tree.NewDString(n.BuildInfo.CgoCompiler), + tree.NewDString(n.BuildInfo.Platform), + tree.NewDString(n.BuildInfo.Distribution), + tree.NewDString(n.BuildInfo.Type), + tree.NewDString(dependencies), + tree.MakeDTimestamp(timeutil.Unix(0, n.StartedAt), time.Microsecond), + tree.MakeDTimestamp(timeutil.Unix(0, n.UpdatedAt), time.Microsecond), + tree.NewDJSON(metrics.Build()), + tree.NewDJSON(json.FromArrayOfJSON(args)), + tree.NewDJSON(json.FromArrayOfJSON(env)), + tree.NewDJSON(activity.Build()), + ); err != nil { + return err + } + } + return nil + }, +} + +// crdbInternalKVStoreStatusTable exposes information about the cluster stores. +var crdbInternalKVStoreStatusTable = virtualSchemaTable{ + schema: ` +CREATE TABLE crdb_internal.kv_store_status ( + node_id INT NOT NULL, + store_id INT NOT NULL, + attrs JSON NOT NULL, + capacity INT NOT NULL, + available INT NOT NULL, + used INT NOT NULL, + logical_bytes INT NOT NULL, + range_count INT NOT NULL, + lease_count INT NOT NULL, + writes_per_second FLOAT NOT NULL, + bytes_per_replica JSON NOT NULL, + writes_per_replica JSON NOT NULL, + metrics JSON NOT NULL +) + `, + populate: func(ctx context.Context, p *planner, prefix string, addRow func(...tree.Datum) error) error { + if err := p.RequireSuperUser("read crdb_internal.kv_store_status"); err != nil { + return err + } + + response, err := p.ExecCfg().StatusServer.Nodes(ctx, &serverpb.NodesRequest{}) + if err != nil { + return err + } + + for _, n := range response.Nodes { + for _, s := range n.StoreStatuses { + var attrs []json.JSON + for _, a := range s.Desc.Attrs.Attrs { + attrs = append(attrs, json.FromString(a)) + } + + metrics := json.NewBuilder() + for k, v := range s.Metrics { + metric, err := json.FromFloat64(v) + if err != nil { + return err + } + metrics.Add(k, metric) + } + + percentilesToJSON := func(ps roachpb.Percentiles) (json.JSON, error) { + b := json.NewBuilder() + v, err := json.FromFloat64(ps.P10) + if err != nil { + return nil, err + } + b.Add("P10", v) + v, err = json.FromFloat64(ps.P25) + if err != nil { + return nil, err + } + b.Add("P25", v) + v, err = json.FromFloat64(ps.P50) + if err != nil { + return nil, err + } + b.Add("P50", v) + v, err = json.FromFloat64(ps.P75) + if err != nil { + return nil, err + } + b.Add("P75", v) + v, err = json.FromFloat64(ps.P90) + if err != nil { + return nil, err + } + b.Add("P90", v) + return b.Build(), nil + } + + bytesPerReplica, err := percentilesToJSON(s.Desc.Capacity.BytesPerReplica) + if err != nil { + return err + } + writesPerReplica, err := percentilesToJSON(s.Desc.Capacity.WritesPerReplica) + if err != nil { + return err + } + + if err := addRow( + tree.NewDInt(tree.DInt(s.Desc.Node.NodeID)), + tree.NewDInt(tree.DInt(s.Desc.StoreID)), + tree.NewDJSON(json.FromArrayOfJSON(attrs)), + tree.NewDInt(tree.DInt(s.Desc.Capacity.Capacity)), + tree.NewDInt(tree.DInt(s.Desc.Capacity.Available)), + tree.NewDInt(tree.DInt(s.Desc.Capacity.Used)), + tree.NewDInt(tree.DInt(s.Desc.Capacity.LogicalBytes)), + tree.NewDInt(tree.DInt(s.Desc.Capacity.RangeCount)), + tree.NewDInt(tree.DInt(s.Desc.Capacity.LeaseCount)), + tree.NewDFloat(tree.DFloat(s.Desc.Capacity.WritesPerSecond)), + tree.NewDJSON(bytesPerReplica), + tree.NewDJSON(writesPerReplica), + tree.NewDJSON(metrics.Build()), + ); err != nil { + return err + } + } + } + return nil + }, +} diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal b/pkg/sql/logictest/testdata/logic_test/crdb_internal index 6137642d999b..2db41605c6ea 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal @@ -228,10 +228,10 @@ node_id component field value 1 UI URI / query ITTTTT colnames -SELECT node_id, network, regexp_replace(address, '\d+$', '') as address, attrs, locality, regexp_replace(version, '^\d+\.\d+(-\d+)?$', '') as version FROM crdb_internal.gossip_nodes WHERE node_id = 1 +SELECT node_id, network, regexp_replace(address, '\d+$', '') as address, attrs, locality, regexp_replace(server_version, '^\d+\.\d+(-\d+)?$', '') as server_version FROM crdb_internal.gossip_nodes WHERE node_id = 1 ---- -node_id network address attrs locality version -1 tcp 127.0.0.1: [] · +node_id network address attrs locality server_version +1 tcp 127.0.0.1: [] {} query IITBB colnames SELECT node_id, epoch, regexp_replace(expiration, '^\d+\.\d+,\d+$', '') as expiration, draining, decommissioning FROM crdb_internal.gossip_liveness WHERE node_id = 1 @@ -239,6 +239,20 @@ SELECT node_id, epoch, regexp_replace(expiration, '^\d+\.\d+,\d+$', ' node_id epoch expiration draining decommissioning 1 1 false false +query ITTTTTT colnames +SELECT node_id, network, regexp_replace(address, '\d+$', '') as address, attrs, locality, regexp_replace(server_version, '^\d+\.\d+(-\d+)?$', '') as server_version, regexp_replace(go_version, '^go.+$', '') as go_version +FROM crdb_internal.kv_node_status WHERE node_id = 1 +---- +node_id network address attrs locality server_version go_version +1 tcp 127.0.0.1: [] {} + +query IITI colnames +SELECT node_id, store_id, attrs, used +FROM crdb_internal.kv_store_status WHERE node_id = 1 +---- +node_id store_id attrs used +1 1 [] 0 + # Check that privileged builtins are only allowed for 'root' user testuser @@ -266,6 +280,12 @@ select * from crdb_internal.gossip_nodes query error pq: only root is allowed to read crdb_internal.gossip_liveness select * from crdb_internal.gossip_liveness +query error pq: only root is allowed to read crdb_internal.kv_node_status +select * from crdb_internal.kv_node_status + +query error pq: only root is allowed to read crdb_internal.kv_store_status +select * from crdb_internal.kv_store_status + # Anyone can see the executable version. query T select crdb_internal.node_executable_version() diff --git a/pkg/sql/logictest/testdata/logic_test/explain b/pkg/sql/logictest/testdata/logic_test/explain index ecff08354677..3e856407f899 100644 --- a/pkg/sql/logictest/testdata/logic_test/explain +++ b/pkg/sql/logictest/testdata/logic_test/explain @@ -165,7 +165,7 @@ sort · · └── render · · └── filter · · └── values · · -· size 5 columns, 85 rows +· size 5 columns, 87 rows query TTT EXPLAIN SHOW DATABASE @@ -222,7 +222,7 @@ sort · · ├── render · · │ └── filter · · │ └── values · · - │ size 16 columns, 717 rows + │ size 16 columns, 751 rows └── render · · └── filter · · └── values · · diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index c4b4d505430d..7b29593bc088 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -217,6 +217,8 @@ crdb_internal gossip_liveness crdb_internal gossip_nodes crdb_internal index_columns crdb_internal jobs +crdb_internal kv_node_status +crdb_internal kv_store_status crdb_internal leases crdb_internal node_build_info crdb_internal node_queries @@ -390,6 +392,8 @@ def crdb_internal gossip_liveness SYSTEM VIEW 1 def crdb_internal gossip_nodes SYSTEM VIEW 1 def crdb_internal index_columns SYSTEM VIEW 1 def crdb_internal jobs SYSTEM VIEW 1 +def crdb_internal kv_node_status SYSTEM VIEW 1 +def crdb_internal kv_store_status SYSTEM VIEW 1 def crdb_internal leases SYSTEM VIEW 1 def crdb_internal node_build_info SYSTEM VIEW 1 def crdb_internal node_queries SYSTEM VIEW 1