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

sql, distsql: add enum cluster settings, add distsql cluster setting #15307

Merged
merged 2 commits into from
Apr 28, 2017
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
155 changes: 137 additions & 18 deletions pkg/settings/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
package settings

import (
"bytes"
"fmt"
"math"
"sort"
"strconv"
"strings"
"sync/atomic"
"time"

"github.com/cockroachdb/cockroach/pkg/util/humanizeutil"
"github.com/pkg/errors"
)

// Setting implementions wrap a val with atomic access.
Expand All @@ -40,6 +44,8 @@ type BoolSetting struct {
v int32
}

var _ Setting = &BoolSetting{}

// Get retrieves the bool value in the setting.
func (b *BoolSetting) Get() bool {
return atomic.LoadInt32(&b.v) != 0
Expand Down Expand Up @@ -67,13 +73,19 @@ func (b *BoolSetting) setToDefault() {
}

// RegisterBoolSetting defines a new setting with type bool.
func RegisterBoolSetting(key, desc string, defVal bool) *BoolSetting {
setting := &BoolSetting{defaultValue: defVal}
func RegisterBoolSetting(key, desc string, defaultValue bool) *BoolSetting {
setting := &BoolSetting{defaultValue: defaultValue}
register(key, desc, setting)
return setting
}

// TestingSetBool returns a mock, unregistered bool setting for testing.
// TestingSetBool returns a mock, unregistered bool setting for testing. It
// takes a pointer to a BoolSetting reference, swapping in the mock setting.
// It returns a cleanup function that swaps back the original setting. This
// function should not be used by tests that run in parallel, as it could
// result in race detector failures, as well as if the cleanup functions are
// called out of order. The original Setting remains registered for
// gossip-driven updates which become visible when it is restored.
func TestingSetBool(s **BoolSetting, v bool) func() {
saved := *s
if v {
Expand All @@ -99,6 +111,8 @@ type IntSetting struct {
v int64
}

var _ Setting = &IntSetting{}

// Get retrieves the int value in the setting.
func (i *IntSetting) Get() int64 {
return atomic.LoadInt64(&i.v)
Expand All @@ -122,13 +136,14 @@ func (i *IntSetting) setToDefault() {
}

// RegisterIntSetting defines a new setting with type int.
func RegisterIntSetting(key, desc string, defVal int64) *IntSetting {
setting := &IntSetting{defaultValue: defVal}
func RegisterIntSetting(key, desc string, defaultValue int64) *IntSetting {
setting := &IntSetting{defaultValue: defaultValue}
register(key, desc, setting)
return setting
}

// TestingSetInt returns a mock, unregistered int setting for testing.
// TestingSetInt returns a mock, unregistered int setting for testing. See
// TestingSetBool for more details.
func TestingSetInt(s **IntSetting, v int64) func() {
saved := *s
*s = &IntSetting{v: v}
Expand All @@ -145,6 +160,8 @@ type FloatSetting struct {
v uint64
}

var _ Setting = &FloatSetting{}

// Get retrieves the float value in the setting.
func (f *FloatSetting) Get() float64 {
return math.Float64frombits(atomic.LoadUint64(&f.v))
Expand All @@ -167,7 +184,8 @@ func (f *FloatSetting) setToDefault() {
f.set(f.defaultValue)
}

// TestingSetFloat returns a mock, unregistered float setting for testing.
// TestingSetFloat returns a mock, unregistered float setting for testing. See
// TestingSetBool for more details.
func TestingSetFloat(s **FloatSetting, v float64) func() {
saved := *s
tmp := &FloatSetting{}
Expand All @@ -179,8 +197,8 @@ func TestingSetFloat(s **FloatSetting, v float64) func() {
}

// RegisterFloatSetting defines a new setting with type float.
func RegisterFloatSetting(key, desc string, defVal float64) *FloatSetting {
setting := &FloatSetting{defaultValue: defVal}
func RegisterFloatSetting(key, desc string, defaultValue float64) *FloatSetting {
setting := &FloatSetting{defaultValue: defaultValue}
register(key, desc, setting)
return setting
}
Expand All @@ -193,6 +211,8 @@ type DurationSetting struct {
v int64
}

var _ Setting = &DurationSetting{}

// Get retrieves the duration value in the setting.
func (d *DurationSetting) Get() time.Duration {
return time.Duration(atomic.LoadInt64(&d.v))
Expand All @@ -216,13 +236,14 @@ func (d *DurationSetting) setToDefault() {
}

// RegisterDurationSetting defines a new setting with type duration.
func RegisterDurationSetting(key, desc string, defVal time.Duration) *DurationSetting {
setting := &DurationSetting{defaultValue: defVal}
func RegisterDurationSetting(key, desc string, defaultValue time.Duration) *DurationSetting {
setting := &DurationSetting{defaultValue: defaultValue}
register(key, desc, setting)
return setting
}

// TestingSetDuration returns a mock, unregistered string setting for testing.
// See TestingSetBool for more details.
func TestingSetDuration(s **DurationSetting, v time.Duration) func() {
saved := *s
*s = &DurationSetting{v: int64(v)}
Expand All @@ -239,6 +260,8 @@ type StringSetting struct {
v atomic.Value
}

var _ Setting = &StringSetting{}

func (s *StringSetting) String() string {
return s.Get()
}
Expand All @@ -262,13 +285,14 @@ func (s *StringSetting) setToDefault() {
}

// RegisterStringSetting defines a new setting with type string.
func RegisterStringSetting(key, desc string, defVal string) *StringSetting {
setting := &StringSetting{defaultValue: defVal}
func RegisterStringSetting(key, desc string, defaultValue string) *StringSetting {
setting := &StringSetting{defaultValue: defaultValue}
register(key, desc, setting)
return setting
}

// TestingSetString returns a mock, unregistered string setting for testing.
// TestingSetString returns a mock, unregistered string setting for testing. See
// TestingSetBool for more details.
func TestingSetString(s **StringSetting, v string) func() {
saved := *s
tmp := &StringSetting{}
Expand All @@ -279,13 +303,107 @@ func TestingSetString(s **StringSetting, v string) func() {
}
}

// EnumSetting is a StringSetting that restricts the values to be one of the `enumValues`
type EnumSetting struct {
IntSetting
enumValues map[int64]string
}

var _ Setting = &EnumSetting{}

// Typ returns the short (1 char) string denoting the type of setting.
func (e *EnumSetting) Typ() string {
return "e"
}

// ParseEnum returns the enum value, and a boolean that indicates if it was parseable.
func (e *EnumSetting) ParseEnum(raw string) (int64, bool) {
rawLower := strings.ToLower(raw)
for k, v := range e.enumValues {
if v == rawLower {
return k, true
}
}
// Attempt to parse the string as an integer since it isn't a valid enum string.
v, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
return 0, false
}
_, ok := e.enumValues[v]
return v, ok
}

func (e *EnumSetting) set(k int64) error {
if _, ok := e.enumValues[k]; !ok {
return errors.Errorf("unrecognized value %d", k)
}
e.IntSetting.set(k)
return nil
}

func enumValuesToDesc(enumValues map[int64]string) string {
var buffer bytes.Buffer
buffer.WriteString("[")
var notFirstElem bool
for k, v := range enumValues {
if notFirstElem {
buffer.WriteString(", ")
}
fmt.Fprintf(&buffer, "%s = %d", strings.ToLower(v), k)
notFirstElem = true
}
buffer.WriteString("]")
return buffer.String()
}

// RegisterEnumSetting defines a new setting with type int.
func RegisterEnumSetting(
key, desc string, defaultValue string, enumValues map[int64]string,
) *EnumSetting {
enumValuesLower := make(map[int64]string)
var i int64
var found bool
for k, v := range enumValues {
enumValuesLower[k] = strings.ToLower(v)
if v == defaultValue {
i = k
found = true
}
}

if !found {
panic(fmt.Sprintf("enum registered with default value %s not in map %s", defaultValue, enumValuesToDesc(enumValuesLower)))
}
setting := &EnumSetting{
IntSetting: IntSetting{defaultValue: i},
enumValues: enumValuesLower,
}
register(key, fmt.Sprintf("%s %s", desc, enumValuesToDesc(enumValues)), setting)
return setting
}

// TestingSetEnum returns a mock, unregistered enum setting for testing. See
// TestingSetBool for more details.
func TestingSetEnum(s **EnumSetting, i int64) func() {
saved := *s
*s = &EnumSetting{
IntSetting: IntSetting{v: i},
enumValues: saved.enumValues,
}
return func() {
*s = saved
}
}

// ByteSizeSetting is the interface of a setting variable that will be
// updated automatically when the corresponding cluster-wide setting
// of type "bytesize" is updated.
type ByteSizeSetting struct {
IntSetting
}

var _ Setting = &ByteSizeSetting{}

// Typ returns the short (1 char) string denoting the type of setting.
func (*ByteSizeSetting) Typ() string {
return "z"
Expand All @@ -296,13 +414,14 @@ func (b *ByteSizeSetting) String() string {
}

// RegisterByteSizeSetting defines a new setting with type bytesize.
func RegisterByteSizeSetting(key, desc string, defVal int64) *ByteSizeSetting {
setting := &ByteSizeSetting{IntSetting{defaultValue: defVal}}
func RegisterByteSizeSetting(key, desc string, defaultValue int64) *ByteSizeSetting {
setting := &ByteSizeSetting{IntSetting{defaultValue: defaultValue}}
register(key, desc, setting)
return setting
}

// TestingSetByteSize returns a mock bytesize setting for testing.
// TestingSetByteSize returns a mock bytesize setting for testing. See
// TestingSetBool for more details.
func TestingSetByteSize(s **ByteSizeSetting, v int64) func() {
saved := *s
*s = &ByteSizeSetting{IntSetting{v: v}}
Expand Down Expand Up @@ -362,7 +481,7 @@ func Hide(key string) {

// Value holds the (parsed, typed) value of a setting.
// raw settings are stored in system.settings as human-readable strings, but are
// cached interally after parsing in these appropriately typed fields (which is
// cached internally after parsing in these appropriately typed fields (which is
// basically a poor-man's union, without boxing).
type wrappedSetting struct {
description string
Expand Down
28 changes: 28 additions & 0 deletions pkg/settings/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var i1A = settings.RegisterIntSetting("i.1", "", 0)
var i2A = settings.RegisterIntSetting("i.2", "", 5)
var fA = settings.RegisterFloatSetting("f", "", 5.4)
var dA = settings.RegisterDurationSetting("d", "", time.Second)
var eA = settings.RegisterEnumSetting("e", "", "foo", map[int64]string{1: "foo", 2: "bar", 3: "baz"})
var byteSize = settings.RegisterByteSizeSetting("zzz", "", mb)
var _ = settings.RegisterBoolSetting("sekretz", "", false)

Expand Down Expand Up @@ -68,6 +69,9 @@ func TestCache(t *testing.T) {
if expected, actual := mb, byteSize.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
if expected, actual := int64(1), eA.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
})

t.Run("lookup", func(t *testing.T) {
Expand All @@ -80,6 +84,9 @@ func TestCache(t *testing.T) {
if actual, _, ok := settings.Lookup("d"); !ok || dA != actual {
t.Fatalf("expected %v, got %v (exists: %v)", dA, actual, ok)
}
if actual, _, ok := settings.Lookup("e"); !ok || eA != actual {
t.Fatalf("expected %v, got %v (exists: %v)", eA, actual, ok)
}
if actual, _, ok := settings.Lookup("dne"); ok {
t.Fatalf("expected nothing, got %v", actual)
}
Expand Down Expand Up @@ -108,6 +115,13 @@ func TestCache(t *testing.T) {
if err := u.Set("zzz", settings.EncodeInt(mb*5), "z"); err != nil {
t.Fatal(err)
}
if err := u.Set("e", settings.EncodeInt(2), "e"); err != nil {
t.Fatal(err)
}
if expected, err := "strconv.Atoi: parsing \"notAValidValue\": invalid syntax",
u.Set("e", "notAValidValue", "e"); !testutils.IsError(err, expected) {
t.Fatalf("expected '%s' != actual error '%s'", expected, err)
}
u.Done()

if expected, actual := false, boolTA.Get(); expected != actual {
Expand All @@ -128,6 +142,9 @@ func TestCache(t *testing.T) {
if expected, actual := 2*time.Hour, dA.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
if expected, actual := int64(2), eA.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
if expected, actual := mb*5, byteSize.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
Expand Down Expand Up @@ -267,6 +284,17 @@ func TestCache(t *testing.T) {
}
}

{
f := settings.TestingSetEnum(&eA, 3)
if expected, actual := int64(3), eA.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
f()
if expected, actual := int64(1), eA.Get(); expected != actual {
t.Fatalf("expected %v, got %v", expected, actual)
}
}

{
f := settings.TestingSetByteSize(&byteSize, mb*7)
if expected, actual := mb*7, byteSize.Get(); expected != actual {
Expand Down
6 changes: 6 additions & 0 deletions pkg/settings/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ func (u Updater) Set(key, rawValue, vt string) error {
return err
}
setting.set(d)
case *EnumSetting:
i, err := strconv.Atoi(rawValue)
if err != nil {
return err
}
return setting.set(int64(i))
}
return nil
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/sql/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/cockroachdb/cockroach/pkg/kv"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/rpc"
"github.com/cockroachdb/cockroach/pkg/settings"
"github.com/cockroachdb/cockroach/pkg/sql/distsqlplan"
"github.com/cockroachdb/cockroach/pkg/sql/distsqlrun"
"github.com/cockroachdb/cockroach/pkg/sql/parser"
Expand Down Expand Up @@ -278,6 +279,9 @@ type ExecutorTestingKnobs struct {
// StatementFilter; otherwise, the statement commits immediately after
// execution so there'll be nothing left to abort by the time the filter runs.
DisableAutoCommit bool

// If OverrideDistSQLMode is set, it is used instead of the cluster setting.
OverrideDistSQLMode *settings.EnumSetting
}

// NewExecutor creates an Executor and registers a callback on the
Expand Down
Loading