feat(tsdb): Teach storage how to only decode timestamps from a block

TimestampArray.Contains(min,max) API performs a binary search to
determine if timestamps exist for the given time interval.

It also implements Exclude to drop timestamps that have been tombstoned.

DecodeTimestampArrayBlock decodes only the timestamps of the provided
block.
pull/13426/head
Stuart Carnie 2019-04-17 16:16:20 -07:00
parent 7fc9661b7b
commit 36a33bcb9f
No known key found for this signature in database
GPG Key ID: 848D9C9718D78B4F
6 changed files with 508 additions and 271 deletions

View File

@ -7,11 +7,12 @@ import "github.com/influxdata/influxdb/tsdb/cursors"
// talk about consuming data. // talk about consuming data.
type ( type (
IntegerArray = cursors.IntegerArray IntegerArray = cursors.IntegerArray
FloatArray = cursors.FloatArray FloatArray = cursors.FloatArray
UnsignedArray = cursors.UnsignedArray UnsignedArray = cursors.UnsignedArray
StringArray = cursors.StringArray StringArray = cursors.StringArray
BooleanArray = cursors.BooleanArray BooleanArray = cursors.BooleanArray
TimestampArray = cursors.TimestampArray
IntegerArrayCursor = cursors.IntegerArrayCursor IntegerArrayCursor = cursors.IntegerArrayCursor
FloatArrayCursor = cursors.FloatArrayCursor FloatArrayCursor = cursors.FloatArrayCursor

View File

@ -34,6 +34,50 @@ func (a *FloatArray) Len() int {
return len(a.Timestamps) return len(a.Timestamps)
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *FloatArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *FloatArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Exclude removes the subset of values in [min, max]. The values must // Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined. // be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *FloatArray) Exclude(min, max int64) { func (a *FloatArray) Exclude(min, max int64) {
@ -96,50 +140,6 @@ func (a *FloatArray) Include(min, max int64) {
} }
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *FloatArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *FloatArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Merge overlays b to top of a. If two values conflict with // Merge overlays b to top of a. If two values conflict with
// the same timestamp, b is used. Both a and b must be sorted // the same timestamp, b is used. Both a and b must be sorted
// in ascending order. // in ascending order.
@ -235,6 +235,50 @@ func (a *IntegerArray) Len() int {
return len(a.Timestamps) return len(a.Timestamps)
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *IntegerArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *IntegerArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Exclude removes the subset of values in [min, max]. The values must // Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined. // be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *IntegerArray) Exclude(min, max int64) { func (a *IntegerArray) Exclude(min, max int64) {
@ -297,50 +341,6 @@ func (a *IntegerArray) Include(min, max int64) {
} }
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *IntegerArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *IntegerArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Merge overlays b to top of a. If two values conflict with // Merge overlays b to top of a. If two values conflict with
// the same timestamp, b is used. Both a and b must be sorted // the same timestamp, b is used. Both a and b must be sorted
// in ascending order. // in ascending order.
@ -436,6 +436,50 @@ func (a *UnsignedArray) Len() int {
return len(a.Timestamps) return len(a.Timestamps)
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *UnsignedArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *UnsignedArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Exclude removes the subset of values in [min, max]. The values must // Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined. // be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *UnsignedArray) Exclude(min, max int64) { func (a *UnsignedArray) Exclude(min, max int64) {
@ -498,50 +542,6 @@ func (a *UnsignedArray) Include(min, max int64) {
} }
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *UnsignedArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *UnsignedArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Merge overlays b to top of a. If two values conflict with // Merge overlays b to top of a. If two values conflict with
// the same timestamp, b is used. Both a and b must be sorted // the same timestamp, b is used. Both a and b must be sorted
// in ascending order. // in ascending order.
@ -637,6 +637,50 @@ func (a *StringArray) Len() int {
return len(a.Timestamps) return len(a.Timestamps)
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *StringArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *StringArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Exclude removes the subset of values in [min, max]. The values must // Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined. // be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *StringArray) Exclude(min, max int64) { func (a *StringArray) Exclude(min, max int64) {
@ -699,50 +743,6 @@ func (a *StringArray) Include(min, max int64) {
} }
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *StringArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *StringArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Merge overlays b to top of a. If two values conflict with // Merge overlays b to top of a. If two values conflict with
// the same timestamp, b is used. Both a and b must be sorted // the same timestamp, b is used. Both a and b must be sorted
// in ascending order. // in ascending order.
@ -838,6 +838,50 @@ func (a *BooleanArray) Len() int {
return len(a.Timestamps) return len(a.Timestamps)
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *BooleanArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *BooleanArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Exclude removes the subset of values in [min, max]. The values must // Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined. // be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *BooleanArray) Exclude(min, max int64) { func (a *BooleanArray) Exclude(min, max int64) {
@ -900,50 +944,6 @@ func (a *BooleanArray) Include(min, max int64) {
} }
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *BooleanArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *BooleanArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Merge overlays b to top of a. If two values conflict with // Merge overlays b to top of a. If two values conflict with
// the same timestamp, b is used. Both a and b must be sorted // the same timestamp, b is used. Both a and b must be sorted
// in ascending order. // in ascending order.
@ -1010,3 +1010,123 @@ func (a *BooleanArray) Merge(b *BooleanArray) {
a.Timestamps = out.Timestamps[:k] a.Timestamps = out.Timestamps[:k]
a.Values = out.Values[:k] a.Values = out.Values[:k]
} }
type TimestampArray struct {
Timestamps []int64
}
func NewTimestampArrayLen(sz int) *TimestampArray {
return &TimestampArray{
Timestamps: make([]int64, sz),
}
}
func (a *TimestampArray) MinTime() int64 {
return a.Timestamps[0]
}
func (a *TimestampArray) MaxTime() int64 {
return a.Timestamps[len(a.Timestamps)-1]
}
func (a *TimestampArray) Size() int {
panic("not implemented")
}
func (a *TimestampArray) Len() int {
return len(a.Timestamps)
}
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *TimestampArray) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *TimestampArray) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Exclude removes the subset of timestamps in [min, max]. The timestamps must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *TimestampArray) Exclude(min, max int64) {
rmin, rmax := a.FindRange(min, max)
if rmin == -1 && rmax == -1 {
return
}
// a.Timestamps[rmin] ≥ min
// a.Timestamps[rmax] ≥ max
if rmax < a.Len() {
if a.Timestamps[rmax] == max {
rmax++
}
rest := a.Len() - rmax
if rest > 0 {
ts := a.Timestamps[:rmin+rest]
copy(ts[rmin:], a.Timestamps[rmax:])
a.Timestamps = ts
return
}
}
a.Timestamps = a.Timestamps[:rmin]
}
// Contains returns true if values exist between min and max inclusive. The
// values must be deduplicated and sorted before calling Contains or the
// results are undefined.
func (a *TimestampArray) Contains(min, max int64) bool {
rmin, rmax := a.FindRange(min, max)
if rmin == -1 && rmax == -1 {
return false
}
// a.Timestamps[rmin] ≥ min
// a.Timestamps[rmax] ≥ max
if a.Timestamps[rmin] == min {
return true
}
if rmax < a.Len() && a.Timestamps[rmax] == max {
return true
}
return rmax-rmin > 0
}

View File

@ -1,18 +1,22 @@
package cursors package cursors
{{range .}} {{range .}}
{{- $typename := print .Name "Array" }}
{{ $typename := print .Name "Array" }} {{- $hasType := or (and .Type true) false }}
type {{ $typename }} struct { type {{ $typename }} struct {
Timestamps []int64 Timestamps []int64
{{- if $hasType }}
Values []{{.Type}} Values []{{.Type}}
{{- end }}
} }
func New{{$typename}}Len(sz int) *{{$typename}} { func New{{$typename}}Len(sz int) *{{$typename}} {
return &{{$typename}}{ return &{{$typename}}{
Timestamps: make([]int64, sz), Timestamps: make([]int64, sz),
{{- if $hasType }}
Values: make([]{{.Type}}, sz), Values: make([]{{.Type}}, sz),
{{- end }}
} }
} }
@ -32,6 +36,51 @@ func (a *{{ $typename}}) Len() int {
return len(a.Timestamps) return len(a.Timestamps)
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *{{ $typename }}) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *{{ $typename }}) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
{{- if $hasType }}
// Exclude removes the subset of values in [min, max]. The values must // Exclude removes the subset of values in [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results are undefined. // be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *{{ $typename }}) Exclude(min, max int64) { func (a *{{ $typename }}) Exclude(min, max int64) {
@ -94,50 +143,6 @@ func (a *{{ $typename }}) Include(min, max int64) {
} }
} }
// search performs a binary search for UnixNano() v in a
// and returns the position, i, where v would be inserted.
// An additional check of a.Timestamps[i] == v is necessary
// to determine if the value v exists.
func (a *{{ $typename }}) search(v int64) int {
// Define: f(x) → a.Timestamps[x] < v
// Define: f(-1) == true, f(n) == false
// Invariant: f(lo-1) == true, f(hi) == false
lo := 0
hi := a.Len()
for lo < hi {
mid := int(uint(lo+hi) >> 1)
if a.Timestamps[mid] < v {
lo = mid + 1 // preserves f(lo-1) == true
} else {
hi = mid // preserves f(hi) == false
}
}
// lo == hi
return lo
}
// FindRange returns the positions where min and max would be
// inserted into the array. If a[0].UnixNano() > max or
// a[len-1].UnixNano() < min then FindRange returns (-1, -1)
// indicating the array is outside the [min, max]. The values must
// be deduplicated and sorted before calling Exclude or the results
// are undefined.
func (a *{{ $typename }}) FindRange(min, max int64) (int, int) {
if a.Len() == 0 || min > max {
return -1, -1
}
minVal := a.MinTime()
maxVal := a.MaxTime()
if maxVal < min || minVal > max {
return -1, -1
}
return a.search(min), a.search(max)
}
// Merge overlays b to top of a. If two values conflict with // Merge overlays b to top of a. If two values conflict with
// the same timestamp, b is used. Both a and b must be sorted // the same timestamp, b is used. Both a and b must be sorted
// in ascending order. // in ascending order.
@ -204,5 +209,56 @@ func (a *{{ $typename }}) Merge(b *{{ $typename }}) {
a.Timestamps = out.Timestamps[:k] a.Timestamps = out.Timestamps[:k]
a.Values = out.Values[:k] a.Values = out.Values[:k]
} }
{{ else }}
// Exclude removes the subset of timestamps in [min, max]. The timestamps must
// be deduplicated and sorted before calling Exclude or the results are undefined.
func (a *{{ $typename }}) Exclude(min, max int64) {
rmin, rmax := a.FindRange(min, max)
if rmin == -1 && rmax == -1 {
return
}
// a.Timestamps[rmin] ≥ min
// a.Timestamps[rmax] ≥ max
if rmax < a.Len() {
if a.Timestamps[rmax] == max {
rmax++
}
rest := a.Len()-rmax
if rest > 0 {
ts := a.Timestamps[:rmin+rest]
copy(ts[rmin:], a.Timestamps[rmax:])
a.Timestamps = ts
return
}
}
a.Timestamps = a.Timestamps[:rmin]
}
// Contains returns true if values exist between min and max inclusive. The
// values must be deduplicated and sorted before calling Contains or the
// results are undefined.
func (a *{{ $typename }}) Contains(min, max int64) bool {
rmin, rmax := a.FindRange(min, max)
if rmin == -1 && rmax == -1 {
return false
}
// a.Timestamps[rmin] ≥ min
// a.Timestamps[rmax] ≥ max
if a.Timestamps[rmin] == min {
return true
}
if rmax < a.Len() && a.Timestamps[rmax] == max {
return true
}
return rmax-rmin > 0
}
{{ end }}
{{ end }} {{ end }}

View File

@ -18,5 +18,9 @@
{ {
"Name":"Boolean", "Name":"Boolean",
"Type":"bool" "Type":"bool"
},
{
"Name":"Timestamp",
"Type": null
} }
] ]

View File

@ -122,6 +122,50 @@ func TestIntegerArray_Include(t *testing.T) {
} }
} }
func makeTimestampArray(count int, min, max int64) *TimestampArray {
vals := NewTimestampArrayLen(count)
ts := min
inc := (max - min) / int64(count)
for i := 0; i < count; i++ {
vals.Timestamps[i] = ts
ts += inc
}
return vals
}
func TestTimestampArray_Contains(t *testing.T) {
cases := []struct {
n string
min, max int64
exp bool
}{
{"no/lo", 0, 9, false},
{"no/hi", 19, 30, false},
{"no/middle", 13, 13, false},
{"yes/first", 0, 10, true},
{"yes/first-eq", 10, 10, true},
{"yes/last", 18, 20, true},
{"yes/last-eq", 18, 18, true},
{"yes/all but first and last", 12, 16, true},
{"yes/middle-eq", 14, 14, true},
{"yes/middle-overlap", 13, 15, true},
{"yes/covers", 8, 22, true},
}
for _, tc := range cases {
t.Run(fmt.Sprintf("%s[%d,%d]", tc.n, tc.min, tc.max), func(t *testing.T) {
vals := makeTimestampArray(5, 10, 20)
if got := vals.Contains(tc.min, tc.max); got != tc.exp {
t.Errorf("Contains -got/+exp\n%s", cmp.Diff(got, tc.exp))
}
})
}
}
func benchExclude(b *testing.B, vals *IntegerArray, min, max int64) { func benchExclude(b *testing.B, vals *IntegerArray, min, max int64) {
b.ResetTimer() b.ResetTimer()

View File

@ -110,3 +110,15 @@ func DecodeStringArrayBlock(block []byte, a *tsdb.StringArray) error {
a.Values, err = StringArrayDecodeAll(vb, a.Values) a.Values, err = StringArrayDecodeAll(vb, a.Values)
return err return err
} }
// DecodeTimestampArrayBlock decodes the timestamps from the specified
// block, ignoring the block type and the values.
func DecodeTimestampArrayBlock(block []byte, a *tsdb.TimestampArray) error {
tb, _, err := unpackBlock(block[1:])
if err != nil {
return err
}
a.Timestamps, err = TimeArrayDecodeAll(tb, a.Timestamps)
return err
}