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 }