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  }