github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/settings/statemachine.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 "bytes" 15 "context" 16 "fmt" 17 ) 18 19 // StateMachineSettingImpl provides the setting-specific parts of a 20 // StateMachineSetting. The StateMachineSetting is in charge of interacting with 21 // the Values (loading and saving state) and StateMachineSettingImpl is in 22 // charge to converting to/from strings and performing validations. 23 type StateMachineSettingImpl interface { 24 // Decode takes in an encoded value and returns it as the native type of the 25 // setting in question. For the Version setting, this is a ClusterVersion 26 // proto. 27 Decode(val []byte) (interface{}, error) 28 29 // DecodeToString takes in an encoded value and returns its string 30 // representation. 31 DecodeToString(val []byte) (string, error) 32 33 // ValidateLogical checks whether an update is permitted. It takes in the old 34 // (encoded) value and the proposed new value (as a string to be parsed). 35 // This is called by SET CLUSTER SETTING. 36 ValidateLogical(ctx context.Context, sv *Values, old []byte, newV string) ([]byte, error) 37 38 // ValidateGossipUpdate performs fewer validations than ValidateLogical. 39 // For the cluster version setting, it only checks that the current binary 40 // supports the proposed version. This is called when the version is being 41 // communicated to us by a different node. 42 ValidateGossipUpdate(ctx context.Context, sv *Values, val []byte) error 43 44 // SettingsListDefault returns the value that should be presented by 45 // `./cockroach gen settings-list` 46 SettingsListDefault() string 47 48 // BeforeChange is called before an updated value for this setting is about to 49 // be set on the values container. 50 BeforeChange(ctx context.Context, encodedVal []byte, sv *Values) 51 } 52 53 // A StateMachineSetting is a setting that keeps a state machine driven by user 54 // input. 55 // 56 // For (a nonsensical) example, a StateMachineSetting can be used to maintain an 57 // encoded protobuf containing an integer that the user can only increment by 3 58 // if the int is odd and by two if it is even. More generally, the setting 59 // starts from an initial state, and can take the current state into account 60 // when determining user input. Initially this is motivated for use in cluster 61 // version upgrades. 62 // 63 // The state machine as well as its encoding are represented by the 64 // StateMachineSettingImpl backing this StateMachineSetting; it is in charge to 65 // converting to/from strings and performing validations. 66 type StateMachineSetting struct { 67 impl StateMachineSettingImpl 68 common 69 } 70 71 var _ Setting = &StateMachineSetting{} 72 73 // MakeStateMachineSetting creates a StateMachineSetting. 74 func MakeStateMachineSetting(impl StateMachineSettingImpl) StateMachineSetting { 75 return StateMachineSetting{impl: impl} 76 } 77 78 // Decode takes in an encoded value and returns it as the native type of the 79 // setting in question. For the Version setting, this is a ClusterVersion proto. 80 func (s *StateMachineSetting) Decode(val []byte) (interface{}, error) { 81 return s.impl.Decode(val) 82 } 83 84 func (s *StateMachineSetting) String(sv *Values) string { 85 encV := []byte(s.Get(sv)) 86 if encV == nil { 87 panic("unexpected nil value") 88 } 89 str, err := s.impl.DecodeToString(encV) 90 if err != nil { 91 panic(err) 92 } 93 return str 94 } 95 96 // Typ returns the short (1 char) string denoting the type of setting. 97 func (*StateMachineSetting) Typ() string { 98 return "m" 99 } 100 101 // Get retrieves the (encoded) value in the setting. Get panics if set( ) has 102 // not been previously called. 103 func (s *StateMachineSetting) Get(sv *Values) string { 104 encV := sv.getGeneric(s.slotIdx) 105 if encV == nil { 106 panic(fmt.Sprintf("missing value for state machine in slot %d", s.slotIdx)) 107 } 108 return string(encV.([]byte)) 109 } 110 111 // GetInternal returns the setting's current value. 112 func (s *StateMachineSetting) GetInternal(sv *Values) interface{} { 113 return sv.getGeneric(s.slotIdx) 114 } 115 116 // SetInternal updates the setting's value in the sv container. 117 func (s *StateMachineSetting) SetInternal(sv *Values, newVal interface{}) { 118 sv.setGeneric(s.getSlotIdx(), newVal) 119 } 120 121 // SettingsListDefault returns the value that should be presented by 122 // `./cockroach gen settings-list` 123 func (s *StateMachineSetting) SettingsListDefault() string { 124 return s.impl.SettingsListDefault() 125 } 126 127 // Encoded is part of the Setting interface. 128 func (s *StateMachineSetting) Encoded(sv *Values) string { 129 return s.Get(sv) 130 } 131 132 // EncodedDefault returns the encoded value of the default value of the setting. 133 func (s *StateMachineSetting) EncodedDefault() string { 134 return "unsupported" 135 } 136 137 // Validate that the state machine accepts the user input. Returns new encoded 138 // state. 139 func (s *StateMachineSetting) Validate( 140 ctx context.Context, sv *Values, old []byte, update string, 141 ) ([]byte, error) { 142 return s.impl.ValidateLogical(ctx, sv, old, update) 143 } 144 145 // set is part of the Setting interface. 146 func (s *StateMachineSetting) set(sv *Values, encodedVal []byte) error { 147 if err := s.impl.ValidateGossipUpdate(context.TODO(), sv, encodedVal); err != nil { 148 return err 149 } 150 curVal := sv.getGeneric(s.slotIdx) 151 if curVal != nil { 152 if bytes.Equal(curVal.([]byte), encodedVal) { 153 // Nothing to do. 154 return nil 155 } 156 } 157 s.impl.BeforeChange(context.TODO(), encodedVal, sv) 158 sv.setGeneric(s.slotIdx, encodedVal) 159 return nil 160 } 161 162 // setToDefault is part of the Setting interface. 163 // 164 // This is a no-op for StateMachineSettings. They don't have defaults that they 165 // can go back to at any time. 166 func (s *StateMachineSetting) setToDefault(_ *Values) {} 167 168 // RegisterStateMachineSetting registers a StateMachineSetting. See the comment 169 // for StateMachineSetting for details. 170 func RegisterStateMachineSetting(key, desc string, setting *StateMachineSetting) { 171 register(key, desc, setting) 172 } 173 174 // RegisterStateMachineSettingImpl is like RegisterStateMachineSetting, 175 // but it takes a StateMachineSettingImpl. 176 func RegisterStateMachineSettingImpl( 177 key, desc string, impl StateMachineSettingImpl, 178 ) *StateMachineSetting { 179 setting := MakeStateMachineSetting(impl) 180 register(key, desc, &setting) 181 return &setting 182 }