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 }