github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/settings/setting.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package settings
    12  
    13  import (
    14  	"fmt"
    15  	"sync/atomic"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    18  )
    19  
    20  // MaxSettings is the maximum number of settings that the system supports.
    21  // Exported for tests.
    22  const MaxSettings = 256
    23  
    24  // Values is a container that stores values for all registered settings.
    25  // Each setting is assigned a unique slot (up to MaxSettings).
    26  // Note that slot indices are 1-based (this is to trigger panics if an
    27  // uninitialized slot index is used).
    28  type Values struct {
    29  	container valuesContainer
    30  
    31  	overridesMu struct {
    32  		syncutil.Mutex
    33  		// defaultOverrides maintains the set of overridden default values (see
    34  		// Override()).
    35  		defaultOverrides valuesContainer
    36  		// setOverrides is the list of slots with values in defaultOverrides.
    37  		setOverrides map[int]struct{}
    38  	}
    39  
    40  	changeMu struct {
    41  		syncutil.Mutex
    42  		// NB: any in place modification to individual slices must also hold the
    43  		// lock, e.g. if we ever add RemoveOnChange or something.
    44  		onChange [MaxSettings][]func()
    45  	}
    46  	// opaque is an arbitrary object that can be set by a higher layer to make it
    47  	// accessible from certain callbacks (like state machine transformers).
    48  	opaque interface{}
    49  }
    50  
    51  type valuesContainer struct {
    52  	intVals     [MaxSettings]int64
    53  	genericVals [MaxSettings]atomic.Value
    54  }
    55  
    56  func (c *valuesContainer) setGenericVal(slotIdx int, newVal interface{}) {
    57  	c.genericVals[slotIdx].Store(newVal)
    58  }
    59  
    60  func (c *valuesContainer) setInt64Val(slotIdx int, newVal int64) bool {
    61  	return atomic.SwapInt64(&c.intVals[slotIdx], newVal) != newVal
    62  }
    63  
    64  var (
    65  	canonicalValues atomic.Value
    66  )
    67  
    68  // TODO is usable at callsites that do not have *settings.Values available.
    69  // Please don't use this.
    70  func TODO() *Values {
    71  	if ptr := canonicalValues.Load(); ptr != nil {
    72  		return ptr.(*Values)
    73  	}
    74  	return nil
    75  }
    76  
    77  // SetCanonicalValuesContainer sets the Values container that will be refreshed
    78  // at runtime -- ideally we should have no other *Values containers floating
    79  // around, as they will be stale / lies.
    80  func SetCanonicalValuesContainer(v *Values) {
    81  	canonicalValues.Store(v)
    82  }
    83  
    84  type testOpaqueType struct{}
    85  
    86  // TestOpaque can be passed to Values.Init when we are testing the settings
    87  // infrastructure.
    88  var TestOpaque interface{} = testOpaqueType{}
    89  
    90  // Init must be called before using a Values instance; it initializes all
    91  // variables to their defaults.
    92  //
    93  // The opaque argument can be retrieved later via Opaque().
    94  func (sv *Values) Init(opaque interface{}) {
    95  	sv.opaque = opaque
    96  	for _, s := range registry {
    97  		s.setToDefault(sv)
    98  	}
    99  }
   100  
   101  // Opaque returns the argument passed to Init.
   102  func (sv *Values) Opaque() interface{} {
   103  	return sv.opaque
   104  }
   105  
   106  func (sv *Values) settingChanged(slotIdx int) {
   107  	sv.changeMu.Lock()
   108  	funcs := sv.changeMu.onChange[slotIdx-1]
   109  	sv.changeMu.Unlock()
   110  	for _, fn := range funcs {
   111  		fn()
   112  	}
   113  }
   114  
   115  func (c *valuesContainer) getInt64(slotIdx int) int64 {
   116  	return atomic.LoadInt64(&c.intVals[slotIdx-1])
   117  }
   118  
   119  func (c *valuesContainer) getGeneric(slotIdx int) interface{} {
   120  	return c.genericVals[slotIdx-1].Load()
   121  }
   122  
   123  func (sv *Values) setInt64(slotIdx int, newVal int64) {
   124  	if sv.container.setInt64Val(slotIdx-1, newVal) {
   125  		sv.settingChanged(slotIdx)
   126  	}
   127  }
   128  
   129  // setDefaultOverrideInt64 overrides the default value for the respective
   130  // setting to newVal.
   131  func (sv *Values) setDefaultOverrideInt64(slotIdx int, newVal int64) {
   132  	sv.overridesMu.Lock()
   133  	defer sv.overridesMu.Unlock()
   134  	sv.overridesMu.defaultOverrides.setInt64Val(slotIdx-1, newVal)
   135  	sv.setDefaultOverrideLocked(slotIdx)
   136  }
   137  
   138  // setDefaultOverrideLocked marks slotIdx-1 as having an overridden default value.
   139  func (sv *Values) setDefaultOverrideLocked(slotIdx int) {
   140  	if sv.overridesMu.setOverrides == nil {
   141  		sv.overridesMu.setOverrides = make(map[int]struct{})
   142  	}
   143  	sv.overridesMu.setOverrides[slotIdx-1] = struct{}{}
   144  }
   145  
   146  // getDefaultOverrides checks whether there's a default override for slotIdx-1.
   147  // If there isn't, the first ret val is false. Otherwise, the first ret val is
   148  // true, the second is the int64 override and the last is a pointer to the
   149  // generic value override. Callers are expected to only use the override value
   150  // corresponding to their setting type.
   151  func (sv *Values) getDefaultOverride(slotIdx int) (bool, int64, *atomic.Value) {
   152  	slotIdx--
   153  	sv.overridesMu.Lock()
   154  	defer sv.overridesMu.Unlock()
   155  	if _, ok := sv.overridesMu.setOverrides[slotIdx]; !ok {
   156  		return false, 0, nil
   157  	}
   158  	return true,
   159  		sv.overridesMu.defaultOverrides.intVals[slotIdx],
   160  		&sv.overridesMu.defaultOverrides.genericVals[slotIdx]
   161  }
   162  
   163  func (sv *Values) setGeneric(slotIdx int, newVal interface{}) {
   164  	sv.container.setGenericVal(slotIdx-1, newVal)
   165  	sv.settingChanged(slotIdx)
   166  }
   167  
   168  func (sv *Values) getInt64(slotIdx int) int64 {
   169  	return sv.container.getInt64(slotIdx)
   170  }
   171  
   172  func (sv *Values) getGeneric(slotIdx int) interface{} {
   173  	return sv.container.getGeneric(slotIdx)
   174  }
   175  
   176  // setOnChange installs a callback to be called when a setting's value changes.
   177  // `fn` should avoid doing long-running or blocking work as it is called on the
   178  // goroutine which handles all settings updates.
   179  func (sv *Values) setOnChange(slotIdx int, fn func()) {
   180  	sv.changeMu.Lock()
   181  	sv.changeMu.onChange[slotIdx-1] = append(sv.changeMu.onChange[slotIdx-1], fn)
   182  	sv.changeMu.Unlock()
   183  }
   184  
   185  // Setting is a descriptor for each setting; once it is initialized, it is
   186  // immutable. The values for the settings are stored separately, in
   187  // Values. This way we can have a global set of registered settings, each
   188  // with potentially multiple instances.
   189  type Setting interface {
   190  	// Typ returns the short (1 char) string denoting the type of setting.
   191  	Typ() string
   192  	String(sv *Values) string
   193  	Description() string
   194  	Visibility() Visibility
   195  }
   196  
   197  // WritableSetting is the exported interface of non-masked settings.
   198  type WritableSetting interface {
   199  	Setting
   200  
   201  	// Encoded returns the encoded value of the current value of the setting.
   202  	Encoded(sv *Values) string
   203  	EncodedDefault() string
   204  	SetOnChange(sv *Values, fn func())
   205  }
   206  
   207  type extendedSetting interface {
   208  	WritableSetting
   209  
   210  	isRetired() bool
   211  	setToDefault(sv *Values)
   212  	setDescription(desc string)
   213  	setSlotIdx(slotIdx int)
   214  	getSlotIdx() int
   215  	// isReportable indicates whether the value of the setting can be
   216  	// included in user-facing reports such as that produced by SHOW ALL
   217  	// CLUSTER SETTINGS.
   218  	// This only affects reports though; direct access is unconstrained.
   219  	// For example, `enterprise.license` is non-reportable:
   220  	// it cannot be listed, but can be accessed with `SHOW CLUSTER
   221  	// SETTING enterprise.license` or SET CLUSTER SETTING.
   222  	isReportable() bool
   223  }
   224  
   225  // Visibility describes how a user should feel confident that
   226  // they can customize the setting.  See the constant definitions below
   227  // for details.
   228  type Visibility int
   229  
   230  const (
   231  	// Reserved - which is the default - indicates that a setting is
   232  	// not documented and the CockroachDB team has not developed
   233  	// internal experience about the impact of customizing it to other
   234  	// values.
   235  	// In short: "Use at your own risk."
   236  	Reserved Visibility = iota
   237  	// Public indicates that a setting is documented, the range of
   238  	// possible values yields predictable results, and the CockroachDB
   239  	// team is there to assist if issues occur as a result of the
   240  	// customization.
   241  	// In short: "Go ahead but be careful."
   242  	Public
   243  )
   244  
   245  type common struct {
   246  	description string
   247  	visibility  Visibility
   248  	// Each setting has a slotIdx which is used as a handle with Values.
   249  	slotIdx       int
   250  	nonReportable bool
   251  	retired       bool
   252  }
   253  
   254  func (i *common) isRetired() bool {
   255  	return i.retired
   256  }
   257  
   258  func (i *common) setSlotIdx(slotIdx int) {
   259  	if slotIdx < 1 {
   260  		panic(fmt.Sprintf("Invalid slot index %d", slotIdx))
   261  	}
   262  	if slotIdx > MaxSettings {
   263  		panic(fmt.Sprintf("too many settings; increase MaxSettings"))
   264  	}
   265  	i.slotIdx = slotIdx
   266  }
   267  func (i *common) getSlotIdx() int {
   268  	return i.slotIdx
   269  }
   270  
   271  func (i *common) setDescription(s string) {
   272  	i.description = s
   273  }
   274  
   275  func (i common) Description() string {
   276  	return i.description
   277  }
   278  
   279  func (i common) Visibility() Visibility {
   280  	return i.visibility
   281  }
   282  
   283  func (i common) isReportable() bool {
   284  	return !i.nonReportable
   285  }
   286  
   287  // SetReportable indicates whether a setting's value can show up in SHOW ALL
   288  // CLUSTER SETTINGS and telemetry reports.
   289  //
   290  // The setting can still be used with SET and SHOW if the exact
   291  // setting name is known. Use SetReportable(false) for data that must
   292  // be hidden from standard setting report, telemetry and
   293  // troubleshooting screenshots, such as license data or keys.
   294  //
   295  // All string settings are also non-reportable by default and must be
   296  // opted in to reports manually with SetReportable(true).
   297  func (i *common) SetReportable(reportable bool) {
   298  	i.nonReportable = !reportable
   299  }
   300  
   301  // SetVisibility customizes the visibility of a setting.
   302  func (i *common) SetVisibility(v Visibility) {
   303  	i.visibility = v
   304  }
   305  
   306  // SetRetired marks the setting as obsolete. It also hides
   307  // it from the output of SHOW CLUSTER SETTINGS.
   308  func (i *common) SetRetired() {
   309  	i.description = "do not use - " + i.description
   310  	i.retired = true
   311  }
   312  
   313  // SetOnChange installs a callback to be called when a setting's value changes.
   314  // `fn` should avoid doing long-running or blocking work as it is called on the
   315  // goroutine which handles all settings updates.
   316  func (i *common) SetOnChange(sv *Values, fn func()) {
   317  	sv.setOnChange(i.slotIdx, fn)
   318  }
   319  
   320  type numericSetting interface {
   321  	Setting
   322  	Validate(i int64) error
   323  	set(sv *Values, i int64) error
   324  }
   325  
   326  // TestingIsReportable is used in testing for reportability.
   327  func TestingIsReportable(s Setting) bool {
   328  	if _, ok := s.(*MaskedSetting); ok {
   329  		return false
   330  	}
   331  	if e, ok := s.(extendedSetting); ok {
   332  		return e.isReportable()
   333  	}
   334  	return true
   335  }