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  }