// This is copied from storage/metric/helpers_test.go, which is unfortunate but // presently required to make things work. func NewTestTieredStorage(t test.Tester) (storage *tiered.TieredStorage, closer test.Closer) { var directory test.TemporaryDirectory directory = test.NewTemporaryDirectory("test_tiered_storage", t) storage, err := tiered.NewTieredStorage(2500, 1000, 5*time.Second, 0*time.Second, directory.Path()) if err != nil { if storage != nil { storage.Close() } directory.Close() t.Fatalf("Error creating storage: %s", err) } if storage == nil { directory.Close() t.Fatalf("storage == nil") } started := make(chan bool) go storage.Serve(started) <-started closer = &testTieredStorageCloser{ storage: storage, directory: directory, } return }
func AppendSampleAsPureSparseAppendTests(p MetricPersistence, t test.Tester) { appendSample := func(x int) (success bool) { v := model.SampleValue(x) ts := time.Unix(int64(x), int64(x)) labelName := model.LabelName(x) labelValue := model.LabelValue(x) l := model.Metric{labelName: labelValue} sample := model.Sample{ Value: v, Timestamp: ts, Metric: l, } err := p.AppendSample(sample) success = err == nil if !success { t.Error(err) } return } if err := quick.Check(appendSample, nil); err != nil { t.Error(err) } }
func testLabelValues(t test.Tester) { var scenarios = []struct { in LabelValues out LabelValues }{ { in: LabelValues{"ZZZ", "zzz"}, out: LabelValues{"ZZZ", "zzz"}, }, { in: LabelValues{"aaa", "AAA"}, out: LabelValues{"AAA", "aaa"}, }, } for i, scenario := range scenarios { sort.Sort(scenario.in) for j, expected := range scenario.out { if expected != scenario.in[j] { t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) } } } }
func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) { metric := model.Metric{ model.MetricNameLabel: "errors_total", "controller": "foo", "operation": "bar", } increments := 10 repetitions := 500 for i := 0; i < increments; i++ { for j := 0; j < repetitions; j++ { time := time.Time{}.Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) testAppendSample(p, model.Sample{ Value: model.SampleValue(i), Timestamp: time, Metric: metric, }, t) } } if true { // XXX: Purely a benchmark. return } labelSet := model.LabelSet{ model.MetricNameLabel: "errors_total", "controller": "foo", "operation": "bar", } for i := 0; i < increments; i++ { for j := 0; j < repetitions; j++ { fingerprints, err := p.GetFingerprintsForLabelSet(labelSet) if err != nil { t.Fatal(err) } if len(fingerprints) != 1 { t.Fatalf("expected %d fingerprints, got %d", 1, len(fingerprints)) } time := time.Time{}.Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) sample, err := p.GetValueAtTime(fingerprints[0], time, StalenessPolicy{}) if err != nil { t.Fatal(err) } if sample == nil { t.Fatal("expected non-nil sample.") } expected := model.SampleValue(i) if sample.Value != expected { t.Fatalf("expected %d value, got %d", expected, sample.Value) } } } }
func newTestStorage(t test.Tester) (storage *tiered.TieredStorage, closer test.Closer) { storage, closer = NewTestTieredStorage(t) if storage == nil { t.Fatal("storage == nil") } storeMatrix(storage, testMatrix) return }
func buildLevelDBTestPersistencesMaker(name string, t test.Tester) func() (MetricPersistence, test.Closer) { return func() (MetricPersistence, test.Closer) { temporaryDirectory := test.NewTemporaryDirectory("get_value_at_time", t) p, err := NewLevelDBMetricPersistence(temporaryDirectory.Path()) if err != nil { t.Errorf("Could not start up LevelDB: %q\n", err) } return p, temporaryDirectory } }
func testLabelPairs(t test.Tester) { var scenarios = []struct { in LabelPairs out LabelPairs }{ { in: LabelPairs{ { Name: "AAA", Value: "aaa", }, }, out: LabelPairs{ { Name: "AAA", Value: "aaa", }, }, }, { in: LabelPairs{ { Name: "aaa", Value: "aaa", }, { Name: "ZZZ", Value: "aaa", }, }, out: LabelPairs{ { Name: "ZZZ", Value: "aaa", }, { Name: "aaa", Value: "aaa", }, }, }, } for i, scenario := range scenarios { sort.Sort(scenario.in) for j, expected := range scenario.out { if !expected.Equal(scenario.in[j]) { t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) } } } }
func testLevelDBStochastic(t test.Tester) { persistenceMaker := func() (MetricPersistence, test.Closer) { temporaryDirectory := test.NewTemporaryDirectory("test_leveldb_stochastic", t) p, err := NewLevelDBMetricPersistence(temporaryDirectory.Path()) if err != nil { t.Errorf("Could not start up LevelDB: %q\n", err) } return p, temporaryDirectory } StochasticTests(persistenceMaker, t) }
func testMetric(t test.Tester) { var scenarios = []struct { input map[string]string hash uint64 rowkey string }{ { input: map[string]string{}, rowkey: "02676020557754725067--0-", hash: 2676020557754725067, }, { input: map[string]string{ "first_name": "electro", "occupation": "robot", "manufacturer": "westinghouse", }, rowkey: "04776841610193542734-f-56-t", hash: 4776841610193542734, }, { input: map[string]string{ "x": "y", }, rowkey: "01306929544689993150-x-2-y", hash: 1306929544689993150, }, } for i, scenario := range scenarios { metric := Metric{} for key, value := range scenario.input { metric[LabelName(key)] = LabelValue(value) } expectedRowKey := scenario.rowkey expectedHash := scenario.hash fingerprint := NewFingerprintFromMetric(metric) actualRowKey := fingerprint.ToRowKey() actualHash := fingerprint.Hash() if expectedRowKey != actualRowKey { t.Errorf("%d. expected %s, got %s", i, expectedRowKey, actualRowKey) } if actualHash != expectedHash { t.Errorf("%d. expected %d, got %d", i, expectedHash, actualHash) } } }
func AppendRepeatingValuesTests(p metric.Persistence, t test.Tester) { m := clientmodel.Metric{ clientmodel.MetricNameLabel: "errors_total", "controller": "foo", "operation": "bar", } increments := 10 repetitions := 500 for i := 0; i < increments; i++ { for j := 0; j < repetitions; j++ { time := clientmodel.Timestamp(0).Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) testAppendSamples(p, &clientmodel.Sample{ Value: clientmodel.SampleValue(i), Timestamp: time, Metric: m, }, t) } } v, ok := p.(metric.View) if !ok { // It's purely a benchmark for a Persistence that is not viewable. return } matchers := labelMatchersFromLabelSet(clientmodel.LabelSet{ clientmodel.MetricNameLabel: "errors_total", "controller": "foo", "operation": "bar", }) for i := 0; i < increments; i++ { for j := 0; j < repetitions; j++ { fingerprints, err := p.GetFingerprintsForLabelMatchers(matchers) if err != nil { t.Fatal(err) } if len(fingerprints) != 1 { t.Fatalf("expected %d fingerprints, got %d", 1, len(fingerprints)) } time := clientmodel.Timestamp(0).Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) samples := v.GetValueAtTime(fingerprints[0], time) if len(samples) == 0 { t.Fatal("expected at least one sample.") } expected := clientmodel.SampleValue(i) for _, sample := range samples { if sample.Value != expected { t.Fatalf("expected %v value, got %v", expected, sample.Value) } } } } }
func AppendSampleAsPureSingleEntityAppendTests(p metric.Persistence, t test.Tester) { appendSample := func(x int) bool { sample := &clientmodel.Sample{ Value: clientmodel.SampleValue(x), Timestamp: clientmodel.TimestampFromUnix(int64(x)), Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "my_metric"}, } err := p.AppendSamples(clientmodel.Samples{sample}) return err == nil } if err := quick.Check(appendSample, nil); err != nil { t.Error(err) } }
func AppendSampleAsPureSingleEntityAppendTests(p MetricPersistence, t test.Tester) { appendSample := func(x int) bool { sample := model.Sample{ Value: model.SampleValue(x), Timestamp: time.Unix(int64(x), 0), Metric: model.Metric{model.MetricNameLabel: "my_metric"}, } err := p.AppendSample(sample) return err == nil } if err := quick.Check(appendSample, nil); err != nil { t.Error(err) } }
func newTestTieredStorage(t test.Tester) (storage Storage, closer test.Closer) { var directory test.TemporaryDirectory directory = test.NewTemporaryDirectory("test_tiered_storage", t) storage, err := NewTieredStorage(5000000, 2500, 1000, 5*time.Second, 15*time.Second, 0*time.Second, directory.Path()) if err != nil { t.Fatalf("Error creating storage: %s", err) } if storage == nil { t.Fatalf("storage == nil") } go storage.Serve() closer = &testTieredStorageCloser{ storage: storage, directory: directory, } return }
func GetFingerprintsForLabelSetUsesAndForLabelMatchingTests(p metric.Persistence, t test.Tester) { metrics := []clientmodel.LabelSet{ {clientmodel.MetricNameLabel: "request_metrics_latency_equal_tallying_microseconds", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"}, {clientmodel.MetricNameLabel: "requests_metrics_latency_equal_accumulating_microseconds", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"}, {clientmodel.MetricNameLabel: "requests_metrics_latency_logarithmic_accumulating_microseconds", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"}, {clientmodel.MetricNameLabel: "requests_metrics_latency_logarithmic_tallying_microseconds", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"}, {clientmodel.MetricNameLabel: "targets_healthy_scrape_latency_ms", "instance": "http://localhost:9090/metrics.json", "percentile": "0.010000"}, } for _, metric := range metrics { m := clientmodel.Metric{} for k, v := range metric { m[clientmodel.LabelName(k)] = clientmodel.LabelValue(v) } testAppendSamples(p, &clientmodel.Sample{ Value: clientmodel.SampleValue(0.0), Timestamp: clientmodel.Now(), Metric: m, }, t) } labelSet := clientmodel.LabelSet{ clientmodel.MetricNameLabel: "targets_healthy_scrape_latency_ms", "percentile": "0.010000", } fingerprints, err := p.GetFingerprintsForLabelMatchers(labelMatchersFromLabelSet(labelSet)) if err != nil { t.Errorf("could not get labels: %s", err) } if len(fingerprints) != 1 { t.Errorf("did not get a single metric as is expected, got %s", fingerprints) } }
func testDiscriminatorHttpHeader(t test.Tester) { var scenarios = []struct { input map[string]string output Processor err error }{ { output: nil, err: fmt.Errorf("Received illegal and nil header."), }, { input: map[string]string{"X-Prometheus-API-Version": "0.0.0"}, output: nil, err: fmt.Errorf("Unrecognized API version 0.0.0"), }, { input: map[string]string{"X-Prometheus-API-Version": "0.0.1"}, output: Processor001, err: nil, }, } for i, scenario := range scenarios { var header http.Header if len(scenario.input) > 0 { header = http.Header{} } for key, value := range scenario.input { header.Add(key, value) } actual, err := DefaultRegistry.ProcessorForRequestHeader(header) if scenario.err != err { if scenario.err != nil && err != nil { if scenario.err.Error() != err.Error() { t.Errorf("%d. expected %s, got %s", i, scenario.err, err) } } else if scenario.err != nil || err != nil { t.Errorf("%d. expected %s, got %s", i, scenario.err, err) } } if scenario.output != actual { t.Errorf("%d. expected %s, got %s", i, scenario.output, actual) } } }
func GetLabelValuesForLabelNameTests(p metric.Persistence, t test.Tester) { testAppendSamples(p, &clientmodel.Sample{ Value: 0, Timestamp: 0, Metric: clientmodel.Metric{ clientmodel.MetricNameLabel: "my_metric", "request_type": "create", "result": "success", }, }, t) testAppendSamples(p, &clientmodel.Sample{ Value: 0, Timestamp: 0, Metric: clientmodel.Metric{ clientmodel.MetricNameLabel: "my_metric", "request_type": "delete", "outcome": "failure", }, }, t) expectedIndex := map[clientmodel.LabelName]clientmodel.LabelValues{ clientmodel.MetricNameLabel: {"my_metric"}, "request_type": {"create", "delete"}, "result": {"success"}, "outcome": {"failure"}, } for name, expected := range expectedIndex { actual, err := p.GetLabelValuesForLabelName(name) if err != nil { t.Fatalf("Error getting values for label %s: %v", name, err) } if len(actual) != len(expected) { t.Fatalf("Number of values don't match for label %s: got %d; want %d", name, len(actual), len(expected)) } for i := range expected { if actual[i] != expected[i] { t.Fatalf("%d. Got %s; want %s", i, actual[i], expected[i]) } } } }
func GetFingerprintsForLabelSetTests(p metric.Persistence, t test.Tester) { metrics := []clientmodel.Metric{ { clientmodel.MetricNameLabel: "test_metric", "method": "get", "result": "success", }, { clientmodel.MetricNameLabel: "test_metric", "method": "get", "result": "failure", }, { clientmodel.MetricNameLabel: "test_metric", "method": "post", "result": "success", }, { clientmodel.MetricNameLabel: "test_metric", "method": "post", "result": "failure", }, } newTestLabelMatcher := func(matchType metric.MatchType, name clientmodel.LabelName, value clientmodel.LabelValue) *metric.LabelMatcher { m, err := metric.NewLabelMatcher(matchType, name, value) if err != nil { t.Fatalf("Couldn't create label matcher: %v", err) } return m } scenarios := []struct { in metric.LabelMatchers outIndexes []int }{ { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "test_metric"), }, outIndexes: []int{0, 1, 2, 3}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "non_existent_metric"), }, outIndexes: []int{}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "non_existent_metric"), newTestLabelMatcher(metric.Equal, "result", "success"), }, outIndexes: []int{}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "test_metric"), newTestLabelMatcher(metric.Equal, "result", "success"), }, outIndexes: []int{0, 2}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "test_metric"), newTestLabelMatcher(metric.NotEqual, "result", "success"), }, outIndexes: []int{1, 3}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "test_metric"), newTestLabelMatcher(metric.RegexMatch, "result", "foo|success|bar"), }, outIndexes: []int{0, 2}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "test_metric"), newTestLabelMatcher(metric.RegexNoMatch, "result", "foo|success|bar"), }, outIndexes: []int{1, 3}, }, { in: metric.LabelMatchers{ newTestLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, "test_metric"), newTestLabelMatcher(metric.RegexNoMatch, "result", "foo|success|bar"), newTestLabelMatcher(metric.RegexMatch, "method", "os"), }, outIndexes: []int{3}, }, } for _, m := range metrics { testAppendSamples(p, &clientmodel.Sample{ Value: 0, Timestamp: 0, Metric: m, }, t) } for i, s := range scenarios { actualFps, err := p.GetFingerprintsForLabelMatchers(s.in) if err != nil { t.Fatalf("%d. Couldn't get fingerprints for label matchers: %v", i, err) } expectedFps := clientmodel.Fingerprints{} for _, i := range s.outIndexes { fp := &clientmodel.Fingerprint{} fp.LoadFromMetric(metrics[i]) expectedFps = append(expectedFps, fp) } sort.Sort(actualFps) sort.Sort(expectedFps) if len(actualFps) != len(expectedFps) { t.Fatalf("%d. Got %d fingerprints; want %d", i, len(actualFps), len(expectedFps)) } for j, actualFp := range actualFps { if !actualFp.Equal(expectedFps[j]) { t.Fatalf("%d.%d. Got fingerprint %v; want %v", i, j, actualFp, expectedFps[j]) } } } }
func GetMetricForFingerprintTests(p metric.Persistence, t test.Tester) { testAppendSamples(p, &clientmodel.Sample{ Value: 0, Timestamp: 0, Metric: clientmodel.Metric{ "request_type": "your_mom", }, }, t) testAppendSamples(p, &clientmodel.Sample{ Value: 0, Timestamp: 0, Metric: clientmodel.Metric{ "request_type": "your_dad", "one-off": "value", }, }, t) result, err := p.GetFingerprintsForLabelMatchers(metric.LabelMatchers{{ Type: metric.Equal, Name: "request_type", Value: "your_mom", }}) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } m, err := p.GetMetricForFingerprint(result[0]) if err != nil { t.Error(err) } if m == nil { t.Fatal("Did not expect nil.") } if len(m) != 1 { t.Errorf("Expected one-dimensional metric.") } if m["request_type"] != "your_mom" { t.Errorf("Expected metric to match.") } result, err = p.GetFingerprintsForLabelMatchers(metric.LabelMatchers{{ Type: metric.Equal, Name: "request_type", Value: "your_dad", }}) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } m, err = p.GetMetricForFingerprint(result[0]) if m == nil { t.Fatal("Did not expect nil.") } if err != nil { t.Error(err) } if len(m) != 2 { t.Errorf("Expected two-dimensional metric.") } if m["request_type"] != "your_dad" { t.Errorf("Expected metric to match.") } if m["one-off"] != "value" { t.Errorf("Expected metric to match.") } // Verify that mutating a returned metric does not result in the mutated // metric to be returned at the next GetMetricForFingerprint() call. m["one-off"] = "new value" m, err = p.GetMetricForFingerprint(result[0]) if m == nil { t.Fatal("Did not expect nil.") } if err != nil { t.Error(err) } if len(m) != 2 { t.Errorf("Expected two-dimensional metric.") } if m["request_type"] != "your_dad" { t.Errorf("Expected metric to match.") } if m["one-off"] != "value" { t.Errorf("Expected metric to match.") } }
func testBuilder(t test.Tester) { type atTime struct { fingerprint string time clientmodel.Timestamp } type atInterval struct { fingerprint string from clientmodel.Timestamp through clientmodel.Timestamp interval time.Duration } type atRange struct { fingerprint string from clientmodel.Timestamp through clientmodel.Timestamp } type in struct { atTimes []atTime atIntervals []atInterval atRanges []atRange } type out []struct { fingerprint string operations ops } var scenarios = []struct { in in out out }{ // Ensure that the fingerprint is sorted in proper order. { in: in{ atTimes: []atTime{ { fingerprint: "0000000000000001111-a-4-a", time: clientmodel.TimestampFromUnix(100), }, { fingerprint: "0000000000000000000-a-4-a", time: clientmodel.TimestampFromUnix(100), }, }, }, out: out{ { fingerprint: "00000000000000000000-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, }, }, // // Ensure that the fingerprint-timestamp pairs are sorted in proper order. { in: in{ atTimes: []atTime{ { fingerprint: "1111-a-4-a", time: clientmodel.TimestampFromUnix(100), }, { fingerprint: "1111-a-4-a", time: clientmodel.TimestampFromUnix(200), }, { fingerprint: "0-a-4-a", time: clientmodel.TimestampFromUnix(100), }, { fingerprint: "0-a-4-a", time: clientmodel.TimestampFromUnix(0), }, }, }, out: out{ { fingerprint: "00000000000000000000-a-4-a", }, { fingerprint: "00000000000000000000-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, }, }, // Ensure grouping of operations { in: in{ atTimes: []atTime{ { fingerprint: "1111-a-4-a", time: clientmodel.TimestampFromUnix(100), }, }, atRanges: []atRange{ { fingerprint: "1111-a-4-a", from: clientmodel.TimestampFromUnix(100), through: clientmodel.TimestampFromUnix(1000), }, { fingerprint: "1111-a-4-a", from: clientmodel.TimestampFromUnix(100), through: clientmodel.TimestampFromUnix(9000), }, }, }, out: out{ { fingerprint: "00000000000000001111-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, }, }, } for i, scenario := range scenarios { builder := &viewRequestBuilder{} for _, atTime := range scenario.in.atTimes { fingerprint := &clientmodel.Fingerprint{} fingerprint.LoadFromString(atTime.fingerprint) builder.GetMetricAtTime(fingerprint, atTime.time) } for _, atInterval := range scenario.in.atIntervals { fingerprint := &clientmodel.Fingerprint{} fingerprint.LoadFromString(atInterval.fingerprint) builder.GetMetricAtInterval(fingerprint, atInterval.from, atInterval.through, atInterval.interval) } for _, atRange := range scenario.in.atRanges { fingerprint := &clientmodel.Fingerprint{} fingerprint.LoadFromString(atRange.fingerprint) builder.GetMetricRange(fingerprint, atRange.from, atRange.through) } for j, job := range scenario.out { got := builder.PopOp() if got.Fingerprint().String() != job.fingerprint { t.Errorf("%d.%d. expected fingerprint %s, got %s", i, j, job.fingerprint, got.Fingerprint()) } } if builder.HasOp() { t.Error("Expected builder to have no scan jobs left.") } } }
func testHealthScheduler(t test.Tester) { now := time.Now() var scenarios = []struct { futureHealthState []TargetState preloadedTimes []time.Time expectedSchedule []time.Time }{ // The behavior discussed in healthScheduler.Reschedule should be read // fully to understand the whys and wherefores. { futureHealthState: []TargetState{UNKNOWN, ALIVE, ALIVE}, preloadedTimes: []time.Time{now, now.Add(time.Minute), now.Add(time.Minute * 2)}, expectedSchedule: []time.Time{now, now.Add(time.Minute), now.Add(time.Minute * 2)}, }, { futureHealthState: []TargetState{UNKNOWN, UNREACHABLE, UNREACHABLE}, preloadedTimes: []time.Time{now, now.Add(time.Minute), now.Add(time.Minute * 2)}, expectedSchedule: []time.Time{now, now.Add(time.Second * 2), now.Add(time.Minute).Add(time.Second * 4)}, }, { futureHealthState: []TargetState{UNKNOWN, UNREACHABLE, ALIVE}, preloadedTimes: []time.Time{now, now.Add(time.Minute), now.Add(time.Minute * 2)}, expectedSchedule: []time.Time{now, now.Add(time.Second * 2), now.Add(time.Minute * 2)}, }, { futureHealthState: []TargetState{UNKNOWN, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE, UNREACHABLE}, preloadedTimes: []time.Time{now, now.Add(time.Minute), now.Add(time.Minute * 2), now.Add(time.Minute * 3), now.Add(time.Minute * 4), now.Add(time.Minute * 5), now.Add(time.Minute * 6), now.Add(time.Minute * 7), now.Add(time.Minute * 8), now.Add(time.Minute * 9), now.Add(time.Minute * 10), now.Add(time.Minute * 11), now.Add(time.Minute * 12)}, expectedSchedule: []time.Time{now, now.Add(time.Second * 2), now.Add(time.Minute * 1).Add(time.Second * 4), now.Add(time.Minute * 2).Add(time.Second * 8), now.Add(time.Minute * 3).Add(time.Second * 16), now.Add(time.Minute * 4).Add(time.Second * 32), now.Add(time.Minute * 5).Add(time.Second * 64), now.Add(time.Minute * 6).Add(time.Second * 128), now.Add(time.Minute * 7).Add(time.Second * 256), now.Add(time.Minute * 8).Add(time.Second * 512), now.Add(time.Minute * 9).Add(time.Second * 1024), now.Add(time.Minute * 10).Add(time.Minute * 30), now.Add(time.Minute * 11).Add(time.Minute * 30)}, }, } for i, scenario := range scenarios { provider := test.NewInstantProvider(scenario.preloadedTimes) reporter := fakeHealthReporter{} for _, state := range scenario.futureHealthState { reporter.stateQueue = append(reporter.stateQueue, state) } if len(scenario.preloadedTimes) != len(scenario.futureHealthState) || len(scenario.futureHealthState) != len(scenario.expectedSchedule) { t.Fatalf("%d. times and health reports and next time lengths were not equal.", i) } time := utility.Time{ Provider: provider, } scheduler := healthScheduler{ time: time, target: reporter, scheduledFor: now, } for j := 0; j < len(scenario.preloadedTimes); j++ { futureState := scenario.futureHealthState[j] scheduler.Reschedule(scenario.preloadedTimes[j], futureState) nextSchedule := scheduler.ScheduledFor() if nextSchedule != scenario.expectedSchedule[j] { t.Errorf("%d.%d. Expected to be scheduled to %s, got %s", i, j, scenario.expectedSchedule[j], nextSchedule) } } } }
func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) { testAppendSample(p, model.Sample{ Value: 0, Timestamp: time.Time{}, Metric: model.Metric{ "request_type": "your_mom", }, }, t) testAppendSample(p, model.Sample{ Value: 0, Timestamp: time.Time{}, Metric: model.Metric{ "request_type": "your_dad", "one-off": "value", }, }, t) result, err := p.GetFingerprintsForLabelSet(model.LabelSet{ model.LabelName("request_type"): model.LabelValue("your_mom"), }) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } v, e := p.GetMetricForFingerprint(result[0]) if e != nil { t.Error(e) } if v == nil { t.Fatal("Did not expect nil.") } metric := *v if len(metric) != 1 { t.Errorf("Expected one-dimensional metric.") } if metric["request_type"] != "your_mom" { t.Errorf("Expected metric to match.") } result, err = p.GetFingerprintsForLabelSet(model.LabelSet{ model.LabelName("request_type"): model.LabelValue("your_dad"), }) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } v, e = p.GetMetricForFingerprint(result[0]) if v == nil { t.Fatal("Did not expect nil.") } metric = *v if e != nil { t.Error(e) } if len(metric) != 2 { t.Errorf("Expected one-dimensional metric.") } if metric["request_type"] != "your_dad" { t.Errorf("Expected metric to match.") } if metric["one-off"] != "value" { t.Errorf("Expected metric to match.") } }
func GetFingerprintsForLabelNameTests(p MetricPersistence, t test.Tester) { testAppendSample(p, model.Sample{ Value: 0, Timestamp: time.Time{}, Metric: model.Metric{ model.MetricNameLabel: "my_metric", "request_type": "your_mom", "language": "english", }, }, t) testAppendSample(p, model.Sample{ Value: 0, Timestamp: time.Time{}, Metric: model.Metric{ model.MetricNameLabel: "my_metric", "request_type": "your_dad", "sprache": "deutsch", }, }, t) b := model.MetricNameLabel result, err := p.GetFingerprintsForLabelName(b) if err != nil { t.Error(err) } if len(result) != 2 { t.Errorf("Expected two elements.") } b = model.LabelName("request_type") result, err = p.GetFingerprintsForLabelName(b) if err != nil { t.Error(err) } if len(result) != 2 { t.Errorf("Expected two elements.") } b = model.LabelName("language") result, err = p.GetFingerprintsForLabelName(b) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } b = model.LabelName("sprache") result, err = p.GetFingerprintsForLabelName(b) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } }
func testMakeView(t test.Tester) { type in struct { atTime []getValuesAtTimeOp atInterval []getValuesAtIntervalOp alongRange []getValuesAlongRangeOp } type out struct { atTime [][]model.SamplePair atInterval [][]model.SamplePair alongRange [][]model.SamplePair } var ( instant = time.Date(1984, 3, 30, 0, 0, 0, 0, time.Local) metric = model.Metric{model.MetricNameLabel: "request_count"} fingerprint = model.NewFingerprintFromMetric(metric) scenarios = []struct { data []model.Sample in in out out }{ // No sample, but query asks for one. { in: in{ atTime: []getValuesAtTimeOp{ { time: instant, }, }, }, out: out{ atTime: [][]model.SamplePair{{}}, }, }, // Single sample, query asks for exact sample time. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant, }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant, }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Single sample, query time before the sample. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant.Add(time.Second), }, { Metric: metric, Value: 1, Timestamp: instant.Add(time.Second * 2), }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant, }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant.Add(time.Second), Value: 0, }, }, }, }, }, // Single sample, query time after the sample. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant, }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant.Add(time.Second), }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Two samples, query asks for first sample time. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant, }, { Metric: metric, Value: 1, Timestamp: instant.Add(time.Second), }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant, }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Three samples, query asks for second sample time. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant, }, { Metric: metric, Value: 1, Timestamp: instant.Add(time.Second), }, { Metric: metric, Value: 2, Timestamp: instant.Add(time.Second * 2), }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant.Add(time.Second), }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant.Add(time.Second), Value: 1, }, }, }, }, }, // Three samples, query asks for time between first and second samples. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant, }, { Metric: metric, Value: 1, Timestamp: instant.Add(time.Second * 2), }, { Metric: metric, Value: 2, Timestamp: instant.Add(time.Second * 4), }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant.Add(time.Second), }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant, Value: 0, }, { Timestamp: instant.Add(time.Second * 2), Value: 1, }, }, }, }, }, // Three samples, query asks for time between second and third samples. { data: []model.Sample{ { Metric: metric, Value: 0, Timestamp: instant, }, { Metric: metric, Value: 1, Timestamp: instant.Add(time.Second * 2), }, { Metric: metric, Value: 2, Timestamp: instant.Add(time.Second * 4), }, }, in: in{ atTime: []getValuesAtTimeOp{ { time: instant.Add(time.Second * 3), }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant.Add(time.Second * 2), Value: 1, }, { Timestamp: instant.Add(time.Second * 4), Value: 2, }, }, }, }, }, // Two chunks of samples, query asks for values from first chunk. { data: buildSamples(instant, instant.Add(time.Duration(*leveldbChunkSize*2)*time.Second), time.Second, metric), in: in{ atTime: []getValuesAtTimeOp{ { time: instant.Add(time.Second*time.Duration(*leveldbChunkSize/2) + 1), }, }, }, out: out{ atTime: [][]model.SamplePair{ { { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize/2)), Value: 100, }, { Timestamp: instant.Add(time.Second * (time.Duration(*leveldbChunkSize/2) + 1)), Value: 101, }, }, }, }, }, } ) for i, scenario := range scenarios { tiered, closer := newTestTieredStorage(t) defer closer.Close() for j, datum := range scenario.data { err := tiered.AppendSample(datum) if err != nil { t.Fatalf("%d.%d. failed to add fixture data: %s", i, j, err) } } tiered.Flush() requestBuilder := NewViewRequestBuilder() for _, atTime := range scenario.in.atTime { requestBuilder.GetMetricAtTime(fingerprint, atTime.time) } for _, atInterval := range scenario.in.atInterval { requestBuilder.GetMetricAtInterval(fingerprint, atInterval.from, atInterval.through, atInterval.interval) } for _, alongRange := range scenario.in.alongRange { requestBuilder.GetMetricRange(fingerprint, alongRange.from, alongRange.through) } v, err := tiered.MakeView(requestBuilder, time.Second*5) if err != nil { t.Fatalf("%d. failed due to %s", i, err) } for j, atTime := range scenario.in.atTime { actual := v.GetValueAtTime(fingerprint, atTime.time) if len(actual) != len(scenario.out.atTime[j]) { t.Fatalf("%d.%d. expected %d output, got %d", i, j, len(scenario.out.atTime[j]), len(actual)) } for k, value := range scenario.out.atTime[j] { if value.Value != actual[k].Value { t.Fatalf("%d.%d.%d expected %v value, got %v", i, j, k, value.Value, actual[k].Value) } if !value.Timestamp.Equal(actual[k].Timestamp) { t.Fatalf("%d.%d.%d expected %s timestamp, got %s", i, j, k, value.Timestamp, actual[k].Timestamp) } } } tiered.Drain() } }
func GetRangeValuesTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) { type value struct { year int month time.Month day int hour int value float32 } type input struct { openYear int openMonth time.Month openDay int openHour int endYear int endMonth time.Month endDay int endHour int } type output struct { year int month time.Month day int hour int value float32 } type behavior struct { name string input input output []output } var contexts = []struct { name string values []value behaviors []behavior }{ { name: "no values", values: []value{}, behaviors: []behavior{ { name: "non-existent interval", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, }, }, }, }, { name: "singleton value", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, behaviors: []behavior{ { name: "start on first value", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, }, { name: "end on first value", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, }, { name: "overlap on first value", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, }, }, }, { name: "two values", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, behaviors: []behavior{ { name: "start on first value", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, }, { name: "start on second value", input: input{ openYear: 1985, openMonth: 3, openDay: 30, openHour: 0, endYear: 1986, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, }, { name: "end on first value", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, }, { name: "end on second value", input: input{ openYear: 1985, openMonth: 1, openDay: 1, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, }, { name: "overlap on values", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1986, endMonth: 3, endDay: 30, endHour: 0, }, output: []output{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, }, }, }, } for i, context := range contexts { // Wrapping in function to enable garbage collection of resources. func() { p, closer := persistenceMaker() defer closer.Close() defer p.Close() m := model.Metric{ model.MetricNameLabel: "age_in_years", } for _, value := range context.values { testAppendSample(p, model.Sample{ Value: model.SampleValue(value.value), Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC), Metric: m, }, t) } for j, behavior := range context.behaviors { input := behavior.input open := time.Date(input.openYear, input.openMonth, input.openDay, input.openHour, 0, 0, 0, time.UTC) end := time.Date(input.endYear, input.endMonth, input.endDay, input.endHour, 0, 0, 0, time.UTC) in := model.Interval{ OldestInclusive: open, NewestInclusive: end, } values, err := p.GetRangeValues(model.NewFingerprintFromMetric(m), in) if err != nil { t.Fatalf("%d.%d(%s). Could not query for value: %q\n", i, j, behavior.name, err) } if values == nil && len(behavior.output) != 0 { t.Fatalf("%d.%d(%s). Expected %s but got: %s\n", i, j, behavior.name, behavior.output, values) } if behavior.output == nil { if values != nil { t.Fatalf("%d.%d(%s). Expected nil values but got: %s\n", i, j, behavior.name, values) } } else { if len(behavior.output) != len(values.Values) { t.Fatalf("%d.%d(%s). Expected length %d but got: %d\n", i, j, behavior.name, len(behavior.output), len(values.Values)) } for k, actual := range values.Values { expected := behavior.output[k] if actual.Value != model.SampleValue(expected.value) { t.Fatalf("%d.%d.%d(%s). Expected %d but got: %d\n", i, j, k, behavior.name, expected.value, actual.Value) } if actual.Timestamp.Year() != expected.year { t.Fatalf("%d.%d.%d(%s). Expected %d but got: %d\n", i, j, k, behavior.name, expected.year, actual.Timestamp.Year()) } if actual.Timestamp.Month() != expected.month { t.Fatalf("%d.%d.%d(%s). Expected %d but got: %d\n", i, j, k, behavior.name, expected.month, actual.Timestamp.Month()) } // XXX: Find problem here. // Mismatches occur in this and have for a long time in the LevelDB // case, however not im-memory. // // if actual.Timestamp.Day() != expected.day { // t.Fatalf("%d.%d.%d(%s). Expected %d but got: %d\n", i, j, k, behavior.name, expected.day, actual.Timestamp.Day()) // } // if actual.Timestamp.Hour() != expected.hour { // t.Fatalf("%d.%d.%d(%s). Expected %d but got: %d\n", i, j, k, behavior.name, expected.hour, actual.Timestamp.Hour()) // } } } } }() } }
func GetBoundaryValuesTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) { type value struct { year int month time.Month day int hour int value float32 } type input struct { openYear int openMonth time.Month openDay int openHour int endYear int endMonth time.Month endDay int endHour int staleness time.Duration } type output struct { open model.SampleValue end model.SampleValue } type behavior struct { name string input input output *output } var contexts = []struct { name string values []value behaviors []behavior }{ { name: "no values", values: []value{}, behaviors: []behavior{ { name: "non-existent interval without staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, }, { name: "non-existent interval with staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(365*24) * time.Hour, }, }, }, }, { name: "single value", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, behaviors: []behavior{ { name: "on start but missing end without staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, }, { name: "non-existent interval after within staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 31, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(4380) * time.Hour, }, }, { name: "non-existent interval after without staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 31, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, }, { name: "non-existent interval before with staleness policy", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 3, endDay: 29, endHour: 0, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "non-existent interval before without staleness policy", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 3, endDay: 29, endHour: 0, staleness: time.Duration(0), }, }, { name: "on end but not start without staleness policy", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, }, { name: "on end but not start without staleness policy", input: input{ openYear: 1983, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "before point without staleness policy", input: input{ openYear: 1982, openMonth: 3, openDay: 30, openHour: 0, endYear: 1983, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, }, { name: "before point with staleness policy", input: input{ openYear: 1982, openMonth: 3, openDay: 30, openHour: 0, endYear: 1983, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "after point without staleness policy", input: input{ openYear: 1985, openMonth: 3, openDay: 30, openHour: 0, endYear: 1986, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, }, { name: "after point with staleness policy", input: input{ openYear: 1985, openMonth: 3, openDay: 30, openHour: 0, endYear: 1986, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "spanning point without staleness policy", input: input{ openYear: 1983, openMonth: 9, openDay: 29, openHour: 12, endYear: 1984, endMonth: 9, endDay: 28, endHour: 12, staleness: time.Duration(0), }, }, { name: "spanning point with staleness policy", input: input{ openYear: 1983, openMonth: 9, openDay: 29, openHour: 12, endYear: 1984, endMonth: 9, endDay: 28, endHour: 12, staleness: time.Duration(365*24) * time.Hour, }, }, }, }, { name: "double values", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, behaviors: []behavior{ { name: "on points without staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(0), }, output: &output{ open: 0, end: 1, }, }, { name: "on points with staleness policy", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 3, endDay: 30, endHour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ open: 0, end: 1, }, }, { name: "on first before second outside of staleness", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 6, endDay: 29, endHour: 6, staleness: time.Duration(2190) * time.Hour, }, }, { name: "on first before second within staleness", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1984, endMonth: 6, endDay: 29, endHour: 6, staleness: time.Duration(356*24) * time.Hour, }, }, { name: "on first after second outside of staleness", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 6, endDay: 29, endHour: 6, staleness: time.Duration(1) * time.Hour, }, }, { name: "on first after second within staleness", input: input{ openYear: 1984, openMonth: 3, openDay: 30, openHour: 0, endYear: 1985, endMonth: 6, endDay: 29, endHour: 6, staleness: time.Duration(356*24) * time.Hour, }, output: &output{ open: 0, end: 1, }, }, }, }, } for i, context := range contexts { // Wrapping in function to enable garbage collection of resources. func() { p, closer := persistenceMaker() defer closer.Close() defer p.Close() m := model.Metric{ model.MetricNameLabel: "age_in_years", } for _, value := range context.values { testAppendSample(p, model.Sample{ Value: model.SampleValue(value.value), Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC), Metric: m, }, t) } for j, behavior := range context.behaviors { input := behavior.input open := time.Date(input.openYear, input.openMonth, input.openDay, input.openHour, 0, 0, 0, time.UTC) end := time.Date(input.endYear, input.endMonth, input.endDay, input.endHour, 0, 0, 0, time.UTC) interval := model.Interval{ OldestInclusive: open, NewestInclusive: end, } po := StalenessPolicy{ DeltaAllowance: input.staleness, } openValue, endValue, err := p.GetBoundaryValues(model.NewFingerprintFromMetric(m), interval, po) if err != nil { t.Fatalf("%d.%d(%s). Could not query for value: %q\n", i, j, behavior.name, err) } if behavior.output == nil { if openValue != nil { t.Fatalf("%d.%d(%s). Expected open to be nil but got: %q\n", i, j, behavior.name, openValue) } if endValue != nil { t.Fatalf("%d.%d(%s). Expected end to be nil but got: %q\n", i, j, behavior.name, endValue) } } else { if openValue == nil { t.Fatalf("%d.%d(%s). Expected open to be %s but got nil\n", i, j, behavior.name, behavior.output) } if endValue == nil { t.Fatalf("%d.%d(%s). Expected end to be %s but got nil\n", i, j, behavior.name, behavior.output) } if openValue.Value != behavior.output.open { t.Fatalf("%d.%d(%s). Expected open to be %s but got %s\n", i, j, behavior.name, behavior.output.open, openValue.Value) } if endValue.Value != behavior.output.end { t.Fatalf("%d.%d(%s). Expected end to be %s but got %s\n", i, j, behavior.name, behavior.output.end, endValue.Value) } } } }() } }
func GetValueAtTimeTests(persistenceMaker func() (MetricPersistence, test.Closer), t test.Tester) { type value struct { year int month time.Month day int hour int value float32 } type input struct { year int month time.Month day int hour int staleness time.Duration } type output struct { value model.SampleValue } type behavior struct { name string input input output *output } var contexts = []struct { name string values []value behaviors []behavior }{ { name: "no values", values: []value{}, behaviors: []behavior{ { name: "random target", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, }, }, }, { name: "singleton", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, }, behaviors: []behavior{ { name: "exact without staleness policy", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, output: &output{ value: 0, }, }, { name: "exact with staleness policy", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 0, }, }, { name: "before without staleness policy", input: input{ year: 1984, month: 3, day: 29, hour: 0, staleness: time.Duration(0), }, }, { name: "before within staleness policy", input: input{ year: 1984, month: 3, day: 29, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "before outside staleness policy", input: input{ year: 1984, month: 3, day: 29, hour: 0, staleness: time.Duration(1) * time.Hour, }, }, { name: "after without staleness policy", input: input{ year: 1984, month: 3, day: 31, hour: 0, staleness: time.Duration(0), }, }, { name: "after within staleness policy", input: input{ year: 1984, month: 3, day: 31, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 0, }, }, { name: "after outside staleness policy", input: input{ year: 1984, month: 4, day: 7, hour: 0, staleness: time.Duration(7*24) * time.Hour, }, }, }, }, { name: "double", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, }, behaviors: []behavior{ { name: "exact first without staleness policy", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, output: &output{ value: 0, }, }, { name: "exact first with staleness policy", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 0, }, }, { name: "exact second without staleness policy", input: input{ year: 1985, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, output: &output{ value: 1, }, }, { name: "exact second with staleness policy", input: input{ year: 1985, month: 3, day: 30, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 1, }, }, { name: "before first without staleness policy", input: input{ year: 1983, month: 9, day: 29, hour: 12, staleness: time.Duration(0), }, }, { name: "before first with staleness policy", input: input{ year: 1983, month: 9, day: 29, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "after second with staleness policy", input: input{ year: 1985, month: 9, day: 28, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 1, }, }, { name: "after second without staleness policy", input: input{ year: 1985, month: 9, day: 28, hour: 12, staleness: time.Duration(0), }, }, { name: "middle without staleness policy", input: input{ year: 1984, month: 9, day: 28, hour: 12, staleness: time.Duration(0), }, }, { name: "middle with insufficient staleness policy", input: input{ year: 1984, month: 9, day: 28, hour: 12, staleness: time.Duration(364*24) * time.Hour, }, }, { name: "middle with sufficient staleness policy", input: input{ year: 1984, month: 9, day: 28, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 0.5, }, }, }, }, { name: "triple", values: []value{ { year: 1984, month: 3, day: 30, hour: 0, value: 0, }, { year: 1985, month: 3, day: 30, hour: 0, value: 1, }, { year: 1986, month: 3, day: 30, hour: 0, value: 2, }, }, behaviors: []behavior{ { name: "exact first without staleness policy", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, output: &output{ value: 0, }, }, { name: "exact first with staleness policy", input: input{ year: 1984, month: 3, day: 30, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 0, }, }, { name: "exact second without staleness policy", input: input{ year: 1985, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, output: &output{ value: 1, }, }, { name: "exact second with staleness policy", input: input{ year: 1985, month: 3, day: 30, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 1, }, }, { name: "exact third without staleness policy", input: input{ year: 1986, month: 3, day: 30, hour: 0, staleness: time.Duration(0), }, output: &output{ value: 2, }, }, { name: "exact third with staleness policy", input: input{ year: 1986, month: 3, day: 30, hour: 0, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 2, }, }, { name: "before first without staleness policy", input: input{ year: 1983, month: 9, day: 29, hour: 12, staleness: time.Duration(0), }, }, { name: "before first with staleness policy", input: input{ year: 1983, month: 9, day: 29, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, }, { name: "after third within staleness policy", input: input{ year: 1986, month: 9, day: 28, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 2, }, }, { name: "after third outside staleness policy", input: input{ year: 1986, month: 9, day: 28, hour: 12, staleness: time.Duration(1*24) * time.Hour, }, }, { name: "after third without staleness policy", input: input{ year: 1986, month: 9, day: 28, hour: 12, staleness: time.Duration(0), }, }, { name: "first middle without staleness policy", input: input{ year: 1984, month: 9, day: 28, hour: 12, staleness: time.Duration(0), }, }, { name: "first middle with insufficient staleness policy", input: input{ year: 1984, month: 9, day: 28, hour: 12, staleness: time.Duration(364*24) * time.Hour, }, }, { name: "first middle with sufficient staleness policy", input: input{ year: 1984, month: 9, day: 28, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 0.5, }, }, { name: "second middle without staleness policy", input: input{ year: 1985, month: 9, day: 28, hour: 12, staleness: time.Duration(0), }, }, { name: "second middle with insufficient staleness policy", input: input{ year: 1985, month: 9, day: 28, hour: 12, staleness: time.Duration(364*24) * time.Hour, }, }, { name: "second middle with sufficient staleness policy", input: input{ year: 1985, month: 9, day: 28, hour: 12, staleness: time.Duration(365*24) * time.Hour, }, output: &output{ value: 1.5, }, }, }, }, } for i, context := range contexts { // Wrapping in function to enable garbage collection of resources. func() { p, closer := persistenceMaker() defer closer.Close() defer p.Close() m := model.Metric{ model.MetricNameLabel: "age_in_years", } for _, value := range context.values { testAppendSample(p, model.Sample{ Value: model.SampleValue(value.value), Timestamp: time.Date(value.year, value.month, value.day, value.hour, 0, 0, 0, time.UTC), Metric: m, }, t) } for j, behavior := range context.behaviors { input := behavior.input time := time.Date(input.year, input.month, input.day, input.hour, 0, 0, 0, time.UTC) sp := StalenessPolicy{ DeltaAllowance: input.staleness, } actual, err := p.GetValueAtTime(model.NewFingerprintFromMetric(m), time, sp) if err != nil { t.Fatalf("%d.%d(%s). Could not query for value: %q\n", i, j, behavior.name, err) } if behavior.output == nil { if actual != nil { t.Fatalf("%d.%d(%s). Expected nil but got: %q\n", i, j, behavior.name, actual) } } else { if actual == nil { t.Fatalf("%d.%d(%s). Expected %s but got nil\n", i, j, behavior.name, behavior.output) } else { if actual.Value != behavior.output.value { t.Fatalf("%d.%d(%s). Expected %s but got %s\n", i, j, behavior.name, behavior.output, actual) } } } } }() } }
func testTargetPool(t test.Tester) { type expectation struct { size int } type input struct { address string scheduledFor time.Time } type output struct { address string } var scenarios = []struct { name string inputs []input outputs []output }{ { name: "empty", inputs: []input{}, outputs: []output{}, }, { name: "single element", inputs: []input{ { address: "http://single.com", }, }, outputs: []output{ { address: "http://single.com", }, }, }, { name: "plural descending schedules", inputs: []input{ { address: "http://plural-descending.com", scheduledFor: time.Date(2013, 1, 4, 12, 0, 0, 0, time.UTC), }, { address: "http://plural-descending.net", scheduledFor: time.Date(2013, 1, 4, 11, 0, 0, 0, time.UTC), }, }, outputs: []output{ { address: "http://plural-descending.net", }, { address: "http://plural-descending.com", }, }, }, { name: "plural ascending schedules", inputs: []input{ { address: "http://plural-ascending.net", scheduledFor: time.Date(2013, 1, 4, 11, 0, 0, 0, time.UTC), }, { address: "http://plural-ascending.com", scheduledFor: time.Date(2013, 1, 4, 12, 0, 0, 0, time.UTC), }, }, outputs: []output{ { address: "http://plural-ascending.net", }, { address: "http://plural-ascending.com", }, }, }, } for i, scenario := range scenarios { pool := TargetPool{} for _, input := range scenario.inputs { target := target{ address: input.address, scheduler: literalScheduler(input.scheduledFor), } pool.addTarget(&target) } sort.Sort(pool) if pool.Len() != len(scenario.outputs) { t.Errorf("%s %d. expected TargetPool size to be %d but was %d", scenario.name, i, len(scenario.outputs), pool.Len()) } else { for j, output := range scenario.outputs { target := pool.targets[j] if target.Address() != output.address { t.Errorf("%s %d.%d. expected Target address to be %s but was %s", scenario.name, i, j, output.address, target.Address()) } } if pool.Len() != len(scenario.outputs) { t.Errorf("%s %d. expected to repopulated with %d elements, got %d", scenario.name, i, len(scenario.outputs), pool.Len()) } } } }
func testBuilder(t test.Tester) { type atTime struct { fingerprint string time time.Time } type atInterval struct { fingerprint string from time.Time through time.Time interval time.Duration } type atRange struct { fingerprint string from time.Time through time.Time } type in struct { atTimes []atTime atIntervals []atInterval atRanges []atRange } type out []struct { fingerprint string operations ops } var scenarios = []struct { in in out out }{ // // Ensure that the fingerprint is sorted in proper order. { in: in{ atTimes: []atTime{ { fingerprint: "0000000000000001111-a-4-a", time: time.Unix(100, 0), }, { fingerprint: "0000000000000000000-a-4-a", time: time.Unix(100, 0), }, }, }, out: out{ { fingerprint: "00000000000000000000-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, }, }, // // Ensure that the fingerprint-timestamp pairs are sorted in proper order. { in: in{ atTimes: []atTime{ { fingerprint: "1111-a-4-a", time: time.Unix(100, 0), }, { fingerprint: "1111-a-4-a", time: time.Unix(200, 0), }, { fingerprint: "0-a-4-a", time: time.Unix(100, 0), }, { fingerprint: "0-a-4-a", time: time.Unix(0, 0), }, }, }, out: out{ { fingerprint: "00000000000000000000-a-4-a", }, { fingerprint: "00000000000000001111-a-4-a", }, }, }, // Ensure grouping of operations { in: in{ atTimes: []atTime{ { fingerprint: "1111-a-4-a", time: time.Unix(100, 0), }, }, atRanges: []atRange{ { fingerprint: "1111-a-4-a", from: time.Unix(100, 0), through: time.Unix(1000, 0), }, { fingerprint: "1111-a-4-a", from: time.Unix(100, 0), through: time.Unix(9000, 0), }, }, }, out: out{ { fingerprint: "00000000000000001111-a-4-a", }, }, }, } for i, scenario := range scenarios { builder := viewRequestBuilder{ operations: map[model.Fingerprint]ops{}, } for _, atTime := range scenario.in.atTimes { fingerprint := model.NewFingerprintFromRowKey(atTime.fingerprint) builder.GetMetricAtTime(fingerprint, atTime.time) } for _, atInterval := range scenario.in.atIntervals { fingerprint := model.NewFingerprintFromRowKey(atInterval.fingerprint) builder.GetMetricAtInterval(fingerprint, atInterval.from, atInterval.through, atInterval.interval) } for _, atRange := range scenario.in.atRanges { fingerprint := model.NewFingerprintFromRowKey(atRange.fingerprint) builder.GetMetricRange(fingerprint, atRange.from, atRange.through) } jobs := builder.ScanJobs() if len(scenario.out) != len(jobs) { t.Fatalf("%d. expected job length of %d, got %d\n", i, len(scenario.out), len(jobs)) } for j, job := range scenario.out { if jobs[j].fingerprint.ToRowKey() != job.fingerprint { t.Fatalf("%d.%d. expected fingerprint %s, got %s\n", i, j, job.fingerprint, jobs[j].fingerprint.ToRowKey()) } } } }
func testMakeView(t test.Tester, flushToDisk bool) { type in struct { atTime []getValuesAtTimeOp atInterval []getValuesAtIntervalOp alongRange []getValuesAlongRangeOp } type out struct { atTime []metric.Values atInterval []metric.Values alongRange []metric.Values } m := clientmodel.Metric{clientmodel.MetricNameLabel: "request_count"} fingerprint := &clientmodel.Fingerprint{} fingerprint.LoadFromMetric(m) var ( instant = clientmodel.TimestampFromTime(time.Date(1984, 3, 30, 0, 0, 0, 0, time.Local)) scenarios = []struct { data clientmodel.Samples in in out out diskOnly bool }{ // No sample, but query asks for one. { in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant}, }, }, }, out: out{ atTime: []metric.Values{{}}, }, }, // Single sample, query asks for exact sample time. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Single sample, query time before the sample. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant.Add(time.Second), }, { Metric: m, Value: 1, Timestamp: instant.Add(time.Second * 2), }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant.Add(time.Second), Value: 0, }, }, }, }, }, // Single sample, query time after the sample. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant.Add(time.Second)}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Two samples, query asks for first sample time. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, { Metric: m, Value: 1, Timestamp: instant.Add(time.Second), }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Three samples, query asks for second sample time. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, { Metric: m, Value: 1, Timestamp: instant.Add(time.Second), }, { Metric: m, Value: 2, Timestamp: instant.Add(time.Second * 2), }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant.Add(time.Second)}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant.Add(time.Second), Value: 1, }, }, }, }, }, // Three samples, query asks for time between first and second samples. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, { Metric: m, Value: 1, Timestamp: instant.Add(time.Second * 2), }, { Metric: m, Value: 2, Timestamp: instant.Add(time.Second * 4), }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant.Add(time.Second)}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant, Value: 0, }, { Timestamp: instant.Add(time.Second * 2), Value: 1, }, }, }, }, }, // Three samples, query asks for time between second and third samples. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, { Metric: m, Value: 1, Timestamp: instant.Add(time.Second * 2), }, { Metric: m, Value: 2, Timestamp: instant.Add(time.Second * 4), }, }, in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant.Add(time.Second * 3)}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant.Add(time.Second * 2), Value: 1, }, { Timestamp: instant.Add(time.Second * 4), Value: 2, }, }, }, }, }, // Two chunks of samples, query asks for values from second chunk. { data: buildSamples( instant, instant.Add(time.Duration(*leveldbChunkSize*4)*time.Second), 2*time.Second, m, ), in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant.Add(time.Second*time.Duration(*leveldbChunkSize*2) + clientmodel.MinimumTick)}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2)), Value: 200, }, { Timestamp: instant.Add(time.Second * (time.Duration(*leveldbChunkSize*2) + 2)), Value: 201, }, }, }, }, }, // Two chunks of samples, query asks for values between both chunks. { data: buildSamples( instant, instant.Add(time.Duration(*leveldbChunkSize*4)*time.Second), 2*time.Second, m, ), in: in{ atTime: []getValuesAtTimeOp{ { baseOp: baseOp{current: instant.Add(time.Second*time.Duration(*leveldbChunkSize*2) - clientmodel.MinimumTick)}, }, }, }, out: out{ atTime: []metric.Values{ { { Timestamp: instant.Add(time.Second * (time.Duration(*leveldbChunkSize*2) - 2)), Value: 199, }, { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2)), Value: 200, }, }, }, }, }, // Two chunks of samples, getValuesAtIntervalOp spanning both. { data: buildSamples( instant, instant.Add(time.Duration(*leveldbChunkSize*6)*time.Second), 2*time.Second, m, ), in: in{ atInterval: []getValuesAtIntervalOp{ { getValuesAlongRangeOp: getValuesAlongRangeOp{ baseOp: baseOp{current: instant.Add(time.Second*time.Duration(*leveldbChunkSize*2-4) - clientmodel.MinimumTick)}, through: instant.Add(time.Second*time.Duration(*leveldbChunkSize*2+4) + clientmodel.MinimumTick), }, interval: time.Second * 6, }, }, }, out: out{ atInterval: []metric.Values{ { { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2-6)), Value: 197, }, { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2-4)), Value: 198, }, { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2)), Value: 200, }, { Timestamp: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2+2)), Value: 201, }, }, }, }, }, // Three chunks of samples, getValuesAlongRangeOp spanning all of them. { data: buildSamples( instant, instant.Add(time.Duration(*leveldbChunkSize*6)*time.Second), 2*time.Second, m, ), in: in{ alongRange: []getValuesAlongRangeOp{ { baseOp: baseOp{current: instant.Add(time.Second*time.Duration(*leveldbChunkSize*2-4) - clientmodel.MinimumTick)}, through: instant.Add(time.Second*time.Duration(*leveldbChunkSize*4+2) + clientmodel.MinimumTick), }, }, }, out: out{ alongRange: []metric.Values{buildValues( clientmodel.SampleValue(198), instant.Add(time.Second*time.Duration(*leveldbChunkSize*2-4)), instant.Add(time.Second*time.Duration(*leveldbChunkSize*4+2)+clientmodel.MinimumTick), 2*time.Second, )}, }, }, // Three chunks of samples and a getValuesAlongIntervalOp with an // interval larger than the natural sample interval, spanning the gap // between the second and third chunks. To test two consecutive // ExtractSamples() calls for the same op, we need three on-disk chunks, // because the first two chunks are loaded from disk together and passed // as one unit into ExtractSamples(). Especially, we want to test that // the first sample of the last chunk is included in the result. // // This is a regression test for an interval operator advancing too far // past the end of the currently available chunk, effectively skipping // over a value which is only available in the next chunk passed to // ExtractSamples(). // // Chunk and operator layout, assuming 200 samples per chunk: // // Chunk 1 Chunk 2 Chunk 3 // Values: 0......199 200......399 400......599 // Times: 0......398 400......798 800......1198 // | | // |_________ Operator _______| // 395 399 ...... 795 799 803 { data: buildSamples( instant, instant.Add(time.Duration(*leveldbChunkSize*6)*time.Second), 2*time.Second, m, ), in: in{ atInterval: []getValuesAtIntervalOp{ { getValuesAlongRangeOp: getValuesAlongRangeOp{ baseOp: baseOp{current: instant.Add(time.Second * time.Duration(*leveldbChunkSize*2-5))}, through: instant.Add(time.Second * time.Duration(*leveldbChunkSize*4+3)), }, interval: time.Second * 4, }, }, }, out: out{ atInterval: []metric.Values{ // We need two overlapping buildValues() calls here since the last // value of the second chunk is extracted twice (value 399, time // offset 798s). append( // Values 197...399. // Times 394...798. buildValues( clientmodel.SampleValue(197), instant.Add(time.Second*time.Duration(*leveldbChunkSize*2-6)), instant.Add(time.Second*time.Duration(*leveldbChunkSize*4)), 2*time.Second, ), // Values 399...402. // Times 798...804. buildValues( clientmodel.SampleValue(399), instant.Add(time.Second*time.Duration(*leveldbChunkSize*4-2)), instant.Add(time.Second*time.Duration(*leveldbChunkSize*4+6)), 2*time.Second, )..., ), }, }, // This example only works with on-disk chunks due to the repeatedly // extracted value at the end of the second chunk. diskOnly: true, }, // Single sample, getValuesAtIntervalOp starting after the sample. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant, }, }, in: in{ atInterval: []getValuesAtIntervalOp{ { getValuesAlongRangeOp: getValuesAlongRangeOp{ baseOp: baseOp{current: instant.Add(time.Second)}, through: instant.Add(time.Second * 2), }, interval: time.Second, }, }, }, out: out{ atInterval: []metric.Values{ { { Timestamp: instant, Value: 0, }, }, }, }, }, // Single sample, getValuesAtIntervalOp starting before the sample. { data: clientmodel.Samples{ { Metric: m, Value: 0, Timestamp: instant.Add(time.Second), }, }, in: in{ atInterval: []getValuesAtIntervalOp{ { getValuesAlongRangeOp: getValuesAlongRangeOp{ baseOp: baseOp{current: instant}, through: instant.Add(time.Second * 2), }, interval: time.Second, }, }, }, out: out{ atInterval: []metric.Values{ { { Timestamp: instant.Add(time.Second), Value: 0, }, { Timestamp: instant.Add(time.Second), Value: 0, }, }, }, }, }, } ) for i, scenario := range scenarios { if scenario.diskOnly && !flushToDisk { continue } tiered, closer := NewTestTieredStorage(t) err := tiered.AppendSamples(scenario.data) if err != nil { t.Fatalf("%d. failed to add fixture data: %s", i, err) } if flushToDisk { tiered.Flush() } requestBuilder := tiered.NewViewRequestBuilder() for _, atTime := range scenario.in.atTime { requestBuilder.GetMetricAtTime(fingerprint, atTime.current) } for _, atInterval := range scenario.in.atInterval { requestBuilder.GetMetricAtInterval(fingerprint, atInterval.current, atInterval.through, atInterval.interval) } for _, alongRange := range scenario.in.alongRange { requestBuilder.GetMetricRange(fingerprint, alongRange.current, alongRange.through) } v, err := requestBuilder.Execute(time.Second*5, stats.NewTimerGroup()) if err != nil { t.Fatalf("%d. failed due to %s", i, err) } // To get all values in the View, ask for the 'forever' interval. interval := metric.Interval{OldestInclusive: math.MinInt64, NewestInclusive: math.MaxInt64} for j, atTime := range scenario.out.atTime { actual := v.GetRangeValues(fingerprint, interval) if len(actual) != len(atTime) { t.Fatalf("%d.%d. expected %d output, got %d", i, j, len(atTime), len(actual)) } for k, value := range atTime { if value.Value != actual[k].Value { t.Errorf("%d.%d.%d expected %v value, got %v", i, j, k, value.Value, actual[k].Value) } if !value.Timestamp.Equal(actual[k].Timestamp) { t.Errorf("%d.%d.%d expected %s (offset %ss) timestamp, got %s (offset %ss)", i, j, k, value.Timestamp, value.Timestamp.Sub(instant), actual[k].Timestamp, actual[k].Timestamp.Sub(instant)) } } } for j, atInterval := range scenario.out.atInterval { actual := v.GetRangeValues(fingerprint, interval) if len(actual) != len(atInterval) { t.Fatalf("%d.%d. expected %d output, got %d", i, j, len(atInterval), len(actual)) } for k, value := range atInterval { if value.Value != actual[k].Value { t.Errorf("%d.%d.%d expected %v value, got %v", i, j, k, value.Value, actual[k].Value) } if !value.Timestamp.Equal(actual[k].Timestamp) { t.Errorf("%d.%d.%d expected %s (offset %ds) timestamp, got %s (offset %ds, value %s)", i, j, k, value.Timestamp, int(value.Timestamp.Sub(instant)/time.Second), actual[k].Timestamp, int(actual[k].Timestamp.Sub(instant)/time.Second), actual[k].Value) } } } for j, alongRange := range scenario.out.alongRange { actual := v.GetRangeValues(fingerprint, interval) if len(actual) != len(alongRange) { t.Fatalf("%d.%d. expected %d output, got %d", i, j, len(alongRange), len(actual)) } for k, value := range alongRange { if value.Value != actual[k].Value { t.Fatalf("%d.%d.%d expected %v value, got %v", i, j, k, value.Value, actual[k].Value) } if !value.Timestamp.Equal(actual[k].Timestamp) { t.Fatalf("%d.%d.%d expected %s (offset %ss) timestamp, got %s (offset %ss)", i, j, k, value.Timestamp, value.Timestamp.Sub(instant), actual[k].Timestamp, actual[k].Timestamp.Sub(instant)) } } } closer.Close() } }
func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) { testAppendSample(p, model.Sample{ Value: 0, Timestamp: time.Time{}, Metric: model.Metric{ model.MetricNameLabel: "my_metric", "request_type": "your_mom", }, }, t) testAppendSample(p, model.Sample{ Value: 0, Timestamp: time.Time{}, Metric: model.Metric{ model.MetricNameLabel: "my_metric", "request_type": "your_dad", }, }, t) result, err := p.GetFingerprintsForLabelSet(model.LabelSet{ model.MetricNameLabel: model.LabelValue("my_metric"), }) if err != nil { t.Error(err) } if len(result) != 2 { t.Errorf("Expected two elements.") } result, err = p.GetFingerprintsForLabelSet(model.LabelSet{ model.LabelName("request_type"): model.LabelValue("your_mom"), }) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } result, err = p.GetFingerprintsForLabelSet(model.LabelSet{ model.LabelName("request_type"): model.LabelValue("your_dad"), }) if err != nil { t.Error(err) } if len(result) != 1 { t.Errorf("Expected one element.") } }