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 }