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  }