Skip to content
This repository has been archived by the owner on Oct 17, 2018. It is now read-only.

Commit

Permalink
More user-friendly string representation of storage policies
Browse files Browse the repository at this point in the history
  • Loading branch information
xichen2020 committed Oct 13, 2017
1 parent 66f1576 commit b945010
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 22 deletions.
8 changes: 5 additions & 3 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion glide.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package: github.com/m3db/m3metrics
import:
- package: github.com/m3db/m3x
version: 6e9713eca6718643e105f574be392f122bcc062e
version: 5120e5bcfaf4888c15052e5c9d7ba700dce4247c
subpackages:
- pool
- time
Expand Down Expand Up @@ -73,3 +73,5 @@ testImport:
subpackages:
- gomock
- mockgen
- package: github.com/spaolacci/murmur3
version: 0d12bf811670bf6a1a63828dfbd003eded177fce
6 changes: 3 additions & 3 deletions policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ func TestPolicyString(t *testing.T) {
p Policy
expected string
}{
{p: NewPolicy(NewStoragePolicy(10*time.Second, xtime.Second, time.Hour), DefaultAggregationID), expected: "10s@1s:1h0m0s"},
{p: NewPolicy(NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), mustCompress(Mean, P999)), expected: "1m0s@1m:12h0m0s|Mean,P999"},
{p: NewPolicy(NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), mustCompress(Mean)), expected: "1m0s@1m:12h0m0s|Mean"},
{p: NewPolicy(NewStoragePolicy(10*time.Second, xtime.Second, time.Hour), DefaultAggregationID), expected: "10s:1h"},
{p: NewPolicy(NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), mustCompress(Mean, P999)), expected: "1m:12h|Mean,P999"},
{p: NewPolicy(NewStoragePolicy(time.Minute, xtime.Minute, 12*time.Hour), mustCompress(Mean)), expected: "1m:12h|Mean"},
}
for _, input := range inputs {
require.Equal(t, input.expected, input.p.String())
Expand Down
22 changes: 17 additions & 5 deletions policy/resolution.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ type Resolution struct {

// String is the string representation of a resolution.
func (r Resolution) String() string {
return fmt.Sprintf("%s%s1%s", r.Window.String(), windowPrecisionSeparator, r.Precision.String())
_, maxUnit := xtime.MaxUnitForDuration(r.Window)
if maxUnit == r.Precision {
// If the precision is the default value, do not write it for better readability.
return xtime.ToExtendedString(r.Window)
}
return fmt.Sprintf("%s%s1%s", xtime.ToExtendedString(r.Window), windowPrecisionSeparator, r.Precision.String())
}

// ParseResolution parses a resolution.
Expand All @@ -62,10 +67,7 @@ func ParseResolution(str string) (Resolution, error) {
if err != nil {
return emptyResolution, err
}
_, precision, err := xtime.MaxUnitForDuration(windowSize)
if err != nil {
return emptyResolution, err
}
_, precision := xtime.MaxUnitForDuration(windowSize)
return Resolution{Window: windowSize, Precision: precision}, nil
}

Expand All @@ -85,6 +87,16 @@ func ParseResolution(str string) (Resolution, error) {
return Resolution{Window: windowSize, Precision: precision}, nil
}

// MustParseResolution parses a resolution in the form of window@precision,
// and panics if the input string is invalid.
func MustParseResolution(str string) Resolution {
resolution, err := ParseResolution(str)
if err != nil {
panic(fmt.Errorf("invalid resolution string %s: %v", str, err))
}
return resolution
}

// ResolutionValue is the resolution value.
type ResolutionValue int

Expand Down
54 changes: 53 additions & 1 deletion policy/resolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,62 @@ func TestParseResolution(t *testing.T) {
}
}

func TestResolutionString(t *testing.T) {
inputs := []struct {
resolution Resolution
expected string
}{
{
resolution: Resolution{Window: time.Second, Precision: xtime.Second},
expected: "1s",
},
{
resolution: Resolution{Window: 10 * time.Second, Precision: xtime.Second},
expected: "10s",
},
{
resolution: Resolution{Window: time.Minute, Precision: xtime.Minute},
expected: "1m",
},
{
resolution: Resolution{Window: 10 * time.Minute, Precision: xtime.Minute},
expected: "10m",
},
{
resolution: Resolution{Window: time.Hour, Precision: xtime.Hour},
expected: "1h",
},
{
resolution: Resolution{Window: 10 * time.Minute, Precision: xtime.Second},
expected: "10m@1s",
},
}
for _, input := range inputs {
require.Equal(t, input.expected, input.resolution.String())
}
}

func TestResolutionRoundTrip(t *testing.T) {
inputs := []Resolution{
Resolution{Window: time.Second, Precision: xtime.Second},
Resolution{Window: 10 * time.Second, Precision: xtime.Second},
Resolution{Window: time.Minute, Precision: xtime.Minute},
Resolution{Window: 10 * time.Minute, Precision: xtime.Minute},
Resolution{Window: time.Hour, Precision: xtime.Hour},
Resolution{Window: 10 * time.Minute, Precision: xtime.Second},
Resolution{Window: 23491929834 * time.Nanosecond, Precision: xtime.Nanosecond},
}

for _, input := range inputs {
res, err := ParseResolution(input.String())
require.NoError(t, err)
require.Equal(t, input, res)
}
}

func TestParseResolutionNoPrecisionErrors(t *testing.T) {
inputs := []string{
"10seconds",
"0s",
"0.1s",
"10seconds@1s",
"10s@2s",
Expand Down
23 changes: 22 additions & 1 deletion policy/retention.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,43 @@ package policy

import (
"errors"
"fmt"
"time"

xtime "github.com/m3db/m3x/time"
)

// Retention is the retention period for datapoints.
type Retention time.Duration

// String is the string representation of a retention period.
func (r Retention) String() string {
return r.Duration().String()
return xtime.ToExtendedString(r.Duration())
}

// Duration returns the duration of the retention period.
func (r Retention) Duration() time.Duration {
return time.Duration(r)
}

// ParseRetention parses a retention.
func ParseRetention(str string) (Retention, error) {
d, err := xtime.ParseExtendedDuration(str)
if err != nil {
return 0, err
}
return Retention(d), nil
}

// MustParseRetention parses a retention, and panics if the input is invalid.
func MustParseRetention(str string) Retention {
retention, err := ParseRetention(str)
if err != nil {
panic(fmt.Errorf("invalid retention string %s: %v", str, err))
}
return retention
}

// RetentionValue is the retention value.
type RetentionValue int

Expand Down
147 changes: 147 additions & 0 deletions policy/retention_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,150 @@ func TestInvalidRetention(t *testing.T) {
require.Equal(t, errUnknownRetention, err)
}
}

func TestParseRetention(t *testing.T) {
inputs := []struct {
str string
expected Retention
}{
{
str: "1s",
expected: Retention(time.Second),
},
{
str: "10s",
expected: Retention(10 * time.Second),
},
{
str: "1m",
expected: Retention(time.Minute),
},
{
str: "10m",
expected: Retention(10 * time.Minute),
},
{
str: "1h",
expected: Retention(time.Hour),
},
{
str: "1d",
expected: Retention(24 * time.Hour),
},
{
str: "1w",
expected: Retention(24 * 7 * time.Hour),
},
{
str: "1mon",
expected: Retention(24 * 30 * time.Hour),
},
{
str: "6mon",
expected: Retention(24 * 180 * time.Hour),
},
{
str: "1y",
expected: Retention(24 * 365 * time.Hour),
},
{
str: "24h0m0s",
expected: Retention(24 * time.Hour),
},
}

for _, input := range inputs {
res, err := ParseRetention(input.str)
require.NoError(t, err)
require.Equal(t, input.expected, res)
}
}

func TestParseRetentionError(t *testing.T) {
inputs := []string{
"4",
"",
"4minutes",
"4d3",
",3490",
}

for _, input := range inputs {
_, err := ParseRetention(input)
require.Error(t, err)
}
}

func TestRetentionString(t *testing.T) {
inputs := []struct {
retention Retention
expected string
}{
{
retention: Retention(time.Second),
expected: "1s",
},
{
retention: Retention(10 * time.Second),
expected: "10s",
},
{
retention: Retention(time.Minute),
expected: "1m",
},
{
retention: Retention(10 * time.Minute),
expected: "10m",
},
{
retention: Retention(time.Hour),
expected: "1h",
},
{
retention: Retention(24 * time.Hour),
expected: "1d",
},
{
retention: Retention(24 * 7 * time.Hour),
expected: "1w",
},
{
retention: Retention(24 * 30 * time.Hour),
expected: "1mon",
},
{
retention: Retention(24 * 180 * time.Hour),
expected: "6mon",
},
{
retention: Retention(24 * 365 * time.Hour),
expected: "1y",
},
}

for _, input := range inputs {
require.Equal(t, input.expected, input.retention.String())
}
}

func TestRetentionRoundTrip(t *testing.T) {
inputs := []Retention{
Retention(time.Second),
Retention(10 * time.Second),
Retention(time.Minute),
Retention(10 * time.Minute),
Retention(time.Hour),
Retention(24 * time.Hour),
Retention(24 * 7 * time.Hour),
Retention(24 * 30 * time.Hour),
Retention(24 * 180 * time.Hour),
Retention(24 * 365 * time.Hour),
}

for _, input := range inputs {
str := input.String()
res, err := ParseRetention(str)
require.NoError(t, err)
require.Equal(t, input, res)
}
}
4 changes: 2 additions & 2 deletions policy/staged_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ func TestPoliciesListJSONMarshaling(t *testing.T) {
[]Policy{},
),
},
expected: `[{"cutoverNanos":0,"tombstoned":false,"policies":["10s@1s:6h0m0s","1m0s@1m:24h0m0s|Mean,Count"]},` +
`{"cutoverNanos":100,"tombstoned":true,"policies":["10s@1s:6h0m0s|Sum"]},` +
expected: `[{"cutoverNanos":0,"tombstoned":false,"policies":["10s:6h","1m:1d|Mean,Count"]},` +
`{"cutoverNanos":100,"tombstoned":true,"policies":["10s:6h|Sum"]},` +
`{"cutoverNanos":200,"tombstoned":false,"policies":[]}]`,
},
}
Expand Down
3 changes: 1 addition & 2 deletions policy/storage_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,10 @@ func ParseStoragePolicy(str string) (StoragePolicy, error) {
if err != nil {
return EmptyStoragePolicy, err
}
retentionDuration, err := xtime.ParseExtendedDuration(parts[1])
retention, err := ParseRetention(parts[1])
if err != nil {
return EmptyStoragePolicy, err
}
retention := Retention(retentionDuration)
return StoragePolicy{resolution: resolution, retention: retention}, nil
}

Expand Down
Loading

0 comments on commit b945010

Please sign in to comment.