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_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.FloatValue, 1000) c := fs.KeyCursor("cpu", 2, false) values, err := c.ReadFloatBlock(tsm1.NewTimeDecoder(), &tsm1.FloatDecoder{}, &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) } } }
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_Delete(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) fs := tsm1.NewFileStore(dir) // 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(dir, 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 TestScrubValues(t *testing.T) { dummy := Converter{ tracker: new(tracker), } epoch := time.Unix(0, 0) simple := []tsm1.Value{tsm1.NewValue(epoch, 1.0)} for _, tt := range []struct { input, expected []tsm1.Value }{ { input: simple, expected: simple, }, { input: []tsm1.Value{simple[0], tsm1.NewValue(epoch, math.NaN())}, expected: simple, }, { input: []tsm1.Value{simple[0], tsm1.NewValue(epoch, math.Inf(-1))}, expected: simple, }, { input: []tsm1.Value{simple[0], tsm1.NewValue(epoch, math.Inf(1)), tsm1.NewValue(epoch, math.NaN())}, expected: simple, }, } { out := dummy.scrubValues(tt.input) if !reflect.DeepEqual(out, tt.expected) { t.Errorf("Failed to scrub '%s': Got '%s', Expected '%s'", pretty(tt.input), pretty(out), pretty(tt.expected)) } } }
func TestFileStore_SeekToDesc_FromStart(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...) // Search for an entry that exists in the second file buf := make(tsm1.FloatValues, 1000) c := fs.KeyCursor("cpu", 0, false) 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) } } }
func TestWriteWALSegment_UnmarshalBinary_WriteWALCorrupt(t *testing.T) { p1 := tsm1.NewValue(1, 1.1) p2 := tsm1.NewValue(1, int64(1)) p3 := tsm1.NewValue(1, true) p4 := tsm1.NewValue(1, "string") values := map[string][]tsm1.Value{ "cpu,host=A#!~#float": []tsm1.Value{p1, p1}, "cpu,host=A#!~#int": []tsm1.Value{p2, p2}, "cpu,host=A#!~#bool": []tsm1.Value{p3, p3}, "cpu,host=A#!~#string": []tsm1.Value{p4, p4}, } w := &tsm1.WriteWALEntry{ Values: values, } b, err := w.MarshalBinary() if err != nil { t.Fatalf("unexpected error, got %v", err) } // Test every possible truncation of a write WAL entry for i := 0; i < len(b); i++ { // re-allocated to ensure capacity would be exceed if slicing truncated := make([]byte, i) copy(truncated, b[:i]) err := w.UnmarshalBinary(truncated) if err != nil && err != tsm1.ErrWALCorrupt { t.Fatalf("unexpected error: %v", err) } } }
func TestFileStore_Open(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) // Create 3 TSM files... data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(time.Unix(0, 0), 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(time.Unix(1, 0), 2.0)}}, keyValues{"mem", []tsm1.Value{tsm1.NewValue(time.Unix(0, 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 := 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) } }
func TestTSMWriter_Write_Multiple(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) f := MustTempFile(dir) w, err := tsm1.NewTSMWriter(f) 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) } fd, err := os.Open(f.Name()) if err != nil { t.Fatalf("unexpected error open file: %v", err) } r, err := tsm1.NewTSMReader(fd) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } defer r.Close() 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()) } } } }
func TestFileStore_SeekToDesc_Duplicate(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) fs := tsm1.NewFileStore(dir) // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 4.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(0, 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, 2.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(2, 3.0)}}, } files, err := newFiles(dir, 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.FloatValue, 1000) c := fs.KeyCursor("cpu", 2, false) values, err := c.ReadFloatBlock(&tsm1.TimeDecoder{}, &tsm1.FloatDecoder{}, &buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp := []tsm1.Value{ 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) } } c.Next() values, err = c.ReadFloatBlock(&tsm1.TimeDecoder{}, &tsm1.FloatDecoder{}, &buf) if err != nil { t.Fatalf("unexpected error reading values: %v", err) } exp = []tsm1.Value{ data[1].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) } } }
func TestTSMReader_MMAP_TombstoneMultipleRanges(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 creating writer: %v", err) } expValues := []tsm1.Value{ tsm1.NewValue(1, 1.0), tsm1.NewValue(2, 2.0), tsm1.NewValue(3, 3.0), tsm1.NewValue(4, 4.0), tsm1.NewValue(5, 5.0), } if err := w.Write("cpu", expValues); 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) } f, err = os.Open(f.Name()) if err != nil { t.Fatalf("unexpected error open file: %v", err) } r, err := tsm1.NewTSMReader(f) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } defer r.Close() if err := r.DeleteRange([]string{"cpu"}, 2, 2); err != nil { t.Fatalf("unexpected error deleting: %v", err) } if err := r.DeleteRange([]string{"cpu"}, 4, 4); err != nil { t.Fatalf("unexpected error deleting: %v", err) } values, err := r.ReadAll("cpu") if err != nil { t.Fatalf("unexpected error reading all: %v", err) } if got, exp := len(values), 3; got != exp { t.Fatalf("values length mismatch: got %v, exp %v", got, exp) } }
func TestWALWriter_WritePoints_Single(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) f := MustTempFile(dir) w := tsm1.NewWALSegmentWriter(f) p1 := tsm1.NewValue(time.Unix(1, 0), 1.1) p2 := tsm1.NewValue(time.Unix(1, 0), int64(1)) p3 := tsm1.NewValue(time.Unix(1, 0), true) p4 := tsm1.NewValue(time.Unix(1, 0), "string") values := map[string][]tsm1.Value{ "cpu,host=A#!~#float": []tsm1.Value{p1}, "cpu,host=A#!~#int": []tsm1.Value{p2}, "cpu,host=A#!~#bool": []tsm1.Value{p3}, "cpu,host=A#!~#string": []tsm1.Value{p4}, } entry := &tsm1.WriteWALEntry{ Values: values, } if err := w.Write(mustMarshalEntry(entry)); err != nil { fatal(t, "write points", err) } if _, err := f.Seek(0, os.SEEK_SET); err != nil { fatal(t, "seek", err) } r := tsm1.NewWALSegmentReader(f) if !r.Next() { t.Fatalf("expected next, got false") } we, err := r.Read() if err != nil { fatal(t, "read entry", err) } e, ok := we.(*tsm1.WriteWALEntry) if !ok { t.Fatalf("expected WriteWALEntry: got %#v", e) } for k, v := range e.Values { for i, vv := range v { if got, exp := vv.String(), values[k][i].String(); got != exp { t.Fatalf("points mismatch: got %v, exp %v", got, exp) } } } if n := r.Count(); n != MustReadFileSize(f) { t.Fatalf("wrong count of bytes read, got %d, exp %d", n, MustReadFileSize(f)) } }
// Tests that a single TSM file can be read and iterated over func TestTSMKeyIterator_Chunked(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) v0 := tsm1.NewValue(1, 1.1) writes := map[string][]tsm1.Value{ "cpu,host=A#!~#value": []tsm1.Value{v0}, } r1 := MustTSMReader(dir, 1, writes) v1 := tsm1.NewValue(2, 2.1) writes1 := map[string][]tsm1.Value{ "cpu,host=A#!~#value": []tsm1.Value{v1}, } r2 := MustTSMReader(dir, 2, writes1) iter, err := tsm1.NewTSMKeyIterator(2, false, r1, r2) 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), 2; got != exp { t.Fatalf("values length mismatch: got %v, exp %v", got, exp) } readValues = len(values) > 0 assertValueEqual(t, values[0], v0) assertValueEqual(t, values[1], v1) chunk++ } if got, exp := chunk, 1; got != exp { t.Fatalf("chunk count mismatch: got %v, exp %v", got, exp) } if !readValues { t.Fatalf("failed to read any values") } }
// 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()) } } }
func TestFileStore_SeekToAsc_Duplicate(t *testing.T) { fs := tsm1.NewFileStore("") // Setup 3 files data := []keyValues{ keyValues{"cpu", []tsm1.Value{tsm1.NewValue(time.Unix(0, 0), 1.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(time.Unix(0, 0), 2.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(time.Unix(2, 0), 3.0)}}, keyValues{"cpu", []tsm1.Value{tsm1.NewValue(time.Unix(2, 0), 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", time.Unix(0, 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 := 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 %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 = data[3] 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 %v", i, got, exp) } } }
func TestFileStore_CreateSnapshot(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) fs := tsm1.NewFileStore(dir) // 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(dir, data...) if err != nil { t.Fatalf("unexpected error creating files: %v", err) } fs.Add(files...) // Create a tombstone if err := fs.DeleteRange([]string{"cpu"}, 1, 1); err != nil { t.Fatalf("unexpected error delete range: %v", err) } s, e := fs.CreateSnapshot() if e != nil { t.Fatal(e) } t.Logf("temp file for hard links: %q", s) tfs, e := ioutil.ReadDir(s) if e != nil { t.Fatal(e) } if len(tfs) == 0 { t.Fatal("no files found") } for _, f := range fs.Files() { p := filepath.Join(s, filepath.Base(f.Path())) t.Logf("checking for existence of hard link %q", p) if _, err := os.Stat(p); os.IsNotExist(err) { t.Fatalf("unable to find file %q", p) } for _, tf := range f.TombstoneFiles() { p := filepath.Join(s, filepath.Base(tf.Path)) t.Logf("checking for existence of hard link %q", p) if _, err := os.Stat(p); os.IsNotExist(err) { t.Fatalf("unable to find file %q", p) } } } }
// 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") } }
// 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 TestIndirectIndex_UnmarshalBinary_BlockCountOverflow(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 creating writer: %v", err) } for i := 0; i < 3280; i++ { w.Write("cpu", []tsm1.Value{tsm1.NewValue(int64(i), float64(i))}) } 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) } f, err = os.Open(f.Name()) if err != nil { t.Fatalf("unexpected error open file: %v", err) } r, err := tsm1.NewTSMReader(f) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } defer r.Close() }
func TestEncoding_BlockType(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(time.Unix(0, 0), test.value)) b, err := tsm1.Values(values).Encode(nil) if err != nil { t.Fatalf("unexpected error: %v", err) } bt, err := tsm1.BlockType(b) if err != nil { t.Fatalf("unexpected error decoding block type: %v", err) } if got, exp := bt, test.blockType; got != exp { t.Fatalf("block type mismatch: got %v, exp %v", got, exp) } } _, err := tsm1.BlockType([]byte{10}) if err == nil { t.Fatalf("expected error decoding block type, got nil") } }
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 TestTSMReader_Type(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(time.Unix(0, 0), int64(1))} 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 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) } typ, err := r.Type("cpu") if err != nil { fatal(t, "reading type", err) } if got, exp := typ, tsm1.BlockInteger; got != exp { t.Fatalf("type 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_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 BenchmarkValues_Merge(b *testing.B) { valueCount := 1000 times := getTimes(valueCount, 60, time.Second) a := make([]tsm1.Value, len(times)) c := make([]tsm1.Value, len(times)) for i, t := range times { a[i] = tsm1.NewValue(t, float64(i)) c[i] = tsm1.NewValue(t+1, float64(i)) } b.ResetTimer() for i := 0; i < b.N; i++ { tsm1.Values(a).Merge(c) } }
func TestTSMReader_MMAP_Tombstone(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 creating writer: %v", err) } values := []tsm1.Value{tsm1.NewValue(time.Unix(0, 0), 1.0)} if err := w.Write("cpu", values); err != nil { t.Fatalf("unexpected error writing: %v", err) } if err := w.Write("mem", 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) } f, err = os.Open(f.Name()) if err != nil { t.Fatalf("unexpected error open file: %v", err) } r, err := tsm1.NewTSMReaderWithOptions( tsm1.TSMReaderOptions{ MMAPFile: f, }) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } if err := r.Delete([]string{"mem"}); err != nil { t.Fatalf("unexpected error deleting: %v", err) } r, err = tsm1.NewTSMReaderWithOptions( tsm1.TSMReaderOptions{ MMAPFile: f, }) if err != nil { t.Fatalf("unexpected error created reader: %v", err) } defer r.Close() if got, exp := len(r.Keys()), 1; got != exp { t.Fatalf("key length mismatch: got %v, exp %v", got, exp) } }
func TestBlockIterator_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(time.Unix(0, 0), int64(1))} 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 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) } var count int iter := r.BlockIterator() for iter.Next() { key, minTime, maxTime, buf, err := iter.Read() if err != nil { t.Fatalf("unexpected error creating iterator: %v", err) } if got, exp := key, "cpu"; got != exp { t.Fatalf("key mismatch: got %v, exp %v", got, exp) } if got, exp := minTime, time.Unix(0, 0); got != exp { t.Fatalf("min time mismatch: got %v, exp %v", got, exp) } if got, exp := maxTime, time.Unix(0, 0); got != exp { t.Fatalf("max time mismatch: got %v, exp %v", got, exp) } if len(buf) == 0 { t.Fatalf("buf length = 0") } count++ } if got, exp := count, len(values); got != exp { t.Fatalf("value count mismatch: got %v, exp %v", got, exp) } }
func TestWALWriter_WritePoints_LargeBatch(t *testing.T) { dir := MustTempDir() defer os.RemoveAll(dir) f := MustTempFile(dir) w := tsm1.NewWALSegmentWriter(f) var points []tsm1.Value for i := 0; i < 100000; i++ { points = append(points, tsm1.NewValue(time.Unix(int64(i), 0), int64(1))) } values := map[string][]tsm1.Value{ "cpu,host=A,server=01,foo=bar,tag=really-long#!~#float": points, "mem,host=A,server=01,foo=bar,tag=really-long#!~#float": points, } entry := &tsm1.WriteWALEntry{ Values: values, } if err := w.Write(mustMarshalEntry(entry)); err != nil { fatal(t, "write points", err) } if _, err := f.Seek(0, os.SEEK_SET); err != nil { fatal(t, "seek", err) } r := tsm1.NewWALSegmentReader(f) if !r.Next() { t.Fatalf("expected next, got false") } we, err := r.Read() if err != nil { fatal(t, "read entry", err) } e, ok := we.(*tsm1.WriteWALEntry) if !ok { t.Fatalf("expected WriteWALEntry: got %#v", e) } for k, v := range e.Values { for i, vv := range v { if got, exp := vv.String(), values[k][i].String(); got != exp { t.Fatalf("points mismatch: got %v, exp %v", got, exp) } } } if n := r.Count(); n != MustReadFileSize(f) { t.Fatalf("wrong count of bytes read, got %d, exp %d", n, MustReadFileSize(f)) } }
func BenchmarkValues_EncodeBool(b *testing.B) { valueCount := 1024 times := getTimes(valueCount, 60, time.Second) a := make([]tsm1.Value, len(times)) for i, t := range times { if i%2 == 0 { a[i] = tsm1.NewValue(t, true) } else { a[i] = tsm1.NewValue(t, false) } } buf := make([]byte, 1024*8) b.ResetTimer() for i := 0; i < b.N; i++ { tsm1.Values(a).Encode(buf) } }