Commit 994d3064 authored by craig[bot]'s avatar craig[bot]

Merge #47606 #49770 #49819

47606: ui: CSS modules for Table components r=koorosh a=koorosh

Depends on #47513
Related to #47527

This change refactors components to use CSS modules and
incorporate all required styles without any external
dependencies and prevent styles altering from outside.
It affects several components which tightly
coupled with StatementsTable and couldn't be changed
separately.

Following component are changed:
- HighlightedText
- Drawer
- StatementsTable
- SortableTable

Note, that `StatementsTable#makeCommonColumns` function
is refactored to provide custom styles from parent to
child components via props instead of overriding styles.

Storybook is extended to show some components as independent
units or in context of `StatementTable` component (if it is
only the way components work).

Release note: None

49770: changefeedccl: change default flush interval to 5s r=dt a=dt

We observed a customer cluster's changefeeds to cloud storage 'getting stuck'
which on further investigation was determined to be happening because they
were spending too much time in flushing. This was because they were writing to
a cloud sink and the default flush interval of 200ms (poller interval of 1s / 5)
meant it spent all of its time flushing. This default was picked testing with
lower-latency sinks and was noted in a comment as somewhat arbitrary.

This change does two things: it increases the default to the poller interval
if unspecified, instead of poller interval / 5, meaning 1s instead of 200ms
at the default setting, and if the sink being used is cloud storage, it
changes it to the greater of that or 5s. Users who truely desire lower latency
can of course specify their own 'resolved' interval, so this change in the
default is for those that are indifferent, and increasing the latency to 1s or 5s
reduces the chance of hiitting this unfortunate edge case when the sink is too slow.

Release note (enterprise change): The default flush interval for changefeeds that do not specify a 'resolved' option is now 1s instead of 200ms, or 5s if the changefeed sink is cloud-storage.

49819: Use faster set for column IDs in schemaexpr r=mgartner a=mgartner

#### sqlbase: add ColSet wrapper for util.FastIntSet of ColumnID

There are numerous places where a `map[sqlbase.ColumnID]struct{}` or a
`util.FastIntSet` is used to represent a set of `sqlbase.ColumnID`. This
commit adds a typed wrapper around `util.FastIntSet` which is an
efficient and ergonimic replacement for maps and `util.FastIntSet`.

Release note: None

#### schemaexpr: use sqlbase.ColSet instead of maps

This commit replaces maps used as sets of integers with sqlbase.ColSet
because it is a more efficient set implementation.

Release note: None
Co-authored-by: default avatarAndrii Vorobiov <[email protected]>
Co-authored-by: default avatarDavid Taylor <[email protected]>
Co-authored-by: default avatarMarcus Gartner <[email protected]>
......@@ -312,21 +312,22 @@ func emitEntries(
}
// If the resolved timestamp frequency is specified, use it as a rough
// approximation of how latency-sensitive the changefeed user is. If
// it's not, fall back to the poll interval.
// approximation of how latency-sensitive the changefeed user is. If it's
// not, fall back to a default of 5s
//
// The current poller implementation means we emit a changefeed-level
// resolved timestamps to the user once per changefeedPollInterval. This
// buffering adds on average timeBetweenFlushes/2 to that latency. With
// timeBetweenFlushes and changefeedPollInterval both set to 1s, TPCC
// was seeing about 100x more time spent emitting than flushing.
// Dividing by 5 tries to balance these a bit, but ultimately is fairly
// unprincipled.
// With timeBetweenFlushes and changefeedPollInterval both set to 1s, TPCC
// was seeing about 100x more time spent emitting than flushing when tested
// with low-latency sinks like Kafka. However when using cloud-storage
// sinks, flushes can take much longer and trying to flush too often can
// thus end up spending too much time flushing and not enough in emitting to
// keep up with the feed. If a user does not specify a 'resolved' time, we
// instead default to 5s, which is hopefully long enough to account for most
// possible sink latencies we could see without falling behind.
//
// NB: As long as we periodically get new span-level resolved timestamps
// from the poller (which should always happen, even if the watched data
// is not changing), then this is sufficient and we don't have to do
// anything fancy with timers.
// from the poller (which should always happen, even if the watched data is
// not changing), then this is sufficient and we don't have to do anything
// fancy with timers.
var timeBetweenFlushes time.Duration
if r, ok := details.Opts[changefeedbase.OptResolvedTimestamps]; ok && r != `` {
var err error
......@@ -334,7 +335,7 @@ func emitEntries(
return nil, err
}
} else {
timeBetweenFlushes = changefeedbase.TableDescriptorPollInterval.Get(&settings.SV) / 5
timeBetweenFlushes = time.Second * 5
}
if len(resolvedSpans) == 0 ||
(timeutil.Since(lastFlush) < timeBetweenFlushes && !boundaryReached) {
......
......@@ -1418,7 +1418,7 @@ func TestChangefeedMonitoring(t *testing.T) {
// Not reading from foo will backpressure it and max_behind_nanos will grow.
sqlDB.Exec(t, `INSERT INTO foo VALUES (2)`)
const expectedLatency = 100 * time.Millisecond
const expectedLatency = 5 * time.Second
sqlDB.Exec(t, `SET CLUSTER SETTING kv.closed_timestamp.target_duration = $1`,
(expectedLatency / 3).String())
sqlDB.Exec(t, `SET CLUSTER SETTING kv.closed_timestamp.close_fraction = 1.0`)
......
......@@ -14,7 +14,6 @@ import (
"bytes"
"context"
"fmt"
"sort"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
......@@ -102,7 +101,7 @@ func (b *CheckConstraintBuilder) Build(
// Replace the column variables with dummyColumns so that they can be
// type-checked.
replacedExpr, colIDsUsed, err := b.desc.ReplaceColumnVarsInExprWithDummies(expr)
replacedExpr, colIDs, err := replaceVars(&b.desc.TableDescriptor, expr)
if err != nil {
return nil, err
}
......@@ -121,17 +120,10 @@ func (b *CheckConstraintBuilder) Build(
return nil, err
}
// Collect and sort the column IDs referenced in the check expression.
colIDs := make(sqlbase.ColumnIDs, 0, colIDsUsed.Len())
colIDsUsed.ForEach(func(id int) {
colIDs = append(colIDs, sqlbase.ColumnID(id))
})
sort.Sort(colIDs)
return &sqlbase.TableDescriptor_CheckConstraint{
Expr: tree.Serialize(typedExpr),
Name: name,
ColumnIDs: colIDs,
ColumnIDs: colIDs.Ordered(),
Hidden: c.Hidden,
}, nil
}
......
......@@ -60,46 +60,6 @@ func DequalifyColumnRefs(
)
}
// iterColDescriptors iterates over the expression's variable columns and
// calls f on each.
//
// If the expression references a column that does not exist in the table
// descriptor, iterColDescriptors errs with pgcode.UndefinedColumn.
func iterColDescriptors(
desc *sqlbase.MutableTableDescriptor, rootExpr tree.Expr, f func(*sqlbase.ColumnDescriptor) error,
) error {
_, err := tree.SimpleVisit(rootExpr, func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
vBase, ok := expr.(tree.VarName)
if !ok {
// Not a VarName, don't do anything to this node.
return true, expr, nil
}
v, err := vBase.NormalizeVarName()
if err != nil {
return false, nil, err
}
c, ok := v.(*tree.ColumnItem)
if !ok {
return true, expr, nil
}
col, dropped, err := desc.FindColumnByName(c.ColumnName)
if err != nil || dropped {
return false, nil, pgerror.Newf(pgcode.UndefinedColumn,
"column %q does not exist, referenced in %q", c.ColumnName, rootExpr.String())
}
if err := f(col); err != nil {
return false, nil, err
}
return false, expr, err
})
return err
}
// DeserializeTableDescExpr takes in a serialized expression and a table, and
// returns an expression that has all user defined types resolved for
// formatting. It is intended to be used when displaying a serialized
......@@ -116,7 +76,7 @@ func DeserializeTableDescExpr(
if err != nil {
return nil, err
}
expr, _, err = desc.ReplaceColumnVarsInExprWithDummies(expr)
expr, _, err = replaceVars(desc, expr)
if err != nil {
return nil, err
}
......@@ -163,3 +123,127 @@ func FormatColumnForDisplay(
}
return f.CloseAndGetString(), nil
}
// iterColDescriptors iterates over the expression's variable columns and
// calls f on each.
//
// If the expression references a column that does not exist in the table
// descriptor, iterColDescriptors errs with pgcode.UndefinedColumn.
func iterColDescriptors(
desc *sqlbase.MutableTableDescriptor, rootExpr tree.Expr, f func(*sqlbase.ColumnDescriptor) error,
) error {
_, err := tree.SimpleVisit(rootExpr, func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
vBase, ok := expr.(tree.VarName)
if !ok {
// Not a VarName, don't do anything to this node.
return true, expr, nil
}
v, err := vBase.NormalizeVarName()
if err != nil {
return false, nil, err
}
c, ok := v.(*tree.ColumnItem)
if !ok {
return true, expr, nil
}
col, dropped, err := desc.FindColumnByName(c.ColumnName)
if err != nil || dropped {
return false, nil, pgerror.Newf(pgcode.UndefinedColumn,
"column %q does not exist, referenced in %q", c.ColumnName, rootExpr.String())
}
if err := f(col); err != nil {
return false, nil, err
}
return false, expr, err
})
return err
}
// dummyColumn represents a variable column that can type-checked. It is used
// in validating check constraint and partial index predicate expressions. This
// validation requires that the expression can be both both typed-checked and
// examined for variable expressions.
type dummyColumn struct {
typ *types.T
name tree.Name
}
// String implements the Stringer interface.
func (d *dummyColumn) String() string {
return tree.AsString(d)
}
// Format implements the NodeFormatter interface.
func (d *dummyColumn) Format(ctx *tree.FmtCtx) {
d.name.Format(ctx)
}
// Walk implements the Expr interface.
func (d *dummyColumn) Walk(_ tree.Visitor) tree.Expr {
return d
}
// TypeCheck implements the Expr interface.
func (d *dummyColumn) TypeCheck(
_ context.Context, _ *tree.SemaContext, desired *types.T,
) (tree.TypedExpr, error) {
return d, nil
}
// Eval implements the TypedExpr interface.
func (*dummyColumn) Eval(_ *tree.EvalContext) (tree.Datum, error) {
panic("dummyColumnItem.Eval() is undefined")
}
// ResolvedType implements the TypedExpr interface.
func (d *dummyColumn) ResolvedType() *types.T {
return d.typ
}
// replaceVars replaces the occurrences of column names in an expression with
// dummyColumns containing their type, so that they may be type-checked. It
// returns this new expression tree alongside a set containing the ColumnID of
// each column seen in the expression.
//
// If the expression references a column that does not exist in the table
// descriptor, replaceVars errs with pgcode.UndefinedColumn.
func replaceVars(
desc *sqlbase.TableDescriptor, rootExpr tree.Expr,
) (tree.Expr, sqlbase.TableColSet, error) {
var colIDs sqlbase.TableColSet
newExpr, err := tree.SimpleVisit(rootExpr, func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
vBase, ok := expr.(tree.VarName)
if !ok {
// Not a VarName, don't do anything to this node.
return true, expr, nil
}
v, err := vBase.NormalizeVarName()
if err != nil {
return false, nil, err
}
c, ok := v.(*tree.ColumnItem)
if !ok {
return true, expr, nil
}
col, dropped, err := desc.FindColumnByName(c.ColumnName)
if err != nil || dropped {
return false, nil, pgerror.Newf(pgcode.UndefinedColumn,
"column %q does not exist, referenced in %q", c.ColumnName, rootExpr.String())
}
colIDs.Add(col.ID)
// Convert to a dummyColumn of the correct type.
return false, &dummyColumn{typ: col.Type, name: c.ColumnName}, nil
})
return newExpr, colIDs, err
}
......@@ -60,15 +60,14 @@ func (v *ComputedColumnValidator) Validate(d *tree.ColumnTableDef) error {
)
}
// TODO(mgartner): Use util.FastIntSet here instead.
dependencies := make(map[sqlbase.ColumnID]struct{})
var depColIDs sqlbase.TableColSet
// First, check that no column in the expression is a computed column.
err := iterColDescriptors(v.desc, d.Computed.Expr, func(c *sqlbase.ColumnDescriptor) error {
if c.IsComputed() {
return pgerror.New(pgcode.InvalidTableDefinition,
"computed columns cannot reference other computed columns")
}
dependencies[c.ID] = struct{}{}
depColIDs.Add(c.ID)
return nil
})
......@@ -82,7 +81,7 @@ func (v *ComputedColumnValidator) Validate(d *tree.ColumnTableDef) error {
for i := range v.desc.OutboundFKs {
fk := &v.desc.OutboundFKs[i]
for _, id := range fk.OriginColumnIDs {
if _, ok := dependencies[id]; !ok {
if !depColIDs.Contains(id) {
// We don't depend on this column.
continue
}
......@@ -103,7 +102,7 @@ func (v *ComputedColumnValidator) Validate(d *tree.ColumnTableDef) error {
// Replace the column variables with dummyColumns so that they can be
// type-checked.
replacedExpr, _, err := v.desc.ReplaceColumnVarsInExprWithDummies(d.Computed.Expr)
replacedExpr, _, err := replaceVars(&v.desc.TableDescriptor, d.Computed.Expr)
if err != nil {
return err
}
......
......@@ -11,25 +11,6 @@
/*
Package schemaexpr provides utilities for dealing with expressions with table
schemas, such as check constraints, computed columns, and partial index
predicates. It provides the following utilities.
CheckConstraintBuilder
Validates and builds sql.TableDescriptor_CheckConstraints from
tree.CheckConstraintTableDefs.
ComputedColumnValidator
Validates computed columns and can determine if a non-computed column has
dependent computed columns.
PartialIndexValidator
Validates partial index predicates and dequalifies the columns referenced.
DequalifyColumnRefs
Strips database and table names from qualified columns.
predicates.
*/
package schemaexpr
......@@ -58,7 +58,7 @@ func NewIndexPredicateValidator(
func (v *IndexPredicateValidator) Validate(expr tree.Expr) (tree.Expr, error) {
// Replace the column variables with dummyColumns so that they can be
// type-checked.
replacedExpr, _, err := v.desc.ReplaceColumnVarsInExprWithDummies(expr)
replacedExpr, _, err := replaceVars(&v.desc.TableDescriptor, expr)
if err != nil {
return nil, err
}
......
......@@ -28,7 +28,6 @@ import (
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/types"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/encoding"
"github.com/cockroachdb/cockroach/pkg/util/errorutil/unimplemented"
"github.com/cockroachdb/cockroach/pkg/util/hlc"
......@@ -841,85 +840,6 @@ func (desc *TableDescriptor) AllActiveAndInactiveChecks() []*TableDescriptor_Che
return checks
}
// ReplaceColumnVarsInExprWithDummies replaces the occurrences of column names in an expression with
// dummies containing their type, so that they may be typechecked. It returns
// this new expression tree alongside a set containing the ColumnID of each
// column seen in the expression.
func (desc *TableDescriptor) ReplaceColumnVarsInExprWithDummies(
rootExpr tree.Expr,
) (tree.Expr, util.FastIntSet, error) {
var colIDs util.FastIntSet
newExpr, err := tree.SimpleVisit(rootExpr, func(expr tree.Expr) (recurse bool, newExpr tree.Expr, err error) {
vBase, ok := expr.(tree.VarName)
if !ok {
// Not a VarName, don't do anything to this node.
return true, expr, nil
}
v, err := vBase.NormalizeVarName()
if err != nil {
return false, nil, err
}
c, ok := v.(*tree.ColumnItem)
if !ok {
return true, expr, nil
}
col, dropped, err := desc.FindColumnByName(c.ColumnName)
if err != nil || dropped {
return false, nil, pgerror.Newf(pgcode.UndefinedColumn,
"column %q does not exist, referenced in %q", c.ColumnName, rootExpr.String())
}
colIDs.Add(int(col.ID))
// Convert to a dummy node of the correct type.
return false, &dummyColumnItem{typ: col.Type, name: c.ColumnName}, nil
})
return newExpr, colIDs, err
}
// dummyColumnItem is used in makeCheckConstraint and validateIndexPredicate to
// construct an expression that can be both type-checked and examined for
// variable expressions. It can also be used to format typed expressions
// containing column references.
type dummyColumnItem struct {
typ *types.T
name tree.Name
}
// String implements the Stringer interface.
func (d *dummyColumnItem) String() string {
return tree.AsString(d)
}
// Format implements the NodeFormatter interface.
// It should be kept in line with ColumnItem.Format.
func (d *dummyColumnItem) Format(ctx *tree.FmtCtx) {
ctx.FormatNode(&d.name)
}
// Walk implements the Expr interface.
func (d *dummyColumnItem) Walk(_ tree.Visitor) tree.Expr {
return d
}
// TypeCheck implements the Expr interface.
func (d *dummyColumnItem) TypeCheck(
_ context.Context, _ *tree.SemaContext, desired *types.T,
) (tree.TypedExpr, error) {
return d, nil
}
// Eval implements the TypedExpr interface.
func (*dummyColumnItem) Eval(_ *tree.EvalContext) (tree.Datum, error) {
panic("dummyColumnItem.Eval() is undefined")
}
// ResolvedType implements the TypedExpr interface.
func (d *dummyColumnItem) ResolvedType() *types.T {
return d.typ
}
// GetColumnFamilyForShard returns the column family that a newly added shard column
// should be assigned to, given the set of columns it's computed from.
//
......
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package sqlbase
import "github.com/cockroachdb/cockroach/pkg/util"
// TableColSet efficiently stores an unordered set of column ids.
type TableColSet struct {
set util.FastIntSet
}
// MakeTableColSet returns a set initialized with the given values.
func MakeTableColSet(vals ...ColumnID) TableColSet {
var res TableColSet
for _, v := range vals {
res.Add(v)
}
return res
}
// Add adds a column to the set. No-op if the column is already in the set.
func (s *TableColSet) Add(col ColumnID) { s.set.Add(int(col)) }
// Contains returns true if the set contains the column.
func (s TableColSet) Contains(col ColumnID) bool { return s.set.Contains(int(col)) }
// Empty returns true if the set is empty.
func (s TableColSet) Empty() bool { return s.set.Empty() }
// Len returns the number of the columns in the set.
func (s TableColSet) Len() int { return s.set.Len() }
// ForEach calls a function for each column in the set (in increasing order).
func (s TableColSet) ForEach(f func(col ColumnID)) { s.set.ForEach(func(i int) { f(ColumnID(i)) }) }
// Ordered returns a slice with all the ColumnIDs in the set, in increasing
// order.
func (s TableColSet) Ordered() []ColumnID {
if s.Empty() {
return nil
}
result := make([]ColumnID, 0, s.Len())
s.ForEach(func(i ColumnID) {
result = append(result, i)
})
return result
}
// String returns a list representation of elements. Sequential runs of positive
// numbers are shown as ranges. For example, for the set {1, 2, 3 5, 6, 10},
// the output is "(1-3,5,6,10)".
func (s TableColSet) String() string { return s.set.String() }
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package sqlbase
import (
"testing"
"github.com/cockroachdb/cockroach/pkg/util"
)
func BenchmarkColSet(b *testing.B) {
// Verify that the wrapper doesn't add overhead (as was the case with earlier
// go versions which couldn't do mid-stack inlining).
const n = 50
b.Run("fastintset", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var c util.FastIntSet
for j := 0; j < n; j++ {
c.Add(j)
}
}
})
b.Run("colset", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var c TableColSet
for j := 0; j < n; j++ {
c.Add(ColumnID(j))
}
}
})
}
func TestColSet_Ordered(t *testing.T) {
testData := []struct {
set TableColSet
expected []ColumnID
}{
{MakeTableColSet(1, 2, 3), []ColumnID{1, 2, 3}},
{MakeTableColSet(3, 5, 6, 17), []ColumnID{3, 5, 6, 17}},
{MakeTableColSet(9, 4, 6, 1), []ColumnID{1, 4, 6, 9}},
}
for _, d := range testData {
t.Run(d.set.String(), func(t *testing.T) {
res := d.set.Ordered()
if len(res) != len(d.expected) {
t.Fatalf("%s: expected %v, got %v", d.set, d.expected, res)
}
for i := 0; i < len(res); i++ {
if res[i] != d.expected[i] {
t.Errorf("%s: expected %v, got %v", d.set, d.expected, res)
}
}
})
}
}
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
._text-bold
color #87beff
font-family RobotoMono-Bold
......@@ -9,6 +9,10 @@
// licenses/APL.txt.
import React from "react";
import classNames from "classnames/bind";
import styles from "./highlightedText.module.styl";
const cx = classNames.bind(styles);
export default function getHighlightedText(text: string, highlight: string, isOriginalText?: boolean) {
if (!highlight || highlight.length === 0) {
......@@ -25,7 +29,7 @@ export default function getHighlightedText(text: string, highlight: string, isOr
return parts.map((part, i) => {
if (search.includes(part.toLowerCase())) {
return (
<span key={i} className="_text-bold">
<span key={i} className={cx("_text-bold")}>
{`${part}`}
</span>
);
......
......@@ -18,6 +18,7 @@ import { Bytes } from "src/util/format";
import Loading from "src/views/shared/components/loading";
import { CachedDataReducerState } from "src/redux/cachedDataReducer";
import { NonTableStatsResponseMessage } from "src/util/api";
import "src/views/shared/components/sortabletable/sortabletable.styl";
interface TimeSeriesSummaryProps {
nonTableStats: protos.cockroach.server.serverpb.NonTableStatsResponse;
......
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
.__actions
button, a
width auto
color #f6f6f6
font-size 12px
font-family Lato-Bold
line-height 2
letter-spacing 0.1px
&:hover
color #5f6c87
.drawer--preset-black
display flex
align-items center
/* width */
::-webkit-scrollbar {
width 7px
}
/* Track */
::-webkit-scrollbar-track {
background transparent
border-radius 10px
}
/* Handle */
::-webkit-scrollbar-thumb {
background #5f6c87
border-radius 10px
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background #5f6c87ad
}
:global(.ant-divider)
width 1px
background #5f6c87
margin: 0 15px