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

     1  // Copyright 2018 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 "fmt"
    14  
    15  // QueryTimespan describes the time range information for a query - the start
    16  // and end bounds of the query, along with the requested duration of individual
    17  // samples to be returned. Methods of this structure are mutating.
    18  type QueryTimespan struct {
    19  	StartNanos          int64
    20  	EndNanos            int64
    21  	NowNanos            int64
    22  	SampleDurationNanos int64
    23  }
    24  
    25  // width returns the width of the timespan: the distance between its start and
    26  // and end bounds.
    27  func (qt *QueryTimespan) width() int64 {
    28  	return qt.EndNanos - qt.StartNanos
    29  }
    30  
    31  // moveForward modifies the timespan so that it has the same width, but
    32  // both StartNanos and EndNanos is moved forward by the specified number of
    33  // nanoseconds.
    34  func (qt *QueryTimespan) moveForward(forwardNanos int64) {
    35  	qt.StartNanos += forwardNanos
    36  	qt.EndNanos += forwardNanos
    37  }
    38  
    39  // expand modifies the timespan so that its width is expanded *on each side*
    40  // by the supplied size; the resulting width will be (2 * size) larger than the
    41  // original width.
    42  func (qt *QueryTimespan) expand(size int64) {
    43  	qt.StartNanos -= size
    44  	qt.EndNanos += size
    45  }
    46  
    47  // normalize modifies startNanos and endNanos so that they are exact multiples
    48  // of the sampleDuration. Values are modified by subtraction.
    49  func (qt *QueryTimespan) normalize() {
    50  	qt.StartNanos -= qt.StartNanos % qt.SampleDurationNanos
    51  	qt.EndNanos -= qt.EndNanos % qt.SampleDurationNanos
    52  }
    53  
    54  // verifyBounds returns an error if the bounds of this QueryTimespan are
    55  // incorrect; currently, this only occurs if the width is negative.
    56  func (qt *QueryTimespan) verifyBounds() error {
    57  	if qt.StartNanos > qt.EndNanos {
    58  		return fmt.Errorf("startNanos %d was later than endNanos %d", qt.StartNanos, qt.EndNanos)
    59  	}
    60  	return nil
    61  }
    62  
    63  // verifyDiskResolution returns an error if this timespan is not suitable for
    64  // querying the supplied disk resolution.
    65  func (qt *QueryTimespan) verifyDiskResolution(diskResolution Resolution) error {
    66  	resolutionSampleDuration := diskResolution.SampleDuration()
    67  	// Verify that sampleDuration is a multiple of
    68  	// diskResolution.SampleDuration().
    69  	if qt.SampleDurationNanos < resolutionSampleDuration {
    70  		return fmt.Errorf(
    71  			"sampleDuration %d was not less that queryResolution.SampleDuration %d",
    72  			qt.SampleDurationNanos,
    73  			resolutionSampleDuration,
    74  		)
    75  	}
    76  	if qt.SampleDurationNanos%resolutionSampleDuration != 0 {
    77  		return fmt.Errorf(
    78  			"sampleDuration %d is not a multiple of queryResolution.SampleDuration %d",
    79  			qt.SampleDurationNanos,
    80  			resolutionSampleDuration,
    81  		)
    82  	}
    83  	return nil
    84  }
    85  
    86  // adjustForCurrentTime adjusts the passed query timespan in order to prevent
    87  // certain artifacts which can occur when querying in the very recent past.
    88  func (qt *QueryTimespan) adjustForCurrentTime(diskResolution Resolution) error {
    89  	// Disallow queries for the sample period containing the current system time
    90  	// and any later periods. This prevents returning "incomplete" data for sample
    91  	// periods where new data may yet be recorded, which in turn prevents an odd
    92  	// user experience where graphs of recent metric data have a precipitous "dip"
    93  	// at latest timestamp.
    94  	cutoff := qt.NowNanos - qt.SampleDurationNanos
    95  
    96  	// Do not allow queries in the future.
    97  	if qt.StartNanos > cutoff {
    98  		return fmt.Errorf(
    99  			"cannot query time series in the future (start time %d was greater than current clock %d",
   100  			qt.StartNanos,
   101  			qt.NowNanos,
   102  		)
   103  	}
   104  	if qt.EndNanos > cutoff {
   105  		qt.EndNanos = cutoff
   106  	}
   107  
   108  	return nil
   109  }