func TestFileStore_Read(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, keyValues{"mem", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) // Search for an entry that exists in the second file values, err := fs.Read("cpu", 1) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := data[1] if got, exp := len(values), len(exp.values); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp.values { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %d", i, got, exp) } } }
func TestFileStore_Stats(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) // Create 3 TSM files... data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, keyValues{"mem", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, } _, err := newFileDir(dir, data...) if err != nil { fatal(t, "creating test files", err) } fs := tsm1.NewFileStore(dir) if err := fs.Open(); err != nil { fatal(t, "opening file store", err) } defer fs.Close() stats := fs.Stats() if got, exp := len(stats), 3; got != exp { t.Fatalf("file count mismatch: got %v, exp %v", got, exp) } }
func TestFileStore_Delete(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu,host=server2!~#!value", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu,host=server1!~#!value", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, keyValues{"mem,host=server1!~#!value", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) keys := fs.Keys() if got, exp := len(keys), 3; got != exp { t.Fatalf("key length mismatch: got %v, exp %v", got, exp) } if err := fs.Delete([]string{"cpu,host=server2!~#!value"}); err != nil { fatal(t, "deleting", err) } keys = fs.Keys() if got, exp := len(keys), 2; got != exp { t.Fatalf("key length mismatch: got %v, exp %v", got, exp) } }
func TestFileStore_SeekToDesc_End(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, 3.0)}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) buf := make(tsm1.FloatValues, 1000) c := fs.KeyCursor("cpu", 2, false) values, err := c.ReadFloatBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := data[2] if got, exp := len(values), len(exp.values); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp.values { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %d", i, got, exp) } } }
// Tests that writing keys in reverse is able to read them back. func TestTSMWriter_Write_SameKey(t *testing.T) { var b bytes.Buffer w, err := tsm1.NewTSMWriter(&b) if err != nil { t.Fatalf("unexpected error creating writer: %v", err) } var data = []struct { key string values []tsm1.Value }{ {"cpu", []tsm1.Value{ tsm1.NewValue(0, 1.0), tsm1.NewValue(1, 2.0)}, }, {"cpu", []tsm1.Value{ tsm1.NewValue(2, 3.0), tsm1.NewValue(3, 4.0)}, }, } for _, d := range data { if err := w.Write(d.key, d.values); err != nil { t.Fatalf("unexpected error writing: %v", err) } } if err := w.WriteIndex(); err != nil { t.Fatalf("unexpected error closing: %v", err) } if err := w.Close(); err != nil { t.Fatalf("unexpected error closing: %v", err) } r, err := tsm1.NewTSMReader(bytes.NewReader(b.Bytes())) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } values := append(data[0].values, data[1].values...) readValues, err := r.ReadAll("cpu") if err != nil { t.Fatalf("unexpected error readin: %v", err) } if exp := len(values); exp != len(readValues) { t.Fatalf("read values length mismatch: got %v, exp %v", len(readValues), exp) } for i, v := range values { if v.Value() != readValues[i].Value() { t.Fatalf("read value mismatch(%d): got %v, exp %d", i, readValues[i].Value(), v.Value()) } } }
// Tests that duplicate point values are merged. There is only one case // where this could happen and that is when a compaction completed and we replace // the old TSM file with a new one and we crash just before deleting the old file. // No data is lost but the same point time/value would exist in two files until // compaction corrects it. func TestTSMKeyIterator_Duplicate(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) v1 := tsm1.NewValue(1, int64(1)) v2 := tsm1.NewValue(1, int64(2)) writes1 := map[string][]tsm1.Value{ "cpu,host=A#!~#value": []tsm1.Value{v1}, } r1 := MustTSMReader(dir, 1, writes1) writes2 := map[string][]tsm1.Value{ "cpu,host=A#!~#value": []tsm1.Value{v2}, } r2 := MustTSMReader(dir, 2, writes2) iter, err := tsm1.NewTSMKeyIterator(1, false, r1, r2) if err != nil { t.Fatalf("unexpected error creating WALKeyIterator: %v", err) } var readValues bool for iter.Next() { key, _, _, block, err := iter.Read() if err != nil { t.Fatalf("unexpected error read: %v", err) } values, err := tsm1.DecodeBlock(block, nil) if err != nil { t.Fatalf("unexpected error decode: %v", err) } if got, exp := key, "cpu,host=A#!~#value"; got != exp { t.Fatalf("key mismatch: got %v, exp %v", got, exp) } if got, exp := len(values), 1; got != exp { t.Fatalf("values length mismatch: got %v, exp %v", got, exp) } readValues = true assertValueEqual(t, values[0], v2) } if !readValues { t.Fatalf("failed to read any values") } }
func TestFileStore_SeekToAsc_Duplicate(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 2.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, 3.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, 4.0)}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) buf := make(tsm1.FloatValues, 1000) c := fs.KeyCursor("cpu", 0, true) // Search for an entry that exists in the second file values, err := c.ReadFloatBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := []tsm1.Value{ data[1].values[0], data[3].values[0], } if got, exp := len(values), len(exp); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %v", i, got, exp) } } // Check that calling Next will dedupe points c.Next() values, err = c.ReadFloatBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp = nil if got, exp := len(values), len(exp); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } }
// Tests that a single TSM file can be read and iterated over func TestTSMKeyIterator_Chunked(t *testing.T) { t.Skip("fixme") dir := MustTempDir() defer os.RemoveAll(dir) v0 := tsm1.NewValue(1, 1.1) v1 := tsm1.NewValue(2, 2.1) writes := map[string][]tsm1.Value{ "cpu,host=A#!~#value": []tsm1.Value{v0, v1}, } r := MustTSMReader(dir, 1, writes) iter, err := tsm1.NewTSMKeyIterator(1, false, r) if err != nil { t.Fatalf("unexpected error creating WALKeyIterator: %v", err) } var readValues bool var chunk int for iter.Next() { key, _, _, block, err := iter.Read() if err != nil { t.Fatalf("unexpected error read: %v", err) } values, err := tsm1.DecodeBlock(block, nil) if err != nil { t.Fatalf("unexpected error decode: %v", err) } if got, exp := key, "cpu,host=A#!~#value"; got != exp { t.Fatalf("key mismatch: got %v, exp %v", got, exp) } if got, exp := len(values), len(writes); got != exp { t.Fatalf("values length mismatch: got %v, exp %v", got, exp) } for _, v := range values { readValues = true assertValueEqual(t, v, writes["cpu,host=A#!~#value"][chunk]) } chunk++ } if !readValues { t.Fatalf("failed to read any values") } }
func TestCacheKeyIterator_Chunked(t *testing.T) { v0 := tsm1.NewValue(1, 1.0) v1 := tsm1.NewValue(2, 2.0) writes := map[string][]tsm1.Value{ "cpu,host=A#!~#value": []tsm1.Value{v0, v1}, } c := tsm1.NewCache(0, "") for k, v := range writes { if err := c.Write(k, v); err != nil { t.Fatalf("failed to write key foo to cache: %s", err.Error()) } } iter := tsm1.NewCacheKeyIterator(c, 1) var readValues bool var chunk int for iter.Next() { key, _, _, block, err := iter.Read() if err != nil { t.Fatalf("unexpected error read: %v", err) } values, err := tsm1.DecodeBlock(block, nil) if err != nil { t.Fatalf("unexpected error decode: %v", err) } if got, exp := key, "cpu,host=A#!~#value"; got != exp { t.Fatalf("key mismatch: got %v, exp %v", got, exp) } if got, exp := len(values), 1; got != exp { t.Fatalf("values length mismatch: got %v, exp %v", got, exp) } for _, v := range values { readValues = true assertValueEqual(t, v, writes["cpu,host=A#!~#value"][chunk]) } chunk++ } if !readValues { t.Fatalf("failed to read any values") } }
func TestEncoding_Count(t *testing.T) { tests := []struct { value interface{} blockType byte }{ {value: float64(1.0), blockType: tsm1.BlockFloat64}, {value: int64(1), blockType: tsm1.BlockInteger}, {value: true, blockType: tsm1.BlockBoolean}, {value: "string", blockType: tsm1.BlockString}, } for _, test := range tests { var values []tsm1.Value values = append(values, tsm1.NewValue(0, test.value)) b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if got, exp := tsm1.BlockCount(b), 1; got != exp { t.Fatalf("block count mismatch: got %v, exp %v", got, exp) } } }
func TestEncoding_BooleanBlock_Basic(t *testing.T) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) values := make([]tsm1.Value, len(times)) for i, t := range times { v := true if i%2 == 0 { v = false } values[i] = tsm1.NewValue(t, v) } b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } var decodedValues []tsm1.Value decodedValues, err = tsm1.DecodeBlock(b, decodedValues) if err != nil { t.Fatalf("unexpected error decoding block: %v", err) } if !reflect.DeepEqual(decodedValues, values) { t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values) } }
func TestEncoding_IntBlock_Basic(t *testing.T) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) values := make([]tsm1.Value, len(times)) for i, t := range times { values[i] = tsm1.NewValue(t, int64(i)) } b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } var decodedValues []tsm1.Value decodedValues, err = tsm1.DecodeBlock(b, decodedValues) if err != nil { t.Fatalf("unexpected error decoding block: %v", err) } if len(decodedValues) != len(values) { t.Fatalf("unexpected results length:\n\tgot: %v\n\texp: %v\n", len(decodedValues), len(values)) } for i := 0; i < len(decodedValues); i++ { if decodedValues[i].UnixNano() != values[i].UnixNano() { t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues[i].UnixNano(), values[i].UnixNano()) } if decodedValues[i].Value() != values[i].Value() { t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues[i].Value(), values[i].Value()) } } }
func TestFileStore_Open_Deleted(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) // Create 3 TSM files... data := []keyValues{ keyValues{"cpu,host=server2!~#!value", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu,host=server1!~#!value", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, keyValues{"mem,host=server1!~#!value", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, } _, err := newFileDir(dir, data...) if err != nil { fatal(t, "creating test files", err) } fs := tsm1.NewFileStore(dir) if err := fs.Open(); err != nil { fatal(t, "opening file store", err) } defer fs.Close() if got, exp := len(fs.Keys()), 3; got != exp { t.Fatalf("file count mismatch: got %v, exp %v", got, exp) } if err := fs.Delete([]string{"cpu,host=server2!~#!value"}); err != nil { fatal(t, "deleting", err) } fs2 := tsm1.NewFileStore(dir) if err := fs2.Open(); err != nil { fatal(t, "opening file store", err) } defer fs2.Close() if got, exp := len(fs2.Keys()), 2; got != exp { t.Fatalf("file count mismatch: got %v, exp %v", got, exp) } }
func TestFileStore_Remove(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) // Create 3 TSM files... data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, keyValues{"mem", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, } files, err := newFileDir(dir, data...) if err != nil { fatal(t, "creating test files", err) } fs := tsm1.NewFileStore(dir) if err := fs.Open(); err != nil { fatal(t, "opening file store", err) } defer fs.Close() if got, exp := fs.Count(), 3; got != exp { t.Fatalf("file count mismatch: got %v, exp %v", got, exp) } if got, exp := fs.CurrentGeneration(), 4; got != exp { t.Fatalf("current ID mismatch: got %v, exp %v", got, exp) } fs.Remove(files[2]) if got, exp := fs.Count(), 2; got != exp { t.Fatalf("file count mismatch: got %v, exp %v", got, exp) } if got, exp := fs.CurrentGeneration(), 4; got != exp { t.Fatalf("current ID mismatch: got %v, exp %v", got, exp) } }
func BenchmarkValues_Deduplicate(b *testing.B) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) values := make([]tsm1.Value, len(times)) for i, t := range times { values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i)) } values = append(values, values...) b.ResetTimer() for i := 0; i < b.N; i++ { tsm1.Values(values).Deduplicate() } }
func TestEncoding_FloatBlock_SimilarFloats(t *testing.T) { values := make([]tsm1.Value, 5) values[0] = tsm1.NewValue(1444238178437870000, 6.00065e+06) values[1] = tsm1.NewValue(1444238185286830000, 6.000656e+06) values[2] = tsm1.NewValue(1444238188441501000, 6.000657e+06) values[3] = tsm1.NewValue(1444238195286811000, 6.000659e+06) values[4] = tsm1.NewValue(1444238198439917000, 6.000661e+06) b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } var decodedValues []tsm1.Value decodedValues, err = tsm1.DecodeBlock(b, decodedValues) if err != nil { t.Fatalf("unexpected error decoding block: %v", err) } if !reflect.DeepEqual(decodedValues, values) { t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values) } }
func TestFileStore_SeekToAsc_Middle(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(1, 1.0), tsm1.NewValue(2, 2.0), tsm1.NewValue(3, 3.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(4, 4.0)}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) // Search for an entry that exists in the second file buf := make(tsm1.FloatValues, 1000) c := fs.KeyCursor("cpu", 3, true) values, err := c.ReadFloatBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := data[0] if got, exp := len(values), len(exp.values); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp.values { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %d", i, got, exp) } } }
// Next returns whether any data remains to be read. It must be called before // the next call to Read(). func (r *Reader) Next() bool { r.valuePos = 0 OUTER: for { if r.currCursor >= len(r.cursors) { // All cursors drained. No more data remains. return false } cc := r.cursors[r.currCursor] r.keyBuf = tsm1.SeriesFieldKey(cc.series, cc.field) for { k, v := cc.Next() if k == -1 { // Go to next cursor and try again. r.currCursor++ if r.valuePos == 0 { // The previous cursor had no data. Instead of returning // just go immediately to the next cursor. continue OUTER } // There is some data available. Indicate that it should be read. return true } if f, ok := v.(float64); ok { if math.IsInf(f, 0) { r.stats.AddPointsRead(1) r.stats.IncrInf() continue } if math.IsNaN(f) { r.stats.AddPointsRead(1) r.stats.IncrNaN() continue } } r.values[r.valuePos] = tsm1.NewValue(k, v) r.valuePos++ if r.valuePos >= len(r.values) { return true } } } }
func TestCacheRace(t *testing.T) { values := make(tsm1.Values, 1000) timestamps := make([]int64, len(values)) series := make([]string, 100) for i := range timestamps { timestamps[i] = int64(rand.Int63n(int64(len(values)))) } for i := range values { values[i] = tsm1.NewValue(timestamps[i*len(timestamps)/len(values)], float64(i)) } for i := range series { series[i] = fmt.Sprintf("series%d", i) } wg := sync.WaitGroup{} c := tsm1.NewCache(1000000, "") ch := make(chan struct{}) for _, s := range series { for _, v := range values { c.Write(s, tsm1.Values{v}) } wg.Add(1) go func(s string) { defer wg.Done() <-ch c.Values(s) }(s) } wg.Add(1) go func() { wg.Done() <-ch s, err := c.Snapshot() if err == tsm1.ErrSnapshotInProgress { return } if err != nil { t.Fatalf("failed to snapshot cache: %v", err) } s.Deduplicate() c.ClearSnapshot(true) }() close(ch) wg.Wait() }
func TestTSMWriter_Write_Single(t *testing.T) { var b bytes.Buffer w, err := tsm1.NewTSMWriter(&b) if err != nil { t.Fatalf("unexpected error creating writer: %v", err) } values := []tsm1.Value{tsm1.NewValue(0, 1.0)} if err := w.Write("cpu", values); err != nil { t.Fatalf("unexpected error writing: %v", err) } if err := w.WriteIndex(); err != nil { t.Fatalf("unexpected error writing index: %v", err) } if err := w.Close(); err != nil { t.Fatalf("unexpected error closing: %v", err) } if got, exp := len(b.Bytes()), 5; got < exp { t.Fatalf("file size mismatch: got %v, exp %v", got, exp) } if got := binary.BigEndian.Uint32(b.Bytes()[0:4]); got != tsm1.MagicNumber { t.Fatalf("magic number mismatch: got %v, exp %v", got, tsm1.MagicNumber) } r, err := tsm1.NewTSMReader(bytes.NewReader(b.Bytes())) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } readValues, err := r.ReadAll("cpu") if err != nil { t.Fatalf("unexpected error readin: %v", err) } if len(readValues) != len(values) { t.Fatalf("read values length mismatch: got %v, exp %v", len(readValues), len(values)) } for i, v := range values { if v.Value() != readValues[i].Value() { t.Fatalf("read value mismatch(%d): got %v, exp %d", i, readValues[i].Value(), v.Value()) } } }
func TestCheckConcurrentReadsAreSafe(t *testing.T) { values := make(tsm1.Values, 1000) timestamps := make([]int64, len(values)) series := make([]string, 100) for i := range timestamps { timestamps[i] = int64(rand.Int63n(int64(len(values)))) } for i := range values { values[i] = tsm1.NewValue(timestamps[i*len(timestamps)/len(values)], float64(i)) } for i := range series { series[i] = fmt.Sprintf("series%d", i) } wg := sync.WaitGroup{} c := tsm1.NewCache(1000000, "") ch := make(chan struct{}) for _, s := range series { for _, v := range values { c.Write(s, tsm1.Values{v}) } wg.Add(3) go func(s string) { defer wg.Done() <-ch c.Values(s) }(s) go func(s string) { defer wg.Done() <-ch c.Values(s) }(s) go func(s string) { defer wg.Done() <-ch c.Values(s) }(s) } close(ch) wg.Wait() }
func TestTSMWriter_Write_MaxKey(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) f := MustTempFile(dir) defer f.Close() w, err := tsm1.NewTSMWriter(f) if err != nil { t.Fatalf("unexpected error created writer: %v", err) } var key string for i := 0; i < 100000; i++ { key += "a" } if err := w.Write(key, []tsm1.Value{tsm1.NewValue(0, 1.0)}); err != tsm1.ErrMaxKeyLengthExceeded { t.Fatalf("expected max key length error writing key: %v", err) } }
func TestEncoding_FloatBlock_ZeroTime(t *testing.T) { values := make([]tsm1.Value, 3) for i := 0; i < 3; i++ { values[i] = tsm1.NewValue(0, float64(i)) } b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } var decodedValues []tsm1.Value decodedValues, err = tsm1.DecodeBlock(b, decodedValues) if err != nil { t.Fatalf("unexpected error decoding block: %v", err) } if !reflect.DeepEqual(decodedValues, values) { t.Fatalf("unexpected results:\n\tgot: %v\n\texp: %v\n", decodedValues, values) } }
func BenchmarkDecodeBlock_Boolean_EqualSize(b *testing.B) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) values := make([]tsm1.Value, len(times)) for i, t := range times { values[i] = tsm1.NewValue(t, true) } bytes, err := tsm1.Values(values).Encode(nil) if err != nil { b.Fatalf("unexpected error: %v", err) } decodedValues := make([]tsm1.Value, len(values)) b.ResetTimer() for i := 0; i < b.N; i++ { _, err = tsm1.DecodeBlock(bytes, decodedValues) if err != nil { b.Fatalf("unexpected error decoding block: %v", err) } } }
func BenchmarkDecodeBlock_String_TypeSpecific(b *testing.B) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) values := make([]tsm1.Value, len(times)) for i, t := range times { values[i] = tsm1.NewValue(t, fmt.Sprintf("value %d", i)) } bytes, err := tsm1.Values(values).Encode(nil) if err != nil { b.Fatalf("unexpected error: %v", err) } decodedValues := make([]tsm1.StringValue, len(values)) b.ResetTimer() for i := 0; i < b.N; i++ { _, err = tsm1.DecodeStringBlock(bytes, decodedValues) if err != nil { b.Fatalf("unexpected error decoding block: %v", err) } } }
func TestEncoding_FloatBlock(t *testing.T) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) values := make([]tsm1.Value, len(times)) for i, t := range times { values[i] = tsm1.NewValue(t, float64(i)) } b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } var decodedValues []tsm1.Value decodedValues, err = tsm1.DecodeBlock(b, decodedValues) if err != nil { t.Fatalf("unexpected error decoding block: %v", err) } if !reflect.DeepEqual(decodedValues, values) { t.Fatalf("unexpected results:\n\tgot: %s\n\texp: %s\n", spew.Sdump(decodedValues), spew.Sdump(values)) } }
func TestFileStore_SeekToDesc_AfterEnd_OverlapString(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(8, "eight"), tsm1.NewValue(9, "nine")}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, "two")}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(3, "three")}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(3, "four"), tsm1.NewValue(7, "seven")}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) buf := make(tsm1.StringValues, 1000) c := fs.KeyCursor("cpu", 8, false) values, err := c.ReadStringBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := []tsm1.Value{ data[1].values[0], data[3].values[0], data[3].values[1], data[0].values[0], data[0].values[1], } if got, exp := len(values), len(exp); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %v", i, got, exp) } } }
// Tests that seeking and reading all blocks that contain overlapping points does // not skip any blocks. func TestFileStore_SeekToAsc_BeforeStart_OverlapString(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, "zero"), tsm1.NewValue(1, "one")}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, "two")}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(3, "three")}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, "four"), tsm1.NewValue(7, "seven")}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) // Search for an entry that exists in the second file buf := make(tsm1.StringValues, 1000) c := fs.KeyCursor("cpu", 0, true) values, err := c.ReadStringBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := []tsm1.Value{ data[3].values[0], data[0].values[1], data[1].values[0], data[2].values[0], data[3].values[1], } if got, exp := len(values), len(exp); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %v", i, got, exp) } } }
func TestFileStore_SeekToDesc_AfterEnd_OverlapInteger(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(8, int64(0)), tsm1.NewValue(9, int64(1))}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, int64(2))}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(3, int64(3))}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(3, int64(4)), tsm1.NewValue(7, int64(7))}}, } files, err := newFiles(data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) buf := make(tsm1.IntegerValues, 1000) c := fs.KeyCursor("cpu", 8, false) values, err := c.ReadIntegerBlock(buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := []tsm1.Value{ data[1].values[0], data[3].values[0], data[3].values[1], data[0].values[0], data[0].values[1], } if got, exp := len(values), len(exp); got != exp { t.Fatalf("value length mismatch: got %v, exp %v", got, exp) } for i, v := range exp { if got, exp := values[i].Value(), v.Value(); got != exp { t.Fatalf("read value mismatch(%d): got %v, exp %v", i, got, exp) } } }
func TestTSMWriter_WriteBlock_Multiple(t *testing.T) { // Write a new TSM file var b bytes.Buffer w, err := tsm1.NewTSMWriter(&b) if err != nil { t.Fatalf("unexpected error creating writer: %v", err) } var data = []struct { key string values []tsm1.Value }{ {"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, {"mem", []tsm1.Value{tsm1.NewValue(1, 2.0)}}, } for _, d := range data { if err := w.Write(d.key, d.values); err != nil { t.Fatalf("unexpected error writing: %v", err) } } if err := w.WriteIndex(); err != nil { t.Fatalf("unexpected error closing: %v", err) } if err := w.Close(); err != nil { t.Fatalf("unexpected error closing: %v", err) } if got, exp := len(b.Bytes()), 5; got < exp { t.Fatalf("file size mismatch: got %v, exp %v", got, exp) } if got := binary.BigEndian.Uint32(b.Bytes()[0:4]); got != tsm1.MagicNumber { t.Fatalf("magic number mismatch: got %v, exp %v", got, tsm1.MagicNumber) } // Create reader for that file r, err := tsm1.NewTSMReader(bytes.NewReader(b.Bytes())) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } // Using the reader, write a new file using WriteBlocks b.Reset() w, err = tsm1.NewTSMWriter(&b) if err != nil { t.Fatalf("unexpected error creating writer: %v", err) } iter := r.BlockIterator() for iter.Next() { key, minTime, maxTime, b, err := iter.Read() if err != nil { t.Fatalf("unexpected error reading block: %v", err) } if err := w.WriteBlock(key, minTime, maxTime, b); err != nil { t.Fatalf("unexpected error writing block: %v", err) } } if err := w.WriteIndex(); err != nil { t.Fatalf("unexpected error closing: %v", err) } if err := w.Close(); err != nil { t.Fatalf("unexpected error closing: %v", err) } // Now create a reader to verify the written blocks matches the originally // written file using Write r, err = tsm1.NewTSMReader(bytes.NewReader(b.Bytes())) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } for _, d := range data { readValues, err := r.ReadAll(d.key) if err != nil { t.Fatalf("unexpected error readin: %v", err) } if exp := len(d.values); exp != len(readValues) { t.Fatalf("read values length mismatch: got %v, exp %v", len(readValues), exp) } for i, v := range d.values { if v.Value() != readValues[i].Value() { t.Fatalf("read value mismatch(%d): got %v, exp %d", i, readValues[i].Value(), v.Value()) } } } }