github.com/m3db/m3@v1.5.0/src/metrics/rules/store/kv/store.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 kv 22 23 import ( 24 "errors" 25 "fmt" 26 27 "github.com/m3db/m3/src/cluster/kv" 28 merrors "github.com/m3db/m3/src/metrics/errors" 29 "github.com/m3db/m3/src/metrics/generated/proto/rulepb" 30 "github.com/m3db/m3/src/metrics/rules" 31 ) 32 33 var ( 34 errNilRuleSet = errors.New("nil ruleset") 35 errNilNamespaces = errors.New("nil namespaces") 36 ) 37 38 type store struct { 39 kvStore kv.TxnStore 40 opts StoreOptions 41 } 42 43 // NewStore creates a new Store. 44 func NewStore(kvStore kv.TxnStore, opts StoreOptions) rules.Store { 45 return &store{kvStore: kvStore, opts: opts} 46 } 47 48 func (s *store) ReadNamespaces() (*rules.Namespaces, error) { 49 value, err := s.kvStore.Get(s.opts.NamespacesKey) 50 if err != nil { 51 return nil, wrapReadError(err) 52 } 53 54 version := value.Version() 55 var namespaces rulepb.Namespaces 56 if err = value.Unmarshal(&namespaces); err != nil { 57 return nil, err 58 } 59 60 nss, err := rules.NewNamespaces(version, &namespaces) 61 if err != nil { 62 return nil, err 63 } 64 return &nss, err 65 } 66 67 func (s *store) ReadRuleSet(nsName string) (rules.RuleSet, error) { 68 ruleSetKey := s.ruleSetKey(nsName) 69 value, err := s.kvStore.Get(ruleSetKey) 70 if err != nil { 71 return nil, wrapReadError(err) 72 } 73 74 version := value.Version() 75 var ruleSet rulepb.RuleSet 76 if err = value.Unmarshal(&ruleSet); err != nil { 77 return nil, fmt.Errorf("could not fetch RuleSet %s: %v", nsName, err.Error()) 78 } 79 80 rs, err := rules.NewRuleSetFromProto(version, &ruleSet, rules.NewOptions()) 81 if err != nil { 82 return nil, fmt.Errorf("could not fetch RuleSet %s: %v", nsName, err.Error()) 83 } 84 return rs, err 85 } 86 87 func (s *store) WriteRuleSet(rs rules.MutableRuleSet) error { 88 if s.opts.Validator != nil { 89 if err := s.opts.Validator.Validate(rs); err != nil { 90 return err 91 } 92 } 93 rsCond, rsOp, err := s.ruleSetTransaction(rs) 94 if err != nil { 95 return err 96 } 97 conditions, ops := []kv.Condition{rsCond}, []kv.Op{rsOp} 98 _, err = s.kvStore.Commit(conditions, ops) 99 return wrapWriteError(err) 100 } 101 102 func (s *store) WriteNamespaces(nss *rules.Namespaces) error { 103 namespacesCond, namespacesOp, err := s.namespacesTransaction(nss) 104 if err != nil { 105 return err 106 } 107 conditions, ops := []kv.Condition{namespacesCond}, []kv.Op{namespacesOp} 108 _, err = s.kvStore.Commit(conditions, ops) 109 return wrapWriteError(err) 110 } 111 112 func (s *store) WriteAll(nss *rules.Namespaces, rs rules.MutableRuleSet) error { 113 if s.opts.Validator != nil { 114 if err := s.opts.Validator.Validate(rs); err != nil { 115 return err 116 } 117 } 118 119 var ( 120 conditions []kv.Condition 121 ops []kv.Op 122 ) 123 ruleSetCond, ruleSetOp, err := s.ruleSetTransaction(rs) 124 if err != nil { 125 return err 126 } 127 conditions = append(conditions, ruleSetCond) 128 ops = append(ops, ruleSetOp) 129 130 namespacesCond, namespacesOp, err := s.namespacesTransaction(nss) 131 if err != nil { 132 return err 133 } 134 conditions = append(conditions, namespacesCond) 135 ops = append(ops, namespacesOp) 136 _, err = s.kvStore.Commit(conditions, ops) 137 return wrapWriteError(err) 138 } 139 140 func (s *store) Close() { 141 if s.opts.Validator == nil { 142 return 143 } 144 s.opts.Validator.Close() 145 } 146 147 func (s *store) ruleSetKey(ns string) string { 148 return fmt.Sprintf(s.opts.RuleSetKeyFmt, ns) 149 } 150 151 func (s *store) ruleSetTransaction(rs rules.MutableRuleSet) (kv.Condition, kv.Op, error) { 152 if rs == nil { 153 return nil, nil, errNilRuleSet 154 } 155 156 ruleSetKey := s.ruleSetKey(string(rs.Namespace())) 157 rsProto, err := rs.Proto() 158 if err != nil { 159 return nil, nil, err 160 } 161 162 ruleSetCond := kv.NewCondition(). 163 SetKey(ruleSetKey). 164 SetCompareType(kv.CompareEqual). 165 SetTargetType(kv.TargetVersion). 166 SetValue(rs.Version()) 167 168 return ruleSetCond, kv.NewSetOp(ruleSetKey, rsProto), nil 169 } 170 171 func (s *store) namespacesTransaction(nss *rules.Namespaces) (kv.Condition, kv.Op, error) { 172 if nss == nil { 173 return nil, nil, errNilNamespaces 174 } 175 176 namespacesKey := s.opts.NamespacesKey 177 nssProto, err := nss.Proto() 178 if err != nil { 179 return nil, nil, err 180 } 181 namespacesCond := kv.NewCondition(). 182 SetKey(namespacesKey). 183 SetCompareType(kv.CompareEqual). 184 SetTargetType(kv.TargetVersion). 185 SetValue(nss.Version()) 186 187 return namespacesCond, kv.NewSetOp(namespacesKey, nssProto), nil 188 } 189 190 func wrapWriteError(err error) error { 191 if err == kv.ErrConditionCheckFailed { 192 return merrors.NewStaleDataError( 193 fmt.Sprintf("stale write request: %s", err.Error()), 194 ) 195 } 196 197 return err 198 } 199 200 func wrapReadError(err error) error { 201 switch err { 202 case kv.ErrNotFound: 203 return merrors.NewNotFoundError( 204 fmt.Sprintf("not found: %s", err.Error()), 205 ) 206 default: 207 return err 208 } 209 }