github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ts/timeseries_test.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package ts 12 13 import ( 14 "reflect" 15 "testing" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/roachpb" 19 "github.com/cockroachdb/cockroach/pkg/storage" 20 "github.com/cockroachdb/cockroach/pkg/testutils" 21 "github.com/cockroachdb/cockroach/pkg/ts/tspb" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 ) 24 25 func tsd(name, source string, dps ...tspb.TimeSeriesDatapoint) tspb.TimeSeriesData { 26 return tspb.TimeSeriesData{ 27 Name: name, 28 Source: source, 29 Datapoints: dps, 30 } 31 } 32 33 func tsdp(ts time.Duration, val float64) tspb.TimeSeriesDatapoint { 34 return tspb.TimeSeriesDatapoint{ 35 TimestampNanos: ts.Nanoseconds(), 36 Value: val, 37 } 38 } 39 40 // TestToInternal verifies the conversion of tspb.TimeSeriesData to internal storage 41 // format is correct. 42 func TestToInternal(t *testing.T) { 43 defer leaktest.AfterTest(t)() 44 tcases := []struct { 45 keyDuration int64 46 sampleDuration int64 47 columnar bool 48 expectedError string 49 input tspb.TimeSeriesData 50 expected []roachpb.InternalTimeSeriesData 51 }{ 52 { 53 time.Minute.Nanoseconds(), 54 101, 55 false, 56 "does not evenly divide key duration", 57 tsd("error.series", ""), 58 nil, 59 }, 60 { 61 time.Minute.Nanoseconds(), 62 time.Hour.Nanoseconds(), 63 false, 64 "does not evenly divide key duration", 65 tsd("error.series", ""), 66 nil, 67 }, 68 { 69 (24 * time.Hour).Nanoseconds(), 70 (20 * time.Minute).Nanoseconds(), 71 false, 72 "", 73 tsd("test.series", "", 74 tsdp(5*time.Hour+5*time.Minute, 1.0), 75 tsdp(24*time.Hour+39*time.Minute, 2.0), 76 tsdp(10*time.Hour+10*time.Minute, 3.0), 77 tsdp(48*time.Hour, 4.0), 78 tsdp(15*time.Hour+22*time.Minute+1, 5.0), 79 tsdp(52*time.Hour+15*time.Minute, 0.0), 80 ), 81 []roachpb.InternalTimeSeriesData{ 82 { 83 StartTimestampNanos: 0, 84 SampleDurationNanos: 20 * time.Minute.Nanoseconds(), 85 Samples: []roachpb.InternalTimeSeriesSample{ 86 { 87 Offset: 15, 88 Count: 1, 89 Sum: 1.0, 90 }, 91 { 92 Offset: 30, 93 Count: 1, 94 Sum: 3.0, 95 }, 96 { 97 Offset: 46, 98 Count: 1, 99 Sum: 5.0, 100 }, 101 }, 102 }, 103 { 104 StartTimestampNanos: 24 * time.Hour.Nanoseconds(), 105 SampleDurationNanos: 20 * time.Minute.Nanoseconds(), 106 Samples: []roachpb.InternalTimeSeriesSample{ 107 { 108 Offset: 1, 109 Count: 1, 110 Sum: 2.0, 111 }, 112 }, 113 }, 114 { 115 StartTimestampNanos: 48 * time.Hour.Nanoseconds(), 116 SampleDurationNanos: 20 * time.Minute.Nanoseconds(), 117 Samples: []roachpb.InternalTimeSeriesSample{ 118 { 119 Offset: 0, 120 Count: 1, 121 Sum: 4.0, 122 }, 123 { 124 Offset: 12, 125 Count: 1, 126 Sum: 0.0, 127 }, 128 }, 129 }, 130 }, 131 }, 132 { 133 (24 * time.Hour).Nanoseconds(), 134 (20 * time.Minute).Nanoseconds(), 135 true, 136 "", 137 tsd("test.series", "", 138 tsdp(5*time.Hour+5*time.Minute, 1.0), 139 tsdp(24*time.Hour+39*time.Minute, 2.0), 140 tsdp(10*time.Hour+10*time.Minute, 3.0), 141 tsdp(48*time.Hour, 4.0), 142 tsdp(15*time.Hour+22*time.Minute+1, 5.0), 143 tsdp(52*time.Hour+15*time.Minute, 0.0), 144 ), 145 []roachpb.InternalTimeSeriesData{ 146 { 147 StartTimestampNanos: 0, 148 SampleDurationNanos: 20 * time.Minute.Nanoseconds(), 149 Offset: []int32{15, 30, 46}, 150 Last: []float64{1.0, 3.0, 5.0}, 151 }, 152 { 153 StartTimestampNanos: 24 * time.Hour.Nanoseconds(), 154 SampleDurationNanos: 20 * time.Minute.Nanoseconds(), 155 Offset: []int32{1}, 156 Last: []float64{2.0}, 157 }, 158 { 159 StartTimestampNanos: 48 * time.Hour.Nanoseconds(), 160 SampleDurationNanos: 20 * time.Minute.Nanoseconds(), 161 Offset: []int32{0, 12}, 162 Last: []float64{4.0, 0.0}, 163 }, 164 }, 165 }, 166 } 167 168 for i, tc := range tcases { 169 actual, err := tc.input.ToInternal(tc.keyDuration, tc.sampleDuration, tc.columnar) 170 if !testutils.IsError(err, tc.expectedError) { 171 t.Errorf("expected error %q from case %d, got %v", tc.expectedError, i, err) 172 } 173 174 if !reflect.DeepEqual(actual, tc.expected) { 175 t.Errorf("case %d fails: ToInternal result was %v, expected %v", i, actual, tc.expected) 176 } 177 } 178 } 179 180 // TestDiscardEarlierSamples verifies that only a single sample is kept for each 181 // sample period; earlier samples are discarded. 182 func TestDiscardEarlierSamples(t *testing.T) { 183 defer leaktest.AfterTest(t)() 184 // TODO(ajkr): this should also run on Pebble. Maybe combine it into the merge_test.go tests or prove 185 // it is redundant with the tests there. 186 ts := tsd("test.series", "", 187 tsdp(5*time.Hour+5*time.Minute, -1.0), 188 tsdp(5*time.Hour+5*time.Minute, -2.0), 189 ) 190 internal, err := ts.ToInternal(Resolution10s.SlabDuration(), Resolution10s.SampleDuration(), false) 191 if err != nil { 192 t.Fatal(err) 193 } 194 195 out, err := storage.MergeInternalTimeSeriesData(true /* mergeIntoNil */, false /* usePartialMerge */, internal...) 196 if err != nil { 197 t.Fatal(err) 198 } 199 200 if len(out.Samples) > 0 { 201 if maxVal := out.Samples[0].Max; maxVal != nil { 202 t.Fatal("Expected maximum of sample 0 to be nil; samples are no longer merged") 203 } 204 if a, e := out.Samples[0].Sum, -2.0; a != e { 205 t.Fatalf("Expected sum of sample 0 to be %f after initial merge, was %f", e, a) 206 } 207 } else { 208 t.Fatal("All samples unexpectedly discarded") 209 } 210 }