github.com/m3db/m3@v1.5.0/src/cluster/kv/util/util.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package util
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/cluster/generated/proto/commonpb"
    29  	"github.com/m3db/m3/src/cluster/kv"
    30  
    31  	"go.uber.org/zap"
    32  )
    33  
    34  var (
    35  	noopLogger  = zap.NewNop()
    36  	errNilStore = errors.New("kv store is nil")
    37  )
    38  
    39  // BoolFromValue get a bool from kv.Value. If the value is nil, the default value
    40  // is returned.
    41  func BoolFromValue(v kv.Value, key string, defaultValue bool, opts Options) (bool, error) {
    42  	if opts == nil {
    43  		opts = NewOptions()
    44  	}
    45  
    46  	var res bool
    47  	updateFn := func(i interface{}) { res = i.(bool) }
    48  
    49  	if err := updateWithKV(
    50  		getBool, updateFn, opts.ValidateFn(), key, v, defaultValue, opts.Logger(),
    51  	); err != nil {
    52  		return false, err
    53  	}
    54  
    55  	return res, nil
    56  }
    57  
    58  // Float64FromValue gets a float64 from kv.Value. If the value is nil, the default
    59  // value is returned.
    60  func Float64FromValue(v kv.Value, key string, defaultValue float64, opts Options) (float64, error) {
    61  	if opts == nil {
    62  		opts = NewOptions()
    63  	}
    64  
    65  	var res float64
    66  	updateFn := func(i interface{}) { res = i.(float64) }
    67  
    68  	if err := updateWithKV(
    69  		getFloat64, updateFn, opts.ValidateFn(), key, v, defaultValue, opts.Logger(),
    70  	); err != nil {
    71  		return 0, err
    72  	}
    73  
    74  	return res, nil
    75  }
    76  
    77  // Int64FromValue gets an int64 from kv.Value. If the value is nil, the default
    78  // value is returned.
    79  func Int64FromValue(v kv.Value, key string, defaultValue int64, opts Options) (int64, error) {
    80  	if opts == nil {
    81  		opts = NewOptions()
    82  	}
    83  
    84  	var res int64
    85  	updateFn := func(i interface{}) { res = i.(int64) }
    86  
    87  	if err := updateWithKV(
    88  		getInt64, updateFn, opts.ValidateFn(), key, v, defaultValue, opts.Logger(),
    89  	); err != nil {
    90  		return 0, err
    91  	}
    92  
    93  	return res, nil
    94  }
    95  
    96  // StringFromValue gets a string from kv.Value. If the value is nil, the default
    97  // value is returned.
    98  func StringFromValue(v kv.Value, key string, defaultValue string, opts Options) (string, error) {
    99  	if opts == nil {
   100  		opts = NewOptions()
   101  	}
   102  
   103  	var res string
   104  	updateFn := func(i interface{}) { res = i.(string) }
   105  
   106  	if err := updateWithKV(
   107  		getString, updateFn, opts.ValidateFn(), key, v, defaultValue, opts.Logger(),
   108  	); err != nil {
   109  		return "", err
   110  	}
   111  
   112  	return res, nil
   113  }
   114  
   115  // StringArrayFromValue gets a string array from kv.Value. If the value is nil,
   116  // the default value is returned.
   117  func StringArrayFromValue(
   118  	v kv.Value, key string, defaultValue []string, opts Options,
   119  ) ([]string, error) {
   120  	if opts == nil {
   121  		opts = NewOptions()
   122  	}
   123  
   124  	var res []string
   125  	updateFn := func(i interface{}) { res = i.([]string) }
   126  
   127  	if err := updateWithKV(
   128  		getStringArray, updateFn, opts.ValidateFn(), key, v, defaultValue, opts.Logger(),
   129  	); err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	return res, nil
   134  }
   135  
   136  // TimeFromValue gets a time.Time from kv.Value. If the value is nil, the
   137  // default value is returned.
   138  func TimeFromValue(
   139  	v kv.Value, key string, defaultValue time.Time, opts Options,
   140  ) (time.Time, error) {
   141  	if opts == nil {
   142  		opts = NewOptions()
   143  	}
   144  
   145  	var res time.Time
   146  	updateFn := func(i interface{}) { res = i.(time.Time) }
   147  
   148  	if err := updateWithKV(
   149  		getTime, updateFn, opts.ValidateFn(), key, v, defaultValue, opts.Logger(),
   150  	); err != nil {
   151  		return time.Time{}, err
   152  	}
   153  
   154  	return res, nil
   155  }
   156  
   157  func getBool(v kv.Value) (interface{}, error) {
   158  	var boolProto commonpb.BoolProto
   159  	err := v.Unmarshal(&boolProto)
   160  	return boolProto.Value, err
   161  }
   162  
   163  func getFloat64(v kv.Value) (interface{}, error) {
   164  	var float64proto commonpb.Float64Proto
   165  	err := v.Unmarshal(&float64proto)
   166  	return float64proto.Value, err
   167  }
   168  
   169  func getInt64(v kv.Value) (interface{}, error) {
   170  	var int64Proto commonpb.Int64Proto
   171  	if err := v.Unmarshal(&int64Proto); err != nil {
   172  		return 0, err
   173  	}
   174  
   175  	return int64Proto.Value, nil
   176  }
   177  
   178  func getString(v kv.Value) (interface{}, error) {
   179  	var stringProto commonpb.StringProto
   180  	if err := v.Unmarshal(&stringProto); err != nil {
   181  		return 0, err
   182  	}
   183  
   184  	return stringProto.Value, nil
   185  }
   186  
   187  func getStringArray(v kv.Value) (interface{}, error) {
   188  	var stringArrProto commonpb.StringArrayProto
   189  	if err := v.Unmarshal(&stringArrProto); err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	return stringArrProto.Values, nil
   194  }
   195  
   196  func getStringArrayPointer(v kv.Value) (interface{}, error) {
   197  	var stringArrProto commonpb.StringArrayProto
   198  	if err := v.Unmarshal(&stringArrProto); err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	return &stringArrProto.Values, nil
   203  }
   204  
   205  func getTime(v kv.Value) (interface{}, error) {
   206  	var int64Proto commonpb.Int64Proto
   207  	if err := v.Unmarshal(&int64Proto); err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	return time.Unix(int64Proto.Value, 0), nil
   212  }
   213  
   214  func getDuration(v kv.Value) (interface{}, error) {
   215  	var int64Proto commonpb.Int64Proto
   216  	if err := v.Unmarshal(&int64Proto); err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	return time.Duration(int64Proto.Value), nil
   221  }
   222  
   223  func watchAndUpdate(
   224  	store kv.Store,
   225  	key string,
   226  	getValue getValueFn,
   227  	update updateFn,
   228  	validate ValidateFn,
   229  	defaultValue interface{},
   230  	logger *zap.Logger,
   231  ) (kv.ValueWatch, error) {
   232  	if store == nil {
   233  		return nil, errNilStore
   234  	}
   235  
   236  	var (
   237  		watch kv.ValueWatch
   238  		err   error
   239  	)
   240  
   241  	watch, err = store.Watch(key)
   242  	if err != nil {
   243  		return nil, fmt.Errorf("could not establish initial watch: %v", err)
   244  	}
   245  
   246  	go func() {
   247  		for range watch.C() {
   248  			updateWithKV(getValue, update, validate, key, watch.Get(), defaultValue, logger)
   249  		}
   250  		// The channel for a ValueWatch should never close.
   251  		getLogger(logger).
   252  			With(zap.String("key", key)).
   253  			Error("watch unexpectedly closed")
   254  	}()
   255  
   256  	return watch, nil
   257  }
   258  
   259  func updateWithKV(
   260  	getValue getValueFn,
   261  	update updateFn,
   262  	validate ValidateFn,
   263  	key string,
   264  	v kv.Value,
   265  	defaultValue interface{},
   266  	logger *zap.Logger,
   267  ) error {
   268  	if v == nil {
   269  		// The key is deleted from kv, use the default value.
   270  		update(defaultValue)
   271  		logNilUpdate(logger, key, defaultValue)
   272  		return nil
   273  	}
   274  
   275  	newValue, err := getValue(v)
   276  	if err != nil {
   277  		logMalformedUpdate(logger, key, v.Version(), newValue, err)
   278  		return err
   279  	}
   280  
   281  	if validate != nil {
   282  		if err := validate(newValue); err != nil {
   283  			logInvalidUpdate(logger, key, v.Version(), newValue, err)
   284  			return err
   285  		}
   286  	}
   287  
   288  	update(newValue)
   289  	logUpdateSuccess(logger, key, v.Version(), newValue)
   290  	return nil
   291  }
   292  
   293  func logNilUpdate(logger *zap.Logger, k string, v interface{}) {
   294  	getLogger(logger).Warn("nil value from kv store, applying default value",
   295  		zap.String("key", k),
   296  		zap.Any("defaultValue", v),
   297  	)
   298  }
   299  
   300  func logMalformedUpdate(logger *zap.Logger, k string, ver int, v interface{}, err error) {
   301  	getLogger(logger).Warn("malformed value from kv store, not applying update",
   302  		zap.String("key", k),
   303  		zap.Any("malformedValue", v),
   304  		zap.Int("version", ver),
   305  		zap.Error(err),
   306  	)
   307  }
   308  
   309  func logInvalidUpdate(logger *zap.Logger, k string, ver int, v interface{}, err error) {
   310  	getLogger(logger).Warn("invalid value from kv store, not applying update",
   311  		zap.String("key", k),
   312  		zap.Any("invalidValue", v),
   313  		zap.Int("version", ver),
   314  		zap.Error(err),
   315  	)
   316  }
   317  
   318  func logUpdateSuccess(logger *zap.Logger, k string, ver int, v interface{}) {
   319  	getLogger(logger).Info("value update success",
   320  		zap.String("key", k),
   321  		zap.Any("value", v),
   322  		zap.Int("version", ver),
   323  	)
   324  }
   325  
   326  func getLogger(logger *zap.Logger) *zap.Logger {
   327  	if logger == nil {
   328  		return noopLogger
   329  	}
   330  	return logger
   331  }