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

ui, server: modify hot ranges api and table to use new contents approx. #134106

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
9 changes: 6 additions & 3 deletions docs/generated/http/full.md
Original file line number Diff line number Diff line change
Expand Up @@ -3653,9 +3653,9 @@ HotRange message describes a single hot range, ie its QPS, node ID it belongs to
| 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) |
| table_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | table_name has been deprecated in favor of tables = 16; | [reserved](#support-status) |
| database_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | database_name has been deprecated in favor of databases = 17; | [reserved](#support-status) |
| index_name | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | | index_name has been deprecated in favor of indexes = 17; | [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 All @@ -3665,6 +3665,9 @@ HotRange message describes a single hot range, ie its QPS, node ID it belongs to
| 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) |
| databases | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | repeated | Databases for the range. | [reserved](#support-status) |
| tables | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | repeated | Tables for the range | [reserved](#support-status) |
| indexes | [string](#cockroach.server.serverpb.HotRangesResponseV2-string) | repeated | Indexes for the range | [reserved](#support-status) |



Expand Down
1 change: 1 addition & 0 deletions pkg/roachpb/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ go_test(
"//pkg/raft/raftpb",
"//pkg/raft/tracker",
"//pkg/storage/enginepb",
"//pkg/testutils",
"//pkg/testutils/zerofields",
"//pkg/util",
"//pkg/util/bitarray",
Expand Down
31 changes: 21 additions & 10 deletions pkg/roachpb/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,18 @@ func (k Key) Less(b Key) bool {
}

// Clamp fixes the key to something within the range a < k < b.
func (k Key) Clamp(a, b Key) Key {
func (k Key) Clamp(min, max Key) (Key, error) {
if max.Less(min) {
return nil, errors.Newf("cannot clamp when min '%s' is larger than max '%s'", min, max)
}
result := k
if k.Less(a) {
result = a
if k.Less(min) {
result = min
}
if b.Less(k) {
result = b
if max.Less(k) {
result = max
}
return result
return result, nil
}

// SafeFormat implements the redact.SafeFormatter interface.
Expand Down Expand Up @@ -2383,11 +2386,19 @@ func (s Span) ZeroLength() bool {
}

// Clamp clamps span s's keys within the span defined in bounds.
func (s Span) Clamp(bounds Span) Span {
return Span{
s.Key.Clamp(bounds.Key, bounds.EndKey),
s.EndKey.Clamp(bounds.Key, bounds.EndKey),
func (s Span) Clamp(bounds Span) (Span, error) {
start, err := s.Key.Clamp(bounds.Key, bounds.EndKey)
if err != nil {
return Span{}, err
}
end, err := s.EndKey.Clamp(bounds.Key, bounds.EndKey)
if err != nil {
return Span{}, err
}
return Span{
Key: start,
EndKey: end,
}, nil
}

// Overlaps returns true WLOG for span A and B iff:
Expand Down
25 changes: 22 additions & 3 deletions pkg/roachpb/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import (

"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/testutils"
"github.com/cockroachdb/cockroach/pkg/util/encoding"
"github.com/stretchr/testify/require"
)

func TestKeyClampTenants(t *testing.T) {
Expand All @@ -33,7 +35,8 @@ func TestKeyClampTenants(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.k.Clamp(tt.a, tt.b)
result, err := tt.k.Clamp(tt.a, tt.b)
require.NoError(t, err)
if !result.Equal(tt.expected) {
t.Errorf("Clamp(%v, %v, %v) = %v; want %v", tt.k, tt.a, tt.b, result, tt.expected)
}
Expand All @@ -58,7 +61,8 @@ func TestKeyClampTables(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.k.Clamp(tt.a, tt.b)
result, err := tt.k.Clamp(tt.a, tt.b)
require.NoError(t, err)
if !result.Equal(tt.expected) {
t.Errorf("Clamp(%v, %v, %v) = %v; want %v", tt.k, tt.a, tt.b, result, tt.expected)
}
Expand Down Expand Up @@ -99,10 +103,25 @@ func TestKeyClampTenantTablespace(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.k.Clamp(tt.a, tt.b)
result, err := tt.k.Clamp(tt.a, tt.b)
require.NoError(t, err)
if !result.Equal(tt.expected) {
t.Errorf("Clamp(%v, %v, %v) = %v; want %v", tt.k, tt.a, tt.b, result, tt.expected)
}
})
}
}

func TestKeyClampError(t *testing.T) {
// verify that max < min throws error
a, b := roachpb.Key([]byte{'a'}), roachpb.Key([]byte{'b'})
expected := `cannot clamp when min '"b"' is larger than max '"a"'`
_, err := a.Clamp(b, a)
if !testutils.IsError(err, expected) {
t.Fatalf("expected error to be '%s', got '%s'", expected, err)
}

// verify that max = min throws no error
_, err = a.Clamp(a, a)
require.NoError(t, err)
}
15 changes: 15 additions & 0 deletions pkg/roachpb/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -1006,3 +1006,18 @@ func (h *GCHint) advanceGCTimestamp(gcThreshold hlc.Timestamp) bool {
h.GCTimestamp, h.GCTimestampNext = hlc.Timestamp{}, hlc.Timestamp{}
return true
}

type RangeDescriptorsByStartKey []RangeDescriptor

func (r RangeDescriptorsByStartKey) Len() int {
return len(r)
}
func (r RangeDescriptorsByStartKey) Less(i, j int) bool {
return r[i].StartKey.AsRawKey().Less(r[j].StartKey.AsRawKey())
}

func (r RangeDescriptorsByStartKey) Swap(i, j int) {
tmp := r[i]
r[i] = r[j]
r[j] = tmp
}
22 changes: 22 additions & 0 deletions pkg/roachpb/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package roachpb
import (
"fmt"
"reflect"
"sort"
"strings"
"testing"

Expand Down Expand Up @@ -662,3 +663,24 @@ func TestGCHint(t *testing.T) {
})
}
}

func TestRangeDescriptorsByStartKey(t *testing.T) {
// table-prefix-range-key
tprk := func(t byte) RKey {
return RKey(Key([]byte{t}))
}
ranges := []RangeDescriptor{
{StartKey: tprk(2), EndKey: tprk(7)},
{StartKey: tprk(5), EndKey: tprk(5)},
{StartKey: tprk(7), EndKey: tprk(2)},
{StartKey: tprk(1), EndKey: tprk(10)},
{StartKey: tprk(5), EndKey: tprk(5)},
}
sort.Stable(RangeDescriptorsByStartKey(ranges))

for i := 0; i < len(ranges)-1; i++ {
if ranges[i+1].StartKey.AsRawKey().Less(ranges[i].StartKey.AsRawKey()) {
t.Fatalf("expected ranges to be ordered increasing by start key, failed on %d, %d with keys %s, %s", i, i+1, ranges[i].StartKey.AsRawKey(), ranges[i+1].StartKey.AsRawKey())
}
}
}
17 changes: 15 additions & 2 deletions pkg/roachpb/span_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/testutils"
)

func TestSpanZeroLength(t *testing.T) {
Expand Down Expand Up @@ -38,6 +39,7 @@ func TestSpanClamp(t *testing.T) {
span roachpb.Span
bounds roachpb.Span
want roachpb.Span
error string
}{
{
name: "within bounds",
Expand All @@ -63,12 +65,23 @@ func TestSpanClamp(t *testing.T) {
bounds: roachpb.Span{tp(5), tp(15)},
want: roachpb.Span{tp(5), tp(15)},
},
{
name: "clamp start error",
span: roachpb.Span{},
bounds: roachpb.Span{tp(2), tp(1)},
want: roachpb.Span{},
error: "cannot clamp when min '/Table/2' is larger than max '/Table/1'",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.span.Clamp(tt.bounds); !got.Equal(tt.want) {
t.Errorf("Clamp() = %v, want %v", got, tt.want)
span, err := tt.span.Clamp(tt.bounds)
if !testutils.IsError(err, tt.error) {
t.Fatalf("expected error to be '%s', got '%s'", tt.error, err)
}
if !span.Equal(tt.want) {
t.Errorf("Clamp() = %v, want %v", span, tt.want)
}
})
}
Expand Down
1 change: 0 additions & 1 deletion pkg/server/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ go_library(
"//pkg/sql",
"//pkg/sql/appstatspb",
"//pkg/sql/auditlogging",
"//pkg/sql/catalog",
"//pkg/sql/catalog/bootstrap",
"//pkg/sql/catalog/catalogkeys",
"//pkg/sql/catalog/catsessiondata",
Expand Down
6 changes: 5 additions & 1 deletion pkg/server/apiutil/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_library(
name = "apiutil",
srcs = [
"apiutil.go",
"index_names.go",
"rangeutil.go",
],
importpath = "github.com/cockroachdb/cockroach/pkg/server/apiutil",
Expand All @@ -21,7 +22,10 @@ go_library(

go_test(
name = "apiutil_test",
srcs = ["rangeutil_test.go"],
srcs = [
"index_names_test.go",
"rangeutil_test.go",
],
deps = [
":apiutil",
"//pkg/keys",
Expand Down
91 changes: 91 additions & 0 deletions pkg/server/apiutil/index_names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2024 The Cockroach Authors.
//
// Use of this software is governed by the CockroachDB Software License
// included in the /LICENSE file.

package apiutil

import (
"fmt"
"strings"

"github.com/cockroachdb/cockroach/pkg/roachpb"
)

// IndexNames and IndexNamesList are utilities for representing an
// index span by its corresponding identifiers.
// They include the underlying span for comparison against other spans
// and omit the server and schema parts of their four part name.
type IndexNames struct {
Database string
Table string
Index string
Span roachpb.Span
}

func (idx IndexNames) String() string {
return fmt.Sprintf("%s.%s.%s", idx.Database, idx.Table, idx.Index)
}

type IndexNamesList []IndexNames

// quoteIfContainsDot adds quotes to an identifier if it contains
// the four part delimiter '.'.
func quoteIfContainsDot(identifier string) string {
if strings.Contains(identifier, ".") {
return `"` + identifier + `"`
}
return identifier
}

// ToOutput is a simple function which returns a set of
// de-duplicated, fully referenced database, table, and index names
// depending on the number of databases and tables which appear.
func (idxl IndexNamesList) ToOutput() ([]string, []string, []string) {
fpi := quoteIfContainsDot
seenDatabases := map[string]bool{}
databases := []string{}
for _, idx := range idxl {
database := fpi(idx.Database)
if !seenDatabases[database] {
seenDatabases[database] = true
databases = append(databases, database)
}
}

multipleDatabases := len(databases) > 1
seenTables := map[string]bool{}
tables := []string{}
for _, idx := range idxl {
table := fpi(idx.Table)
if multipleDatabases {
table = fpi(idx.Database) + "." + table
}
if !seenTables[table] {
seenTables[table] = true
tables = append(tables, table)
}
}

multipleTables := len(tables) > 1
indexes := []string{}
for _, idx := range idxl {
index := fpi(idx.Index)
if multipleTables {
index = fpi(idx.Table) + "." + index
if multipleDatabases {
index = fpi(idx.Database) + "." + index
}
}
indexes = append(indexes, index)
}

return databases, tables, indexes
}

// Equal only compares the names, not the spans
func (idx IndexNames) Equal(other IndexNames) bool {
return idx.Database == other.Database &&
idx.Table == other.Table &&
idx.Index == other.Index
}
Loading
Loading