github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ts/pruning_test.go (about)

     1  // Copyright 2016 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/keys"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/ts/tspb"
    21  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    22  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    23  )
    24  
    25  func TestContainsTimeSeries(t *testing.T) {
    26  	defer leaktest.AfterTest(t)()
    27  	tsdb := (*DB)(nil)
    28  
    29  	for i, tcase := range []struct {
    30  		start    roachpb.RKey
    31  		end      roachpb.RKey
    32  		expected bool
    33  	}{
    34  		{
    35  			roachpb.RKey("a"),
    36  			roachpb.RKey("b"),
    37  			false,
    38  		},
    39  		{
    40  			roachpb.RKeyMin,
    41  			roachpb.RKey(keys.SystemPrefix),
    42  			false,
    43  		},
    44  		{
    45  			roachpb.RKeyMin,
    46  			roachpb.RKeyMax,
    47  			true,
    48  		},
    49  		{
    50  			roachpb.RKeyMin,
    51  			roachpb.RKey(MakeDataKey("metric", "", Resolution10s, 0)),
    52  			true,
    53  		},
    54  		{
    55  			roachpb.RKey(MakeDataKey("metric", "", Resolution10s, 0)),
    56  			roachpb.RKeyMax,
    57  			true,
    58  		},
    59  		{
    60  			roachpb.RKey(MakeDataKey("metric", "", Resolution10s, 0)),
    61  			roachpb.RKey(MakeDataKey("metric.b", "", Resolution10s, 0)),
    62  			true,
    63  		},
    64  	} {
    65  		if actual := tsdb.ContainsTimeSeries(tcase.start, tcase.end); actual != tcase.expected {
    66  			t.Errorf("case %d: was %t, expected %t", i, actual, tcase.expected)
    67  		}
    68  	}
    69  }
    70  
    71  func TestFindTimeSeries(t *testing.T) {
    72  	defer leaktest.AfterTest(t)()
    73  	tm := newTestModelRunner(t)
    74  	tm.Start()
    75  	defer tm.Stop()
    76  
    77  	// Populate data: two metrics, two sources, two resolutions, two keys.
    78  	metrics := []string{"metric.a", "metric.z"}
    79  	sources := []string{"source1", "source2"}
    80  	resolutions := []Resolution{Resolution10s, resolution1ns}
    81  	for _, metric := range metrics {
    82  		for _, source := range sources {
    83  			for _, resolution := range resolutions {
    84  				tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{
    85  					{
    86  						Name:   metric,
    87  						Source: source,
    88  						Datapoints: []tspb.TimeSeriesDatapoint{
    89  							{
    90  								TimestampNanos: 400 * 1e9,
    91  								Value:          1,
    92  							},
    93  							{
    94  								TimestampNanos: 500 * 1e9,
    95  								Value:          2,
    96  							},
    97  						},
    98  					},
    99  				})
   100  			}
   101  		}
   102  	}
   103  
   104  	e := tm.LocalTestCluster.Eng
   105  	for i, tcase := range []struct {
   106  		start     roachpb.RKey
   107  		end       roachpb.RKey
   108  		timestamp hlc.Timestamp
   109  		expected  []timeSeriesResolutionInfo
   110  	}{
   111  		// Entire key range.
   112  		{
   113  			start:     roachpb.RKeyMin,
   114  			end:       roachpb.RKeyMax,
   115  			timestamp: hlc.MaxTimestamp,
   116  			expected: []timeSeriesResolutionInfo{
   117  				{
   118  					Name:       metrics[0],
   119  					Resolution: Resolution10s,
   120  				},
   121  				{
   122  					Name:       metrics[0],
   123  					Resolution: resolution1ns,
   124  				},
   125  				{
   126  					Name:       metrics[1],
   127  					Resolution: Resolution10s,
   128  				},
   129  				{
   130  					Name:       metrics[1],
   131  					Resolution: resolution1ns,
   132  				},
   133  			},
   134  		},
   135  		// Timestamp at 400s means we prune nothing.
   136  		{
   137  			start:     roachpb.RKeyMin,
   138  			end:       roachpb.RKeyMax,
   139  			timestamp: hlc.Timestamp{WallTime: 400 * 1e9},
   140  			expected:  nil,
   141  		},
   142  		// Timestamp at 401s is just at the limit for 1ns time series pruning.
   143  		{
   144  			start:     roachpb.RKeyMin,
   145  			end:       roachpb.RKeyMax,
   146  			timestamp: hlc.Timestamp{WallTime: 401 * 1e9},
   147  			expected:  nil,
   148  		},
   149  		// Timestamp at 401s + 1ns prunes the 400s records at 1ns resolution.
   150  		{
   151  			start:     roachpb.RKeyMin,
   152  			end:       roachpb.RKeyMax,
   153  			timestamp: hlc.Timestamp{WallTime: 401*1e9 + 1},
   154  			expected: []timeSeriesResolutionInfo{
   155  				{
   156  					Name:       metrics[0],
   157  					Resolution: resolution1ns,
   158  				},
   159  				{
   160  					Name:       metrics[1],
   161  					Resolution: resolution1ns,
   162  				},
   163  			},
   164  		},
   165  		// Timestamp at the Resolution10s threshold doesn't prune the 10s resolutions.
   166  		{
   167  			start:     roachpb.RKeyMin,
   168  			end:       roachpb.RKeyMax,
   169  			timestamp: hlc.Timestamp{WallTime: tm.DB.PruneThreshold(Resolution10s)},
   170  			expected: []timeSeriesResolutionInfo{
   171  				{
   172  					Name:       metrics[0],
   173  					Resolution: resolution1ns,
   174  				},
   175  				{
   176  					Name:       metrics[1],
   177  					Resolution: resolution1ns,
   178  				},
   179  			},
   180  		},
   181  		// Timestamp at the Resolution10s threshold + 1ns prunes all time series.
   182  		{
   183  			start:     roachpb.RKeyMin,
   184  			end:       roachpb.RKeyMax,
   185  			timestamp: hlc.Timestamp{WallTime: tm.DB.PruneThreshold(Resolution10s) + 1},
   186  			expected: []timeSeriesResolutionInfo{
   187  				{
   188  					Name:       metrics[0],
   189  					Resolution: Resolution10s,
   190  				},
   191  				{
   192  					Name:       metrics[0],
   193  					Resolution: resolution1ns,
   194  				},
   195  				{
   196  					Name:       metrics[1],
   197  					Resolution: Resolution10s,
   198  				},
   199  				{
   200  					Name:       metrics[1],
   201  					Resolution: resolution1ns,
   202  				},
   203  			},
   204  		},
   205  		// Key range entirely outside of time series range.
   206  		{
   207  			start:     roachpb.RKey("a"),
   208  			end:       roachpb.RKey("b"),
   209  			timestamp: hlc.MaxTimestamp,
   210  			expected:  nil,
   211  		},
   212  		// Key range split between metrics.
   213  		{
   214  			start:     roachpb.RKeyMin,
   215  			end:       roachpb.RKey(MakeDataKey("metric.b", "", Resolution10s, 0)),
   216  			timestamp: hlc.MaxTimestamp,
   217  			expected: []timeSeriesResolutionInfo{
   218  				{
   219  					Name:       metrics[0],
   220  					Resolution: Resolution10s,
   221  				},
   222  				{
   223  					Name:       metrics[0],
   224  					Resolution: resolution1ns,
   225  				},
   226  			},
   227  		},
   228  		{
   229  			start:     roachpb.RKey(MakeDataKey("metric.b", "", Resolution10s, 0)),
   230  			end:       roachpb.RKeyMax,
   231  			timestamp: hlc.MaxTimestamp,
   232  			expected: []timeSeriesResolutionInfo{
   233  				{
   234  					Name:       metrics[1],
   235  					Resolution: Resolution10s,
   236  				},
   237  				{
   238  					Name:       metrics[1],
   239  					Resolution: resolution1ns,
   240  				},
   241  			},
   242  		},
   243  		// Key range split within a metric along resolution boundary.
   244  		{
   245  			start:     roachpb.RKeyMin,
   246  			end:       roachpb.RKey(MakeDataKey(metrics[0], "", resolution1ns, 0)),
   247  			timestamp: hlc.MaxTimestamp,
   248  			expected: []timeSeriesResolutionInfo{
   249  				{
   250  					Name:       metrics[0],
   251  					Resolution: Resolution10s,
   252  				},
   253  			},
   254  		},
   255  		{
   256  			start:     roachpb.RKey(MakeDataKey(metrics[0], "", resolution1ns, 0)),
   257  			end:       roachpb.RKeyMax,
   258  			timestamp: hlc.MaxTimestamp,
   259  			expected: []timeSeriesResolutionInfo{
   260  				{
   261  					Name:       metrics[0],
   262  					Resolution: resolution1ns,
   263  				},
   264  				{
   265  					Name:       metrics[1],
   266  					Resolution: Resolution10s,
   267  				},
   268  				{
   269  					Name:       metrics[1],
   270  					Resolution: resolution1ns,
   271  				},
   272  			},
   273  		},
   274  	} {
   275  		snap := e.NewSnapshot()
   276  		actual, err := tm.DB.findTimeSeries(snap, tcase.start, tcase.end, tcase.timestamp)
   277  		snap.Close()
   278  		if err != nil {
   279  			t.Fatalf("case %d: unexpected error %q", i, err)
   280  		}
   281  
   282  		if !reflect.DeepEqual(actual, tcase.expected) {
   283  			t.Fatalf("case %d: got %v, expected %v", i, actual, tcase.expected)
   284  		}
   285  	}
   286  }
   287  
   288  // Verifies that pruning works as expected when the server has not yet switched
   289  // to columnar format, and thus does not yet support rollups.
   290  func TestPruneTimeSeries(t *testing.T) {
   291  	defer leaktest.AfterTest(t)()
   292  	runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) {
   293  		// Arbitrary timestamp
   294  		var now int64 = 1475700000 * 1e9
   295  
   296  		// Populate data: two metrics, two sources, two resolutions, two keys.
   297  		metrics := []string{"metric.a", "metric.z"}
   298  		sources := []string{"source1", "source2"}
   299  		resolutions := []Resolution{Resolution10s, resolution1ns}
   300  		for _, metric := range metrics {
   301  			for _, source := range sources {
   302  				for _, resolution := range resolutions {
   303  					tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{
   304  						{
   305  							Name:   metric,
   306  							Source: source,
   307  							Datapoints: []tspb.TimeSeriesDatapoint{
   308  								{
   309  									TimestampNanos: now - int64(365*24*time.Hour),
   310  									Value:          2,
   311  								},
   312  								{
   313  									TimestampNanos: now,
   314  									Value:          1,
   315  								},
   316  							},
   317  						},
   318  					})
   319  				}
   320  			}
   321  		}
   322  
   323  		tm.assertModelCorrect()
   324  		tm.assertKeyCount(16)
   325  
   326  		tm.prune(
   327  			now,
   328  			timeSeriesResolutionInfo{
   329  				Name:       "metric.notexists",
   330  				Resolution: resolutions[0],
   331  			},
   332  		)
   333  		tm.assertModelCorrect()
   334  		tm.assertKeyCount(16)
   335  
   336  		tm.prune(
   337  			now,
   338  			timeSeriesResolutionInfo{
   339  				Name:       metrics[0],
   340  				Resolution: resolutions[0],
   341  			},
   342  		)
   343  		tm.assertModelCorrect()
   344  		tm.assertKeyCount(14)
   345  
   346  		tm.prune(
   347  			now,
   348  			timeSeriesResolutionInfo{
   349  				Name:       metrics[0],
   350  				Resolution: resolutions[1],
   351  			},
   352  			timeSeriesResolutionInfo{
   353  				Name:       metrics[1],
   354  				Resolution: resolutions[0],
   355  			},
   356  			timeSeriesResolutionInfo{
   357  				Name:       metrics[1],
   358  				Resolution: resolutions[1],
   359  			},
   360  		)
   361  		tm.assertModelCorrect()
   362  		tm.assertKeyCount(8)
   363  
   364  		tm.prune(
   365  			now+int64(365*24*time.Hour),
   366  			timeSeriesResolutionInfo{
   367  				Name:       metrics[0],
   368  				Resolution: resolutions[0],
   369  			},
   370  			timeSeriesResolutionInfo{
   371  				Name:       metrics[0],
   372  				Resolution: resolutions[1],
   373  			},
   374  			timeSeriesResolutionInfo{
   375  				Name:       metrics[1],
   376  				Resolution: resolutions[0],
   377  			},
   378  			timeSeriesResolutionInfo{
   379  				Name:       metrics[1],
   380  				Resolution: resolutions[1],
   381  			},
   382  		)
   383  		tm.assertModelCorrect()
   384  		tm.assertKeyCount(0)
   385  	})
   386  }
   387  
   388  func TestMaintainTimeSeriesWithRollups(t *testing.T) {
   389  	defer leaktest.AfterTest(t)()
   390  	tm := newTestModelRunner(t)
   391  	tm.Start()
   392  	defer tm.Stop()
   393  
   394  	// Arbitrary timestamp
   395  	var now int64 = 1475700000 * 1e9
   396  
   397  	// Populate data: two metrics, two sources, two resolutions, two keys.
   398  	metrics := []string{"metric.a", "metric.z"}
   399  	sources := []string{"source1", "source2"}
   400  	resolutions := []Resolution{Resolution10s, resolution1ns}
   401  	for _, metric := range metrics {
   402  		for _, source := range sources {
   403  			for _, resolution := range resolutions {
   404  				tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{
   405  					{
   406  						Name:   metric,
   407  						Source: source,
   408  						Datapoints: []tspb.TimeSeriesDatapoint{
   409  							{
   410  								TimestampNanos: now - int64(2*365*24*time.Hour),
   411  								Value:          2,
   412  							},
   413  							{
   414  								TimestampNanos: now,
   415  								Value:          1,
   416  							},
   417  						},
   418  					},
   419  				})
   420  			}
   421  		}
   422  	}
   423  
   424  	tm.assertModelCorrect()
   425  	tm.assertKeyCount(16)
   426  
   427  	// First call to maintain will actually create rollups.
   428  	tm.maintain(now)
   429  	tm.assertModelCorrect()
   430  	tm.assertKeyCount(16)
   431  
   432  	{
   433  		query := tm.makeQuery("metric.a", Resolution30m, 0, now)
   434  		query.assertSuccess(1, 2)
   435  	}
   436  
   437  	// Second call will actually prune the rollups, since they are very far
   438  	// in the past.
   439  	tm.maintain(now)
   440  	tm.assertModelCorrect()
   441  	tm.assertKeyCount(8)
   442  }
   443  
   444  func TestMaintainTimeSeriesNoRollups(t *testing.T) {
   445  	defer leaktest.AfterTest(t)()
   446  	tm := newTestModelRunner(t)
   447  	tm.Start()
   448  	defer tm.Stop()
   449  	tm.DB.forceRowFormat = true
   450  
   451  	// Arbitrary timestamp
   452  	var now int64 = 1475700000 * 1e9
   453  
   454  	// Populate data: two metrics, two sources, two resolutions, two keys.
   455  	metrics := []string{"metric.a", "metric.z"}
   456  	sources := []string{"source1", "source2"}
   457  	resolutions := []Resolution{Resolution10s, resolution1ns}
   458  	for _, metric := range metrics {
   459  		for _, source := range sources {
   460  			for _, resolution := range resolutions {
   461  				tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{
   462  					{
   463  						Name:   metric,
   464  						Source: source,
   465  						Datapoints: []tspb.TimeSeriesDatapoint{
   466  							{
   467  								TimestampNanos: now - int64(2*365*24*time.Hour),
   468  								Value:          2,
   469  							},
   470  							{
   471  								TimestampNanos: now,
   472  								Value:          1,
   473  							},
   474  						},
   475  					},
   476  				})
   477  			}
   478  		}
   479  	}
   480  
   481  	tm.assertModelCorrect()
   482  	tm.assertKeyCount(16)
   483  
   484  	// First call to maintain will prune time series.
   485  	tm.maintain(now)
   486  	tm.assertModelCorrect()
   487  	tm.assertKeyCount(8)
   488  }