github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/policy/storage_policy.go (about)

     1  // Copyright (c) 2016 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  	"fmt"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/metrics/generated/proto/policypb"
    30  	xtime "github.com/m3db/m3/src/x/time"
    31  )
    32  
    33  const (
    34  	resolutionRetentionSeparator = ":"
    35  )
    36  
    37  var (
    38  	// EmptyStoragePolicy represents an empty storage policy.
    39  	EmptyStoragePolicy StoragePolicy
    40  
    41  	errNilStoragePolicyProto       = errors.New("nil storage policy proto")
    42  	errInvalidStoragePolicyString  = errors.New("invalid storage policy string")
    43  	errStoragePolicyLengthMismatch = errors.New("storage policy list length does not match proto")
    44  )
    45  
    46  // StoragePolicy represents the resolution and retention period metric datapoints
    47  // are stored at.
    48  type StoragePolicy struct {
    49  	resolution Resolution
    50  	retention  Retention
    51  }
    52  
    53  // NewStoragePolicy creates a new storage policy given a resolution and a retention.
    54  func NewStoragePolicy(window time.Duration, precision xtime.Unit, retention time.Duration) StoragePolicy {
    55  	return StoragePolicy{
    56  		resolution: Resolution{
    57  			Window:    window,
    58  			Precision: precision,
    59  		},
    60  		retention: Retention(retention),
    61  	}
    62  }
    63  
    64  // NewStoragePolicyFromProto creates a new storage policy from a storage policy protobuf message.
    65  func NewStoragePolicyFromProto(pb *policypb.StoragePolicy) (StoragePolicy, error) {
    66  	if pb == nil {
    67  		return EmptyStoragePolicy, errNilStoragePolicyProto
    68  	}
    69  	var sp StoragePolicy
    70  	if err := sp.FromProto(*pb); err != nil {
    71  		return EmptyStoragePolicy, err
    72  	}
    73  	return sp, nil
    74  }
    75  
    76  // Equivalent returns whether two storage policies are equal by their
    77  // retention width and resolution. The resolution precision is ignored
    78  // for equivalency (hence why the method is not named Equal).
    79  func (p StoragePolicy) Equivalent(other StoragePolicy) bool {
    80  	return p.resolution.Window == other.resolution.Window &&
    81  		p.retention == other.retention
    82  }
    83  
    84  // String is the string representation of a storage policy.
    85  func (p StoragePolicy) String() string {
    86  	return fmt.Sprintf("%s%s%s", p.resolution.String(), resolutionRetentionSeparator, p.retention.String())
    87  }
    88  
    89  // Resolution returns the resolution of the storage policy.
    90  func (p StoragePolicy) Resolution() Resolution {
    91  	return p.resolution
    92  }
    93  
    94  // Retention return the retention of the storage policy.
    95  func (p StoragePolicy) Retention() Retention {
    96  	return p.retention
    97  }
    98  
    99  // Proto returns the proto message for the storage policy.
   100  func (p StoragePolicy) Proto() (*policypb.StoragePolicy, error) {
   101  	var pb policypb.StoragePolicy
   102  	if err := p.ToProto(&pb); err != nil {
   103  		return nil, err
   104  	}
   105  	return &pb, nil
   106  }
   107  
   108  // ToProto converts the storage policy to a protobuf message in place.
   109  func (p StoragePolicy) ToProto(pb *policypb.StoragePolicy) error {
   110  	if err := p.resolution.ToProto(&pb.Resolution); err != nil {
   111  		return err
   112  	}
   113  	p.retention.ToProto(&pb.Retention)
   114  	return nil
   115  }
   116  
   117  // FromProto converts the protobuf message to a storage policy in place.
   118  func (p *StoragePolicy) FromProto(pb policypb.StoragePolicy) error {
   119  	if err := p.resolution.FromProto(pb.Resolution); err != nil {
   120  		return err
   121  	}
   122  	if err := p.retention.FromProto(pb.Retention); err != nil {
   123  		return err
   124  	}
   125  	return nil
   126  }
   127  
   128  // MarshalText returns the text encoding of a storage policy.
   129  func (p StoragePolicy) MarshalText() ([]byte, error) {
   130  	return []byte(p.String()), nil
   131  }
   132  
   133  // UnmarshalText unmarshals text-encoded data into a storage policy.
   134  func (p *StoragePolicy) UnmarshalText(data []byte) error {
   135  	str := string(data)
   136  	parsed, err := ParseStoragePolicy(str)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	*p = parsed
   141  	return nil
   142  }
   143  
   144  // ParseStoragePolicy parses a storage policy in the form of resolution:retention.
   145  func ParseStoragePolicy(str string) (StoragePolicy, error) {
   146  	parts := strings.Split(str, resolutionRetentionSeparator)
   147  	if len(parts) != 2 {
   148  		return EmptyStoragePolicy, errInvalidStoragePolicyString
   149  	}
   150  	resolution, err := ParseResolution(parts[0])
   151  	if err != nil {
   152  		return EmptyStoragePolicy, err
   153  	}
   154  	retention, err := ParseRetention(parts[1])
   155  	if err != nil {
   156  		return EmptyStoragePolicy, err
   157  	}
   158  	return StoragePolicy{resolution: resolution, retention: retention}, nil
   159  }
   160  
   161  // MustParseStoragePolicy parses a storage policy in the form of resolution:retention,
   162  // and panics if the input string is invalid.
   163  func MustParseStoragePolicy(str string) StoragePolicy {
   164  	sp, err := ParseStoragePolicy(str)
   165  	if err != nil {
   166  		panic(fmt.Errorf("invalid storage policy string %s: %v", str, err))
   167  	}
   168  	return sp
   169  }
   170  
   171  // StoragePolicies is a list of storage policies.
   172  type StoragePolicies []StoragePolicy
   173  
   174  // NewStoragePoliciesFromProto creates a list of storage policies from given storage policies proto.
   175  func NewStoragePoliciesFromProto(
   176  	storagePolicies []*policypb.StoragePolicy,
   177  ) (StoragePolicies, error) {
   178  	res := make(StoragePolicies, 0, len(storagePolicies))
   179  	for _, sp := range storagePolicies {
   180  		storagePolicy, err := NewStoragePolicyFromProto(sp)
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		res = append(res, storagePolicy)
   185  	}
   186  	return res, nil
   187  }
   188  
   189  // Equal returns true if two lists of storage policies are considered equal.
   190  func (sp StoragePolicies) Equal(other StoragePolicies) bool {
   191  	if len(sp) != len(other) {
   192  		return false
   193  	}
   194  	// # of StoragePolicies is typically very small, so it's not worth the overhead of cloning/sorting.
   195  	for i := 0; i < len(sp); i++ {
   196  		found := false
   197  		for j := 0; j < len(other); j++ {
   198  			if sp[i] == other[j] {
   199  				found = true
   200  				break
   201  			}
   202  		}
   203  		if !found {
   204  			return false
   205  		}
   206  	}
   207  	return true
   208  }
   209  
   210  // Proto returns the proto message for the given list of storage policies.
   211  func (sp StoragePolicies) Proto() ([]*policypb.StoragePolicy, error) {
   212  	pbStoragePolicies := make([]*policypb.StoragePolicy, 0, len(sp))
   213  	for _, storagePolicy := range sp {
   214  		pbStoragePolicy, err := storagePolicy.Proto()
   215  		if err != nil {
   216  			return nil, err
   217  		}
   218  		pbStoragePolicies = append(pbStoragePolicies, pbStoragePolicy)
   219  	}
   220  	return pbStoragePolicies, nil
   221  }
   222  
   223  // Clone clones the list of storage policies.
   224  func (sp StoragePolicies) Clone() StoragePolicies {
   225  	cloned := make(StoragePolicies, len(sp))
   226  	copy(cloned, sp)
   227  	return cloned
   228  }
   229  
   230  // IsDefault returns whether a list of storage policies are considered
   231  // as default storage policies.
   232  func (sp StoragePolicies) IsDefault() bool { return len(sp) == 0 }
   233  
   234  // ByResolutionAscRetentionDesc implements the sort.Sort interface that enables sorting
   235  // storage policies by resolution in ascending order and then by retention in descending
   236  // order.
   237  type ByResolutionAscRetentionDesc StoragePolicies
   238  
   239  func (sp ByResolutionAscRetentionDesc) Len() int      { return len(sp) }
   240  func (sp ByResolutionAscRetentionDesc) Swap(i, j int) { sp[i], sp[j] = sp[j], sp[i] }
   241  
   242  func (sp ByResolutionAscRetentionDesc) Less(i, j int) bool {
   243  	rw1, rw2 := sp[i].Resolution().Window, sp[j].Resolution().Window
   244  	if rw1 != rw2 {
   245  		return rw1 < rw2
   246  	}
   247  	rt1, rt2 := sp[i].Retention(), sp[j].Retention()
   248  	if rt1 != rt2 {
   249  		return rt1 > rt2
   250  	}
   251  	return sp[i].Resolution().Precision < sp[j].Resolution().Precision
   252  }
   253  
   254  // ByRetentionAscResolutionAsc implements the sort.Sort interface that enables sorting
   255  // storage policies by retention in ascending order and then by resolution in ascending
   256  // order.
   257  type ByRetentionAscResolutionAsc StoragePolicies
   258  
   259  func (sp ByRetentionAscResolutionAsc) Len() int      { return len(sp) }
   260  func (sp ByRetentionAscResolutionAsc) Swap(i, j int) { sp[i], sp[j] = sp[j], sp[i] }
   261  func (sp ByRetentionAscResolutionAsc) Less(i, j int) bool {
   262  	rt1, rt2 := sp[i].Retention(), sp[j].Retention()
   263  	if rt1 != rt2 {
   264  		return rt1 < rt2
   265  	}
   266  	rw1, rw2 := sp[i].Resolution().Window, sp[j].Resolution().Window
   267  	if rw1 != rw2 {
   268  		return rw1 < rw2
   269  	}
   270  	return sp[i].Resolution().Precision < sp[j].Resolution().Precision
   271  }
   272  
   273  // StoragePoliciesFromProto converts a list of protobuf storage policies to a storage policy in place.
   274  func StoragePoliciesFromProto(src []policypb.StoragePolicy, dst []StoragePolicy) error {
   275  	if len(src) != len(dst) {
   276  		return errStoragePolicyLengthMismatch
   277  	}
   278  	for i := 0; i < len(src); i++ {
   279  		d := &dst[i]
   280  		if err := d.resolution.FromProto(src[i].Resolution); err != nil {
   281  			return err
   282  		}
   283  
   284  		if err := d.retention.FromProto(src[i].Retention); err != nil {
   285  			return err
   286  		}
   287  	}
   288  
   289  	return nil
   290  }