feat(tsm1): Provide columnar value types
* separate slices for time and values * structured to be Arrow ready * batch decoders fill time and value slices independently that vastly improves performance (benchmarks linked in PR)pull/10084/head
parent
b3e53ae2dc
commit
9c29cd69e5
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,207 @@
|
|||
package tsdb
|
||||
|
||||
{{range .}}
|
||||
|
||||
{{ $typename := print .Name "Array" }}
|
||||
|
||||
type {{ $typename }} struct {
|
||||
Timestamps []int64
|
||||
Values []{{.Type}}
|
||||
}
|
||||
|
||||
func New{{$typename}}Len(sz int) *{{$typename}} {
|
||||
return &{{$typename}}{
|
||||
Timestamps: make([]int64, sz),
|
||||
Values: make([]{{.Type}}, sz),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *{{ $typename }}) MinTime() int64 {
|
||||
return a.Timestamps[0]
|
||||
}
|
||||
|
||||
func (a *{{ $typename }}) MaxTime() int64 {
|
||||
return a.Timestamps[len(a.Timestamps)-1]
|
||||
}
|
||||
|
||||
func (a *{{ $typename }}) Size() int {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (a *{{ $typename}}) Len() int {
|
||||
return len(a.Timestamps)
|
||||
}
|
||||
|
||||
// Exclude removes the subset of values not in [min, max]. The values 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
|
||||
|
||||
vs := a.Values[:rmin+rest]
|
||||
copy(vs[rmin:], a.Values[rmax:])
|
||||
a.Values = vs
|
||||
}
|
||||
} else {
|
||||
a.Timestamps = a.Timestamps[:rmin]
|
||||
a.Values = a.Values[:rmin]
|
||||
}
|
||||
}
|
||||
|
||||
// Include returns the subset values between min and max inclusive. The values must
|
||||
// be deduplicated and sorted before calling Exclude or the results are undefined.
|
||||
func (a *{{ $typename }}) Include(min, max int64) {
|
||||
rmin, rmax := a.FindRange(min, max)
|
||||
if rmin == -1 && rmax == -1 {
|
||||
a.Timestamps = a.Timestamps[:0]
|
||||
a.Values = a.Values[:0]
|
||||
return
|
||||
}
|
||||
|
||||
// a.Timestamps[rmin] ≥ min
|
||||
// a.Timestamps[rmax] ≥ max
|
||||
|
||||
if rmax < a.Len() && a.Timestamps[rmax] == max {
|
||||
rmax++
|
||||
}
|
||||
|
||||
if rmin > -1 {
|
||||
ts := a.Timestamps[:rmax-rmin]
|
||||
copy(ts, a.Timestamps[rmin:rmax])
|
||||
a.Timestamps = ts
|
||||
vs := a.Values[:rmax-rmin]
|
||||
copy(vs, a.Values[rmin:rmax])
|
||||
a.Values = vs
|
||||
} else {
|
||||
a.Timestamps = a.Timestamps[:rmax]
|
||||
a.Values = a.Values[:rmax]
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// the same timestamp, b is used. Both a and b must be sorted
|
||||
// in ascending order.
|
||||
func (a *{{ $typename }}) Merge(b *{{ $typename }}) {
|
||||
if a.Len() == 0 {
|
||||
*a = *b
|
||||
return
|
||||
}
|
||||
|
||||
if b.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Normally, both a and b should not contain duplicates. Due to a bug in older versions, it's
|
||||
// possible stored blocks might contain duplicate values. Remove them if they exists before
|
||||
// merging.
|
||||
// a = a.Deduplicate()
|
||||
// b = b.Deduplicate()
|
||||
|
||||
if a.MaxTime() < b.MinTime() {
|
||||
a.Timestamps = append(a.Timestamps, b.Timestamps...)
|
||||
a.Values = append(a.Values, b.Values...)
|
||||
return
|
||||
}
|
||||
|
||||
if b.MaxTime() < a.MinTime() {
|
||||
var tmp {{$typename}}
|
||||
tmp.Timestamps = append(b.Timestamps, a.Timestamps...)
|
||||
tmp.Values = append(b.Values, a.Values...)
|
||||
*a = tmp
|
||||
return
|
||||
}
|
||||
|
||||
out := New{{$typename}}Len(a.Len()+b.Len())
|
||||
i, j, k := 0, 0, 0
|
||||
for i < len(a.Timestamps) && j < len(b.Timestamps) {
|
||||
if a.Timestamps[i] < b.Timestamps[j] {
|
||||
out.Timestamps[k] = a.Timestamps[i]
|
||||
out.Values[k] = a.Values[i]
|
||||
i++
|
||||
} else if a.Timestamps[i] == b.Timestamps[j] {
|
||||
out.Timestamps[k] = b.Timestamps[j]
|
||||
out.Values[k] = b.Values[j]
|
||||
i++
|
||||
j++
|
||||
} else {
|
||||
out.Timestamps[k] = b.Timestamps[j]
|
||||
out.Values[k] = b.Values[j]
|
||||
j++
|
||||
}
|
||||
k++
|
||||
}
|
||||
|
||||
if i < len(a.Timestamps) {
|
||||
n := copy(out.Timestamps[k:], a.Timestamps[i:])
|
||||
copy(out.Values[k:], a.Values[i:])
|
||||
k += n
|
||||
} else if j < len(b.Timestamps) {
|
||||
n := copy(out.Timestamps[k:], b.Timestamps[j:])
|
||||
copy(out.Values[k:], b.Values[j:])
|
||||
k += n
|
||||
}
|
||||
|
||||
a.Timestamps = out.Timestamps[:k]
|
||||
a.Values = out.Values[:k]
|
||||
}
|
||||
|
||||
{{ end }}
|
|
@ -0,0 +1,27 @@
|
|||
[
|
||||
{
|
||||
"Name":"Float",
|
||||
"name":"float",
|
||||
"Type":"float64"
|
||||
},
|
||||
{
|
||||
"Name":"Integer",
|
||||
"name":"integer",
|
||||
"Type":"int64"
|
||||
},
|
||||
{
|
||||
"Name":"Unsigned",
|
||||
"name":"unsigned",
|
||||
"Type":"uint64"
|
||||
},
|
||||
{
|
||||
"Name":"String",
|
||||
"name":"string",
|
||||
"Type":"string"
|
||||
},
|
||||
{
|
||||
"Name":"Boolean",
|
||||
"name":"boolean",
|
||||
"Type":"bool"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,209 @@
|
|||
package tsdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func makeIntegerArray(count int, min, max int64) *IntegerArray {
|
||||
vals := NewIntegerArrayLen(count)
|
||||
|
||||
ts := min
|
||||
inc := (max - min) / int64(count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
vals.Timestamps[i] = ts
|
||||
ts += inc
|
||||
}
|
||||
|
||||
return vals
|
||||
}
|
||||
|
||||
func makeIntegerArrayFromSlice(t []int64) *IntegerArray {
|
||||
iv := NewIntegerArrayLen(len(t))
|
||||
copy(iv.Timestamps, t)
|
||||
return iv
|
||||
}
|
||||
|
||||
func TestIntegerArray_FindRangeNoValues(t *testing.T) {
|
||||
var vals IntegerArray
|
||||
l, r := vals.FindRange(0, 100)
|
||||
if exp := -1; l != exp {
|
||||
t.Errorf("invalid l; exp=%d, got=%d", exp, l)
|
||||
}
|
||||
if exp := -1; r != exp {
|
||||
t.Errorf("invalid r; exp=%d, got=%d", exp, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegerArray_FindRange(t *testing.T) {
|
||||
vals := makeIntegerArrayFromSlice([]int64{10, 11, 13, 15, 17, 20, 21})
|
||||
|
||||
cases := []struct {
|
||||
min, max int64
|
||||
l, r int
|
||||
}{
|
||||
{12, 20, 2, 5},
|
||||
{22, 40, -1, -1},
|
||||
{1, 9, -1, -1},
|
||||
{1, 10, 0, 0},
|
||||
{1, 11, 0, 1},
|
||||
{15, 15, 3, 3},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%d→%d", tc.min, tc.max), func(t *testing.T) {
|
||||
l, r := vals.FindRange(tc.min, tc.max)
|
||||
if l != tc.l {
|
||||
t.Errorf("left: got %d, exp %d", l, tc.l)
|
||||
}
|
||||
if r != tc.r {
|
||||
t.Errorf("right: got %d, exp %d", r, tc.r)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegerArray_Exclude(t *testing.T) {
|
||||
cases := []struct {
|
||||
n string
|
||||
min, max int64
|
||||
exp []int64
|
||||
}{
|
||||
{"excl bad range", 18, 11, []int64{10, 12, 14, 16, 18}},
|
||||
{"excl none-lo", 0, 9, []int64{10, 12, 14, 16, 18}},
|
||||
{"excl none-hi", 19, 30, []int64{10, 12, 14, 16, 18}},
|
||||
{"excl first", 0, 10, []int64{12, 14, 16, 18}},
|
||||
{"excl last", 18, 20, []int64{10, 12, 14, 16}},
|
||||
{"excl all but first and last", 12, 16, []int64{10, 18}},
|
||||
{"excl none in middle", 13, 13, []int64{10, 12, 14, 16, 18}},
|
||||
{"excl middle", 14, 14, []int64{10, 12, 16, 18}},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%s[%d,%d]", tc.n, tc.min, tc.max), func(t *testing.T) {
|
||||
vals := makeIntegerArray(5, 10, 20)
|
||||
vals.Exclude(tc.min, tc.max)
|
||||
got := vals.Timestamps
|
||||
if !cmp.Equal(got, tc.exp) {
|
||||
t.Errorf("unexpected values -got/+exp\n%s", cmp.Diff(got, tc.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegerArray_Include(t *testing.T) {
|
||||
cases := []struct {
|
||||
n string
|
||||
min, max int64
|
||||
exp []int64
|
||||
}{
|
||||
{"incl none-lo", 0, 9, []int64{}},
|
||||
{"incl none-hi", 19, 30, []int64{}},
|
||||
{"incl first", 0, 10, []int64{10}},
|
||||
{"incl last", 18, 20, []int64{18}},
|
||||
{"incl all but first and last", 12, 16, []int64{12, 14, 16}},
|
||||
{"incl none in middle", 13, 13, []int64{}},
|
||||
{"incl middle", 14, 14, []int64{14}},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(fmt.Sprintf("%s[%d,%d]", tc.n, tc.min, tc.max), func(t *testing.T) {
|
||||
vals := makeIntegerArray(5, 10, 20)
|
||||
vals.Include(tc.min, tc.max)
|
||||
got := vals.Timestamps
|
||||
if !cmp.Equal(got, tc.exp) {
|
||||
t.Errorf("unexpected values -got/+exp\n%s", cmp.Diff(got, tc.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchExclude(b *testing.B, vals *IntegerArray, min, max int64) {
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
vals.Exclude(min, max)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeNone_1000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(1000, 1000, 2000), 0, 500)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeMiddleHalf_1000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(1000, 1000, 2000), 1250, 1750)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeFirst_1000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(1000, 1000, 2000), 0, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeLast_1000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(1000, 1000, 2000), 1999, 2000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeNone_10000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(10000, 10000, 20000), 00, 5000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeMiddleHalf_10000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(10000, 10000, 20000), 12500, 17500)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeFirst_10000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(10000, 10000, 20000), 0, 10000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_ExcludeLast_10000(b *testing.B) {
|
||||
benchExclude(b, makeIntegerArray(10000, 10000, 20000), 19999, 20000)
|
||||
}
|
||||
|
||||
func benchInclude(b *testing.B, vals *IntegerArray, min, max int64) {
|
||||
src := *vals
|
||||
tmp := NewIntegerArrayLen(vals.Len())
|
||||
copy(tmp.Timestamps, vals.Timestamps)
|
||||
copy(tmp.Values, vals.Values)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
vals.Include(min, max)
|
||||
*vals = src
|
||||
copy(vals.Timestamps, tmp.Timestamps)
|
||||
copy(vals.Values, tmp.Values)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeNone_1000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(1000, 1000, 2000), 0, 500)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeMiddleHalf_1000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(1000, 1000, 2000), 1250, 1750)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeFirst_1000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(1000, 1000, 2000), 0, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeLast_1000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(1000, 1000, 2000), 1999, 2000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeNone_10000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(10000, 10000, 20000), 00, 5000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeMiddleHalf_10000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(10000, 10000, 20000), 12500, 17500)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeFirst_10000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(10000, 10000, 20000), 0, 10000)
|
||||
}
|
||||
|
||||
func BenchmarkIntegerArray_IncludeLast_10000(b *testing.B) {
|
||||
benchInclude(b, makeIntegerArray(10000, 10000, 20000), 19999, 20000)
|
||||
}
|
|
@ -0,0 +1,459 @@
|
|||
package tsdb_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/influxdb/tsdb"
|
||||
)
|
||||
|
||||
func makeBooleanArray(v ...interface{}) *tsdb.BooleanArray {
|
||||
if len(v)&1 == 1 {
|
||||
panic("invalid array length")
|
||||
}
|
||||
a := tsdb.NewBooleanArrayLen(len(v) / 2)
|
||||
for i := 0; i < len(v); i += 2 {
|
||||
a.Timestamps[i/2] = int64(v[i].(int))
|
||||
a.Values[i/2] = v[i+1].(bool)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func makeFloatArray(v ...interface{}) *tsdb.FloatArray {
|
||||
if len(v)&1 == 1 {
|
||||
panic("invalid array length")
|
||||
}
|
||||
a := tsdb.NewFloatArrayLen(len(v) / 2)
|
||||
for i := 0; i < len(v); i += 2 {
|
||||
a.Timestamps[i/2] = int64(v[i].(int))
|
||||
a.Values[i/2] = v[i+1].(float64)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func makeIntegerArray(v ...interface{}) *tsdb.IntegerArray {
|
||||
if len(v)&1 == 1 {
|
||||
panic("invalid array length")
|
||||
}
|
||||
a := tsdb.NewIntegerArrayLen(len(v) / 2)
|
||||
for i := 0; i < len(v); i += 2 {
|
||||
a.Timestamps[i/2] = int64(v[i].(int))
|
||||
a.Values[i/2] = int64(v[i+1].(int))
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func makeUnsignedArray(v ...interface{}) *tsdb.UnsignedArray {
|
||||
if len(v)&1 == 1 {
|
||||
panic("invalid array length")
|
||||
}
|
||||
a := tsdb.NewUnsignedArrayLen(len(v) / 2)
|
||||
for i := 0; i < len(v); i += 2 {
|
||||
a.Timestamps[i/2] = int64(v[i].(int))
|
||||
a.Values[i/2] = uint64(v[i+1].(int))
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func makeStringArray(v ...interface{}) *tsdb.StringArray {
|
||||
if len(v)&1 == 1 {
|
||||
panic("invalid array length")
|
||||
}
|
||||
a := tsdb.NewStringArrayLen(len(v) / 2)
|
||||
for i := 0; i < len(v); i += 2 {
|
||||
a.Timestamps[i/2] = int64(v[i].(int))
|
||||
a.Values[i/2] = strconv.Itoa(v[i+1].(int))
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func TestBooleanArray_Merge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b, exp *tsdb.BooleanArray
|
||||
}{
|
||||
{
|
||||
name: "empty a",
|
||||
|
||||
a: makeBooleanArray(),
|
||||
b: makeBooleanArray(1, true, 2, true),
|
||||
exp: makeBooleanArray(1, true, 2, true),
|
||||
},
|
||||
{
|
||||
name: "empty b",
|
||||
|
||||
a: makeBooleanArray(1, true, 2, true),
|
||||
b: makeBooleanArray(),
|
||||
exp: makeBooleanArray(1, true, 2, true),
|
||||
},
|
||||
{
|
||||
name: "b replaces a",
|
||||
|
||||
a: makeBooleanArray(1, true),
|
||||
b: makeBooleanArray(
|
||||
0, false,
|
||||
1, false, // overwrites a
|
||||
2, false,
|
||||
3, false,
|
||||
4, false,
|
||||
),
|
||||
exp: makeBooleanArray(0, false, 1, false, 2, false, 3, false, 4, false),
|
||||
},
|
||||
{
|
||||
name: "b replaces partial a",
|
||||
|
||||
a: makeBooleanArray(1, true, 2, true, 3, true, 4, true),
|
||||
b: makeBooleanArray(
|
||||
1, false, // overwrites a
|
||||
2, false, // overwrites a
|
||||
),
|
||||
exp: makeBooleanArray(
|
||||
1, false, // overwrites a
|
||||
2, false, // overwrites a
|
||||
3, true,
|
||||
4, true,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "b replaces all a",
|
||||
|
||||
a: makeBooleanArray(1, true, 2, true, 3, true, 4, true),
|
||||
b: makeBooleanArray(1, false, 2, false, 3, false, 4, false),
|
||||
exp: makeBooleanArray(1, false, 2, false, 3, false, 4, false),
|
||||
},
|
||||
{
|
||||
name: "b replaces a interleaved",
|
||||
a: makeBooleanArray(0, true, 1, true, 2, true, 3, true, 4, true),
|
||||
b: makeBooleanArray(0, false, 2, false, 4, false),
|
||||
exp: makeBooleanArray(0, false, 1, true, 2, false, 3, true, 4, false),
|
||||
},
|
||||
{
|
||||
name: "b merges a interleaved",
|
||||
a: makeBooleanArray(0, true, 2, true, 4, true),
|
||||
b: makeBooleanArray(1, false, 3, false, 5, false),
|
||||
exp: makeBooleanArray(0, true, 1, false, 2, true, 3, false, 4, true, 5, false),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.a.Merge(test.b)
|
||||
if !cmp.Equal(test.a, test.exp) {
|
||||
t.Fatalf("unexpected values -got/+exp\n%s", cmp.Diff(test.a, test.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloatArray_Merge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b, exp *tsdb.FloatArray
|
||||
}{
|
||||
{
|
||||
name: "empty a",
|
||||
|
||||
a: makeFloatArray(),
|
||||
b: makeFloatArray(1, 1.1, 2, 2.1),
|
||||
exp: makeFloatArray(1, 1.1, 2, 2.1),
|
||||
},
|
||||
{
|
||||
name: "empty b",
|
||||
|
||||
a: makeFloatArray(1, 1.0, 2, 2.0),
|
||||
b: makeFloatArray(),
|
||||
exp: makeFloatArray(1, 1.0, 2, 2.0),
|
||||
},
|
||||
{
|
||||
name: "b replaces a",
|
||||
|
||||
a: makeFloatArray(1, 1.0),
|
||||
b: makeFloatArray(
|
||||
0, 0.1,
|
||||
1, 1.1, // overwrites a
|
||||
2, 2.1,
|
||||
3, 3.1,
|
||||
4, 4.1,
|
||||
),
|
||||
exp: makeFloatArray(0, 0.1, 1, 1.1, 2, 2.1, 3, 3.1, 4, 4.1),
|
||||
},
|
||||
{
|
||||
name: "b replaces partial a",
|
||||
|
||||
a: makeFloatArray(1, 1.0, 2, 2.0, 3, 3.0, 4, 4.0),
|
||||
b: makeFloatArray(
|
||||
1, 1.1, // overwrites a
|
||||
2, 2.1, // overwrites a
|
||||
),
|
||||
exp: makeFloatArray(
|
||||
1, 1.1, // overwrites a
|
||||
2, 2.1, // overwrites a
|
||||
3, 3.0,
|
||||
4, 4.0,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "b replaces all a",
|
||||
|
||||
a: makeFloatArray(1, 1.0, 2, 2.0, 3, 3.0, 4, 4.0),
|
||||
b: makeFloatArray(1, 1.1, 2, 2.1, 3, 3.1, 4, 4.1),
|
||||
exp: makeFloatArray(1, 1.1, 2, 2.1, 3, 3.1, 4, 4.1),
|
||||
},
|
||||
{
|
||||
name: "b replaces a interleaved",
|
||||
a: makeFloatArray(0, 0.0, 1, 1.0, 2, 2.0, 3, 3.0, 4, 4.0),
|
||||
b: makeFloatArray(0, 0.1, 2, 2.1, 4, 4.1),
|
||||
exp: makeFloatArray(0, 0.1, 1, 1.0, 2, 2.1, 3, 3.0, 4, 4.1),
|
||||
},
|
||||
{
|
||||
name: "b merges a interleaved",
|
||||
a: makeFloatArray(0, 0.0, 2, 2.0, 4, 4.0),
|
||||
b: makeFloatArray(1, 1.1, 3, 3.1, 5, 5.1),
|
||||
exp: makeFloatArray(0, 0.0, 1, 1.1, 2, 2.0, 3, 3.1, 4, 4.0, 5, 5.1),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.a.Merge(test.b)
|
||||
if !cmp.Equal(test.a, test.exp) {
|
||||
t.Fatalf("unexpected values -got/+exp\n%s", cmp.Diff(test.a, test.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegerArray_Merge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b, exp *tsdb.IntegerArray
|
||||
}{
|
||||
{
|
||||
name: "empty a",
|
||||
|
||||
a: makeIntegerArray(),
|
||||
b: makeIntegerArray(1, 11, 2, 21),
|
||||
exp: makeIntegerArray(1, 11, 2, 21),
|
||||
},
|
||||
{
|
||||
name: "empty b",
|
||||
|
||||
a: makeIntegerArray(1, 10, 2, 20),
|
||||
b: makeIntegerArray(),
|
||||
exp: makeIntegerArray(1, 10, 2, 20),
|
||||
},
|
||||
{
|
||||
name: "b replaces a",
|
||||
|
||||
a: makeIntegerArray(1, 10),
|
||||
b: makeIntegerArray(
|
||||
0, 1,
|
||||
1, 11, // overwrites a
|
||||
2, 21,
|
||||
3, 31,
|
||||
4, 41,
|
||||
),
|
||||
exp: makeIntegerArray(0, 1, 1, 11, 2, 21, 3, 31, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b replaces partial a",
|
||||
|
||||
a: makeIntegerArray(1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeIntegerArray(
|
||||
1, 11, // overwrites a
|
||||
2, 21, // overwrites a
|
||||
),
|
||||
exp: makeIntegerArray(
|
||||
1, 11, // overwrites a
|
||||
2, 21, // overwrites a
|
||||
3, 30,
|
||||
4, 40,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "b replaces all a",
|
||||
|
||||
a: makeIntegerArray(1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeIntegerArray(1, 11, 2, 21, 3, 31, 4, 41),
|
||||
exp: makeIntegerArray(1, 11, 2, 21, 3, 31, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b replaces a interleaved",
|
||||
a: makeIntegerArray(0, 0, 1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeIntegerArray(0, 1, 2, 21, 4, 41),
|
||||
exp: makeIntegerArray(0, 1, 1, 10, 2, 21, 3, 30, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b merges a interleaved",
|
||||
a: makeIntegerArray(0, 00, 2, 20, 4, 40),
|
||||
b: makeIntegerArray(1, 11, 3, 31, 5, 51),
|
||||
exp: makeIntegerArray(0, 00, 1, 11, 2, 20, 3, 31, 4, 40, 5, 51),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.a.Merge(test.b)
|
||||
if !cmp.Equal(test.a, test.exp) {
|
||||
t.Fatalf("unexpected values -got/+exp\n%s", cmp.Diff(test.a, test.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsignedArray_Merge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b, exp *tsdb.UnsignedArray
|
||||
}{
|
||||
{
|
||||
name: "empty a",
|
||||
|
||||
a: makeUnsignedArray(),
|
||||
b: makeUnsignedArray(1, 11, 2, 21),
|
||||
exp: makeUnsignedArray(1, 11, 2, 21),
|
||||
},
|
||||
{
|
||||
name: "empty b",
|
||||
|
||||
a: makeUnsignedArray(1, 10, 2, 20),
|
||||
b: makeUnsignedArray(),
|
||||
exp: makeUnsignedArray(1, 10, 2, 20),
|
||||
},
|
||||
{
|
||||
name: "b replaces a",
|
||||
|
||||
a: makeUnsignedArray(1, 10),
|
||||
b: makeUnsignedArray(
|
||||
0, 1,
|
||||
1, 11, // overwrites a
|
||||
2, 21,
|
||||
3, 31,
|
||||
4, 41,
|
||||
),
|
||||
exp: makeUnsignedArray(0, 1, 1, 11, 2, 21, 3, 31, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b replaces partial a",
|
||||
|
||||
a: makeUnsignedArray(1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeUnsignedArray(
|
||||
1, 11, // overwrites a
|
||||
2, 21, // overwrites a
|
||||
),
|
||||
exp: makeUnsignedArray(
|
||||
1, 11, // overwrites a
|
||||
2, 21, // overwrites a
|
||||
3, 30,
|
||||
4, 40,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "b replaces all a",
|
||||
|
||||
a: makeUnsignedArray(1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeUnsignedArray(1, 11, 2, 21, 3, 31, 4, 41),
|
||||
exp: makeUnsignedArray(1, 11, 2, 21, 3, 31, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b replaces a interleaved",
|
||||
a: makeUnsignedArray(0, 0, 1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeUnsignedArray(0, 1, 2, 21, 4, 41),
|
||||
exp: makeUnsignedArray(0, 1, 1, 10, 2, 21, 3, 30, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b merges a interleaved",
|
||||
a: makeUnsignedArray(0, 00, 2, 20, 4, 40),
|
||||
b: makeUnsignedArray(1, 11, 3, 31, 5, 51),
|
||||
exp: makeUnsignedArray(0, 00, 1, 11, 2, 20, 3, 31, 4, 40, 5, 51),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.a.Merge(test.b)
|
||||
if !cmp.Equal(test.a, test.exp) {
|
||||
t.Fatalf("unexpected values -got/+exp\n%s", cmp.Diff(test.a, test.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringArray_Merge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
a, b, exp *tsdb.StringArray
|
||||
}{
|
||||
{
|
||||
name: "empty a",
|
||||
|
||||
a: makeStringArray(),
|
||||
b: makeStringArray(1, 11, 2, 21),
|
||||
exp: makeStringArray(1, 11, 2, 21),
|
||||
},
|
||||
{
|
||||
name: "empty b",
|
||||
|
||||
a: makeStringArray(1, 10, 2, 20),
|
||||
b: makeStringArray(),
|
||||
exp: makeStringArray(1, 10, 2, 20),
|
||||
},
|
||||
{
|
||||
name: "b replaces a",
|
||||
|
||||
a: makeStringArray(1, 10),
|
||||
b: makeStringArray(
|
||||
0, 1,
|
||||
1, 11, // overwrites a
|
||||
2, 21,
|
||||
3, 31,
|
||||
4, 41,
|
||||
),
|
||||
exp: makeStringArray(0, 1, 1, 11, 2, 21, 3, 31, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b replaces partial a",
|
||||
|
||||
a: makeStringArray(1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeStringArray(
|
||||
1, 11, // overwrites a
|
||||
2, 21, // overwrites a
|
||||
),
|
||||
exp: makeStringArray(
|
||||
1, 11, // overwrites a
|
||||
2, 21, // overwrites a
|
||||
3, 30,
|
||||
4, 40,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "b replaces all a",
|
||||
|
||||
a: makeStringArray(1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeStringArray(1, 11, 2, 21, 3, 31, 4, 41),
|
||||
exp: makeStringArray(1, 11, 2, 21, 3, 31, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b replaces a interleaved",
|
||||
a: makeStringArray(0, 0, 1, 10, 2, 20, 3, 30, 4, 40),
|
||||
b: makeStringArray(0, 1, 2, 21, 4, 41),
|
||||
exp: makeStringArray(0, 1, 1, 10, 2, 21, 3, 30, 4, 41),
|
||||
},
|
||||
{
|
||||
name: "b merges a interleaved",
|
||||
a: makeStringArray(0, 00, 2, 20, 4, 40),
|
||||
b: makeStringArray(1, 11, 3, 31, 5, 51),
|
||||
exp: makeStringArray(0, 00, 1, 11, 2, 20, 3, 31, 4, 40, 5, 51),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.a.Merge(test.b)
|
||||
if !cmp.Equal(test.a, test.exp) {
|
||||
t.Fatalf("unexpected values -got/+exp\n%s", cmp.Diff(test.a, test.exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,3 +3,5 @@ Package tsdb implements a durable time series database.
|
|||
|
||||
*/
|
||||
package tsdb
|
||||
|
||||
//go:generate tmpl -data=@arrayvalues.gen.go.tmpldata arrayvalues.gen.go.tmpl
|
||||
|
|
Loading…
Reference in New Issue