github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/policy/policy.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 policy
    22  
    23  import (
    24  	"errors"
    25  	"strings"
    26  
    27  	"github.com/m3db/m3/src/metrics/aggregation"
    28  	"github.com/m3db/m3/src/metrics/generated/proto/policypb"
    29  )
    30  
    31  const (
    32  	policyAggregationTypeSeparator = "|"
    33  )
    34  
    35  var (
    36  	// DefaultPolicy represents a default policy.
    37  	DefaultPolicy Policy
    38  
    39  	errNilPolicyProto          = errors.New("nil policy proto")
    40  	errInvalidPolicyString     = errors.New("invalid policy string")
    41  	errInvalidDropPolicyString = errors.New("invalid drop policy string")
    42  )
    43  
    44  // Policy contains a storage policy and a list of custom aggregation types.
    45  type Policy struct {
    46  	StoragePolicy
    47  	AggregationID aggregation.ID
    48  }
    49  
    50  // NewPolicy creates a policy.
    51  func NewPolicy(sp StoragePolicy, aggID aggregation.ID) Policy {
    52  	return Policy{StoragePolicy: sp, AggregationID: aggID}
    53  }
    54  
    55  // NewPolicyFromProto creates a new policy from a proto policy.
    56  func NewPolicyFromProto(p *policypb.Policy) (Policy, error) {
    57  	if p == nil {
    58  		return DefaultPolicy, errNilPolicyProto
    59  	}
    60  
    61  	policy, err := NewStoragePolicyFromProto(p.StoragePolicy)
    62  	if err != nil {
    63  		return DefaultPolicy, err
    64  	}
    65  
    66  	aggID, err := aggregation.NewIDFromProto(p.AggregationTypes)
    67  	if err != nil {
    68  		return DefaultPolicy, err
    69  	}
    70  
    71  	return NewPolicy(policy, aggID), nil
    72  
    73  }
    74  
    75  // Proto returns the proto of the policy.
    76  func (p Policy) Proto() (*policypb.Policy, error) {
    77  	var storagePolicyProto policypb.StoragePolicy
    78  	err := p.StoragePolicy.ToProto(&storagePolicyProto)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	aggTypes, err := aggregation.NewIDDecompressor().Decompress(p.AggregationID)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	protoAggTypes, err := aggTypes.Proto()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	return &policypb.Policy{
    94  		StoragePolicy:    &storagePolicyProto,
    95  		AggregationTypes: protoAggTypes,
    96  	}, nil
    97  }
    98  
    99  // String is the string representation of a policy.
   100  func (p Policy) String() string {
   101  	if p.AggregationID.IsDefault() {
   102  		return p.StoragePolicy.String()
   103  	}
   104  	return p.StoragePolicy.String() + policyAggregationTypeSeparator + p.AggregationID.String()
   105  }
   106  
   107  // MarshalText returns the text encoding of a policy.
   108  func (p Policy) MarshalText() ([]byte, error) {
   109  	return []byte(p.String()), nil
   110  }
   111  
   112  // UnmarshalText unmarshals text-encoded data into a policy.
   113  func (p *Policy) UnmarshalText(data []byte) error {
   114  	parsed, err := ParsePolicy(string(data))
   115  	if err != nil {
   116  		return err
   117  	}
   118  	*p = parsed
   119  	return nil
   120  }
   121  
   122  // ParsePolicy parses a policy in the form of resolution:retention|aggregationTypes.
   123  func ParsePolicy(str string) (Policy, error) {
   124  	parts := strings.Split(str, policyAggregationTypeSeparator)
   125  	l := len(parts)
   126  	if l > 2 {
   127  		return DefaultPolicy, errInvalidPolicyString
   128  	}
   129  
   130  	sp, err := ParseStoragePolicy(parts[0])
   131  	if err != nil {
   132  		return DefaultPolicy, err
   133  	}
   134  
   135  	var aggID = aggregation.DefaultID
   136  	if l == 2 {
   137  		aggTypes, err := aggregation.ParseTypes(parts[1])
   138  		if err != nil {
   139  			return DefaultPolicy, err
   140  		}
   141  
   142  		aggID, err = aggregation.NewIDCompressor().Compress(aggTypes)
   143  		if err != nil {
   144  			return DefaultPolicy, err
   145  		}
   146  	}
   147  
   148  	return NewPolicy(sp, aggID), nil
   149  }
   150  
   151  // NewPoliciesFromProto creates multiple new policies from given proto policies.
   152  func NewPoliciesFromProto(policies []*policypb.Policy) ([]Policy, error) {
   153  	res := make([]Policy, 0, len(policies))
   154  	for _, p := range policies {
   155  		policy, err := NewPolicyFromProto(p)
   156  		if err != nil {
   157  			return nil, err
   158  		}
   159  		res = append(res, policy)
   160  	}
   161  	return res, nil
   162  }
   163  
   164  // IsDefaultPolicies checks if the policies are the default policies.
   165  func IsDefaultPolicies(ps []Policy) bool {
   166  	return len(ps) == 0
   167  }
   168  
   169  // Policies is a list of policies. Used to check ploicy list equivalence.
   170  type Policies []Policy
   171  
   172  // Equals takes a list of policies and checks equivalence.
   173  func (p Policies) Equals(other Policies) bool {
   174  	if len(p) != len(other) {
   175  		return false
   176  	}
   177  	for i := 0; i < len(p); i++ {
   178  		if p[i] != other[i] {
   179  			return false
   180  		}
   181  	}
   182  	return true
   183  }
   184  
   185  // UnmarshalText unmarshals a drop policy value from a string.
   186  // Empty string defaults to DefaultDropPolicy.
   187  func (p *DropPolicy) UnmarshalText(data []byte) error {
   188  	str := string(data)
   189  	// Allow default string value (not specified) to mean default
   190  	if str == "" {
   191  		*p = DefaultDropPolicy
   192  		return nil
   193  	}
   194  
   195  	parsed, err := ParseDropPolicy(str)
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	*p = parsed
   201  	return nil
   202  }
   203  
   204  // MarshalText marshals a drop policy to a string.
   205  func (p DropPolicy) MarshalText() ([]byte, error) {
   206  	return []byte(p.String()), nil
   207  }
   208  
   209  // ParseDropPolicy parses a drop policy.
   210  func ParseDropPolicy(str string) (DropPolicy, error) {
   211  	for _, valid := range validDropPolicies {
   212  		if valid.String() == str {
   213  			return valid, nil
   214  		}
   215  	}
   216  
   217  	return DefaultDropPolicy, errInvalidDropPolicyString
   218  }