From 98195031c78c3a75288271a4878eeec3be177c5e Mon Sep 17 00:00:00 2001 From: Thomas Jackson Date: Tue, 3 Sep 2019 11:41:57 -0700 Subject: [PATCH] Truncate times to milliseconds to avoid rounding issues on prometheus Fixes #212 --- pkg/promclient/time_truncate.go | 49 +++++++++++++++++++++++++++++++++ pkg/proxystorage/proxy.go | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 pkg/promclient/time_truncate.go diff --git a/pkg/promclient/time_truncate.go b/pkg/promclient/time_truncate.go new file mode 100644 index 000000000..8611c8735 --- /dev/null +++ b/pkg/promclient/time_truncate.go @@ -0,0 +1,49 @@ +package promclient + +import ( + "context" + "time" + + "github.com/prometheus/client_golang/api" + v1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/pkg/labels" +) + +func NewTimeTruncate(a API) *TimeTruncate { + return &TimeTruncate{a} +} + +const truncateDuration = time.Millisecond + +// TimeTruncate is a workaround to https://github.com/jacksontj/promxy/issues/212 +// context: https://github.com/prometheus/prometheus/issues/5972 +// For now we need to truncate the time so that prometheus doesn't round up and return no data <= the timestamp +// we requested +type TimeTruncate struct { + API +} + +// Query performs a query for the given time. +func (t *TimeTruncate) Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Warnings, error) { + return t.API.Query(ctx, query, ts.Truncate(truncateDuration)) +} + +// QueryRange performs a query for the given range. +func (t *TimeTruncate) QueryRange(ctx context.Context, query string, r v1.Range) (model.Value, api.Warnings, error) { + return t.API.QueryRange(ctx, query, v1.Range{ + Start: r.Start.Truncate(truncateDuration), + End: r.End.Truncate(truncateDuration), + Step: r.Step, + }) +} + +// Series finds series by label matchers. +func (t *TimeTruncate) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Warnings, error) { + return t.API.Series(ctx, matches, startTime.Truncate(truncateDuration), endTime.Truncate(truncateDuration)) +} + +// GetValue loads the raw data for a given set of matchers in the time range +func (t *TimeTruncate) GetValue(ctx context.Context, start, end time.Time, matchers []*labels.Matcher) (model.Value, api.Warnings, error) { + return t.API.GetValue(ctx, start.Truncate(truncateDuration), end.Truncate(truncateDuration), matchers) +} diff --git a/pkg/proxystorage/proxy.go b/pkg/proxystorage/proxy.go index 761ade305..45051055a 100644 --- a/pkg/proxystorage/proxy.go +++ b/pkg/proxystorage/proxy.go @@ -95,7 +95,7 @@ func (p *ProxyStorage) ApplyConfig(c *proxyconfig.Config) error { newState.sgs[i] = tmp apis[i] = tmp } - newState.client = promclient.NewMultiAPI(apis, model.TimeFromUnix(0), nil, len(apis)) + newState.client = promclient.NewTimeTruncate(promclient.NewMultiAPI(apis, model.TimeFromUnix(0), nil, len(apis))) if failed { newState.Cancel(nil)