github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/policy/storage_policy_test.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 "encoding/json" 25 "sort" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/metrics/generated/proto/policypb" 30 "github.com/m3db/m3/src/x/test/testmarshal" 31 xtime "github.com/m3db/m3/src/x/time" 32 33 "github.com/stretchr/testify/require" 34 yaml "gopkg.in/yaml.v2" 35 ) 36 37 var ( 38 testStoragePolicy = NewStoragePolicy(10*time.Second, xtime.Second, time.Hour) 39 testBadStoragePolicy = NewStoragePolicy(10*time.Second, xtime.Unit(100), time.Hour) 40 testStoragePolicyProto = policypb.StoragePolicy{ 41 Resolution: policypb.Resolution{ 42 WindowSize: (10 * time.Second).Nanoseconds(), 43 Precision: time.Second.Nanoseconds(), 44 }, 45 Retention: policypb.Retention{ 46 Period: time.Hour.Nanoseconds(), 47 }, 48 } 49 testStoragePolicyProtoNoResolution = policypb.StoragePolicy{ 50 Retention: policypb.Retention{ 51 Period: time.Hour.Nanoseconds(), 52 }, 53 } 54 testStoragePolicyProtoNoRetention = policypb.StoragePolicy{ 55 Resolution: policypb.Resolution{ 56 WindowSize: (10 * time.Second).Nanoseconds(), 57 Precision: time.Second.Nanoseconds(), 58 }, 59 } 60 testStoragePolicyProtoBadPrecision = policypb.StoragePolicy{ 61 Resolution: policypb.Resolution{ 62 WindowSize: (10 * time.Second).Nanoseconds(), 63 Precision: 2, 64 }, 65 Retention: policypb.Retention{ 66 Period: time.Hour.Nanoseconds(), 67 }, 68 } 69 ) 70 71 func TestStoragePolicyString(t *testing.T) { 72 inputs := []struct { 73 p StoragePolicy 74 expected string 75 }{ 76 {p: NewStoragePolicy(10*time.Second, xtime.Second, time.Hour), expected: "10s:1h"}, 77 {p: NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), expected: "1m:12h"}, 78 {p: NewStoragePolicy(time.Minute, xtime.Second, 12*time.Hour), expected: "1m@1s:12h"}, 79 } 80 for _, input := range inputs { 81 require.Equal(t, input.expected, input.p.String()) 82 } 83 } 84 85 func TestParseStoragePolicy(t *testing.T) { 86 inputs := []struct { 87 str string 88 expected StoragePolicy 89 }{ 90 { 91 str: "1s:1h", 92 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 93 }, 94 { 95 str: "10s:1d", 96 expected: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 97 }, 98 { 99 str: "60s:24h", 100 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 101 }, 102 { 103 str: "1m:1d", 104 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 105 }, 106 { 107 str: "1s@1s:1h", 108 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 109 }, 110 { 111 str: "10s@1s:1d", 112 expected: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 113 }, 114 { 115 str: "60s@1s:24h", 116 expected: NewStoragePolicy(time.Minute, xtime.Second, 24*time.Hour), 117 }, 118 { 119 str: "1m@1m:1d", 120 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 121 }, 122 { 123 str: "1h0m0s@1h0m0s:24h0m0s", 124 expected: NewStoragePolicy(time.Hour, xtime.Hour, 24*time.Hour), 125 }, 126 { 127 str: "1h:24h", 128 expected: NewStoragePolicy(time.Hour, xtime.Hour, 24*time.Hour), 129 }, 130 } 131 for _, input := range inputs { 132 res, err := ParseStoragePolicy(input.str) 133 require.NoError(t, err) 134 require.Equal(t, input.expected, res) 135 } 136 } 137 138 func TestStoragePolicyParseRoundTrip(t *testing.T) { 139 inputs := []StoragePolicy{ 140 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 141 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 142 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 143 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 144 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 145 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 146 NewStoragePolicy(time.Minute, xtime.Second, 24*time.Hour), 147 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 148 } 149 150 for _, input := range inputs { 151 str := input.String() 152 parsed, err := ParseStoragePolicy(str) 153 require.NoError(t, err) 154 require.Equal(t, input, parsed) 155 } 156 } 157 158 func TestParseStoragePolicyErrors(t *testing.T) { 159 inputs := []string{ 160 "1s:1s:1s", 161 "10seconds:1s", 162 "10seconds@1s:1d", 163 "10s@2s:1d", 164 "0.1s@1s:1d", 165 "10s@2minutes:2d", 166 } 167 for _, input := range inputs { 168 _, err := ParseStoragePolicy(input) 169 require.Error(t, err) 170 } 171 } 172 173 func TestStoragePolicyMarshalJSON(t *testing.T) { 174 inputs := []struct { 175 storagePolicy StoragePolicy 176 expected string 177 }{ 178 { 179 storagePolicy: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 180 expected: "\"1s:1h\"", 181 }, 182 { 183 storagePolicy: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 184 expected: "\"10s:1d\"", 185 }, 186 { 187 storagePolicy: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 188 expected: "\"1m:1d\"", 189 }, 190 { 191 storagePolicy: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 192 expected: "\"1m:1d\"", 193 }, 194 { 195 storagePolicy: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 196 expected: "\"1s:1h\"", 197 }, 198 { 199 storagePolicy: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 200 expected: "\"10s:1d\"", 201 }, 202 { 203 storagePolicy: NewStoragePolicy(time.Minute, xtime.Second, 24*time.Hour), 204 expected: "\"1m@1s:1d\"", 205 }, 206 { 207 storagePolicy: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 208 expected: "\"1m:1d\"", 209 }, 210 } 211 212 for _, input := range inputs { 213 res, err := json.Marshal(input.storagePolicy) 214 require.NoError(t, err) 215 require.Equal(t, input.expected, string(res)) 216 } 217 } 218 219 func TestStoragePolicyUnmarshalJSON(t *testing.T) { 220 inputs := []struct { 221 str string 222 expected StoragePolicy 223 }{ 224 { 225 str: "\"1s:1h\"", 226 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 227 }, 228 { 229 str: "\"10s:1d\"", 230 expected: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 231 }, 232 { 233 str: "\"60s:24h\"", 234 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 235 }, 236 { 237 str: "\"1m:1d\"", 238 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 239 }, 240 { 241 str: "\"1s@1s:1h\"", 242 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 243 }, 244 { 245 str: "\"10s@1s:1d\"", 246 expected: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 247 }, 248 { 249 str: "\"60s@1s:24h\"", 250 expected: NewStoragePolicy(time.Minute, xtime.Second, 24*time.Hour), 251 }, 252 { 253 str: "\"1m@1m:1d\"", 254 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 255 }, 256 } 257 258 for _, input := range inputs { 259 var p StoragePolicy 260 require.NoError(t, json.Unmarshal([]byte(input.str), &p)) 261 require.Equal(t, input.expected, p) 262 } 263 } 264 265 func TestStoragePolicyUnmarshalJSONError(t *testing.T) { 266 inputs := []string{ 267 "1m:1d", 268 "1m", 269 "1d", 270 } 271 272 for _, input := range inputs { 273 var p StoragePolicy 274 require.Error(t, json.Unmarshal([]byte(input), &p)) 275 } 276 } 277 278 func TestStoragePolicyMarshalRoundtrip(t *testing.T) { 279 inputs := StoragePolicies{ 280 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 281 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 282 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 283 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 284 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 285 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 286 NewStoragePolicy(time.Minute, xtime.Second, 24*time.Hour), 287 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 288 } 289 290 testmarshal.TestMarshalersRoundtrip(t, inputs, 291 []testmarshal.Marshaler{ 292 testmarshal.YAMLMarshaler, 293 testmarshal.TextMarshaler, 294 testmarshal.JSONMarshaler}) 295 } 296 297 func TestStoragePolicyYAMLMarshal(t *testing.T) { 298 inputs := []struct { 299 str string 300 expected StoragePolicy 301 notCanonical bool 302 }{ 303 { 304 str: "1s:1h", 305 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 306 }, 307 { 308 str: "10s:1d", 309 expected: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 310 }, 311 { 312 str: "60s:24h", 313 notCanonical: true, 314 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 315 }, 316 { 317 str: "1m:1d", 318 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 319 }, 320 { 321 str: "1s@1s:1h", 322 notCanonical: true, 323 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 324 }, 325 { 326 str: "10s@1s:1d", 327 notCanonical: true, 328 expected: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 329 }, 330 { 331 str: "60s@1s:24h", 332 notCanonical: true, 333 expected: NewStoragePolicy(time.Minute, xtime.Second, 24*time.Hour), 334 }, 335 { 336 str: "1m@1m:1d", 337 notCanonical: true, 338 expected: NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 339 }, 340 { 341 str: "2m@1ms:1d", 342 expected: NewStoragePolicy(2*time.Minute, 1*xtime.Millisecond, 24*time.Hour), 343 }, 344 } 345 346 t.Run("marshals", func(t *testing.T) { 347 for _, input := range inputs { 348 canonical := !input.notCanonical 349 if canonical { 350 testmarshal.AssertMarshals(t, testmarshal.YAMLMarshaler, input.expected, []byte(input.str+"\n")) 351 } 352 } 353 }) 354 355 t.Run("unmarshals", func(t *testing.T) { 356 for _, input := range inputs { 357 testmarshal.AssertUnmarshals(t, testmarshal.YAMLMarshaler, input.expected, []byte(input.str)) 358 } 359 }) 360 361 } 362 363 func TestMustParseStoragePolicy(t *testing.T) { 364 inputs := []struct { 365 str string 366 shouldPanic bool 367 expected StoragePolicy 368 }{ 369 { 370 str: "1s:1h", 371 shouldPanic: false, 372 expected: NewStoragePolicy(time.Second, xtime.Second, time.Hour), 373 }, 374 { 375 str: "10seconds:1d", 376 shouldPanic: true, 377 }, 378 } 379 for _, input := range inputs { 380 if input.shouldPanic { 381 require.Panics(t, func() { MustParseStoragePolicy(input.str) }) 382 } else { 383 require.Equal(t, input.expected, MustParseStoragePolicy(input.str)) 384 } 385 } 386 } 387 388 func TestStoragePolicyUnmarshalYAMLErrors(t *testing.T) { 389 inputs := []string{ 390 "1s:1s:1s", 391 "10seconds:1s", 392 "10seconds@1s:1d", 393 "10s@2s:1d", 394 "0.1s@1s:1d", 395 "10s@2minutes:2d", 396 } 397 for _, input := range inputs { 398 var p StoragePolicy 399 require.Error(t, yaml.Unmarshal([]byte(input), &p)) 400 } 401 } 402 403 func TestNewStoragePolicyFromProto(t *testing.T) { 404 inputs := []struct { 405 s *policypb.StoragePolicy 406 p StoragePolicy 407 }{ 408 { 409 s: &policypb.StoragePolicy{ 410 Resolution: policypb.Resolution{ 411 WindowSize: int64(10 * time.Second), 412 Precision: int64(time.Second), 413 }, 414 Retention: policypb.Retention{ 415 Period: int64(24 * time.Hour), 416 }, 417 }, 418 p: NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 419 }, 420 { 421 s: &policypb.StoragePolicy{ 422 Resolution: policypb.Resolution{ 423 WindowSize: int64(time.Minute), 424 Precision: int64(time.Minute), 425 }, 426 Retention: policypb.Retention{ 427 Period: int64(240 * time.Hour), 428 }, 429 }, 430 p: NewStoragePolicy(time.Minute, xtime.Minute, 240*time.Hour), 431 }, 432 } 433 434 for _, input := range inputs { 435 res, err := NewStoragePolicyFromProto(input.s) 436 require.NoError(t, err) 437 require.Equal(t, input.p, res) 438 } 439 } 440 441 func TestStoragePolicyToProto(t *testing.T) { 442 var pb policypb.StoragePolicy 443 require.NoError(t, testStoragePolicy.ToProto(&pb)) 444 require.Equal(t, testStoragePolicyProto, pb) 445 } 446 447 func TestStoragePolicyToProtoBadStoragePolicy(t *testing.T) { 448 var pb policypb.StoragePolicy 449 require.Error(t, testBadStoragePolicy.ToProto(&pb)) 450 } 451 452 func TestStoragePolicyFromProto(t *testing.T) { 453 var res StoragePolicy 454 require.NoError(t, res.FromProto(testStoragePolicyProto)) 455 require.Equal(t, testStoragePolicy, res) 456 } 457 458 func TestStoragePolicyFromProtoNilResolution(t *testing.T) { 459 var res StoragePolicy 460 require.Equal(t, errNilResolutionProto, res.FromProto(testStoragePolicyProtoNoResolution)) 461 } 462 463 func TestStoragePolicyFromProtoNilRetention(t *testing.T) { 464 var res StoragePolicy 465 require.Equal(t, errNilRetentionProto, res.FromProto(testStoragePolicyProtoNoRetention)) 466 } 467 468 func TestStoragePolicyFromProtoBadPrecision(t *testing.T) { 469 var res StoragePolicy 470 require.Error(t, res.FromProto(testStoragePolicyProtoBadPrecision)) 471 } 472 473 func TestStoragePolicyProtoRoundTrip(t *testing.T) { 474 var ( 475 pb policypb.StoragePolicy 476 res StoragePolicy 477 ) 478 require.NoError(t, testStoragePolicy.ToProto(&pb)) 479 require.NoError(t, res.FromProto(pb)) 480 require.Equal(t, testStoragePolicy, res) 481 } 482 483 func TestStoragePoliciesJSONMarshal(t *testing.T) { 484 input := StoragePolicies{ 485 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 486 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 487 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 488 } 489 490 b, err := json.Marshal(input) 491 require.NoError(t, err) 492 expected := "[\"1s:1h\",\"10s:1d\",\"1m:1d\"]" 493 require.Equal(t, expected, string(b)) 494 } 495 496 func TestStoragePoliciesJSONUnMarshal(t *testing.T) { 497 input := "[\"1s:1h\",\"10s:1d\",\"1m:1d\"]" 498 var storagePolicies StoragePolicies 499 require.NoError(t, json.Unmarshal([]byte(input), &storagePolicies)) 500 expected := StoragePolicies{ 501 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 502 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 503 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 504 } 505 require.Equal(t, expected, storagePolicies) 506 } 507 508 func TestStoragePoliciesRoundtrip(t *testing.T) { 509 input := StoragePolicies{ 510 NewStoragePolicy(time.Second, xtime.Second, time.Hour), 511 NewStoragePolicy(10*time.Second, xtime.Second, 24*time.Hour), 512 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 513 } 514 515 testmarshal.TestMarshalersRoundtrip(t, []StoragePolicies{input}, []testmarshal.Marshaler{testmarshal.JSONMarshaler, testmarshal.YAMLMarshaler}) 516 } 517 518 func TestStoragePoliciesByResolutionAscRetentionDesc(t *testing.T) { 519 inputs := StoragePolicies{ 520 NewStoragePolicy(10*time.Second, xtime.Second, 6*time.Hour), 521 NewStoragePolicy(10*time.Second, xtime.Second, 2*time.Hour), 522 NewStoragePolicy(10*time.Second, xtime.Second, 12*time.Hour), 523 NewStoragePolicy(5*time.Minute, xtime.Minute, 48*time.Hour), 524 NewStoragePolicy(time.Minute, xtime.Minute, time.Hour), 525 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 526 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 527 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 528 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 529 } 530 sort.Sort(ByResolutionAscRetentionDesc(inputs)) 531 532 expected := StoragePolicies{ 533 NewStoragePolicy(10*time.Second, xtime.Second, 12*time.Hour), 534 NewStoragePolicy(10*time.Second, xtime.Second, 6*time.Hour), 535 NewStoragePolicy(10*time.Second, xtime.Second, 2*time.Hour), 536 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 537 NewStoragePolicy(time.Minute, xtime.Minute, time.Hour), 538 NewStoragePolicy(5*time.Minute, xtime.Minute, 48*time.Hour), 539 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 540 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 541 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 542 } 543 require.Equal(t, expected, inputs) 544 } 545 546 func TestStoragePoliciesByRetentionAscResolutionAsc(t *testing.T) { 547 inputs := StoragePolicies{ 548 NewStoragePolicy(10*time.Second, xtime.Second, 6*time.Hour), 549 NewStoragePolicy(10*time.Second, xtime.Second, 2*time.Hour), 550 NewStoragePolicy(10*time.Second, xtime.Second, 12*time.Hour), 551 NewStoragePolicy(5*time.Minute, xtime.Minute, 48*time.Hour), 552 NewStoragePolicy(time.Minute, xtime.Minute, time.Hour), 553 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 554 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 555 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 556 NewStoragePolicy(10*time.Second, xtime.Minute, 12*time.Hour), 557 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 558 NewStoragePolicy(11*time.Second, xtime.Second, 2*time.Hour), 559 } 560 sort.Sort(ByRetentionAscResolutionAsc(inputs)) 561 562 expected := StoragePolicies{ 563 NewStoragePolicy(time.Minute, xtime.Minute, time.Hour), 564 NewStoragePolicy(10*time.Second, xtime.Second, 2*time.Hour), 565 NewStoragePolicy(11*time.Second, xtime.Second, 2*time.Hour), 566 NewStoragePolicy(10*time.Second, xtime.Second, 6*time.Hour), 567 NewStoragePolicy(10*time.Second, xtime.Second, 12*time.Hour), 568 NewStoragePolicy(10*time.Second, xtime.Minute, 12*time.Hour), 569 NewStoragePolicy(time.Minute, xtime.Minute, 24*time.Hour), 570 NewStoragePolicy(5*time.Minute, xtime.Minute, 48*time.Hour), 571 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 572 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 573 NewStoragePolicy(10*time.Minute, xtime.Minute, 48*time.Hour), 574 } 575 require.Equal(t, expected, inputs) 576 }